From 65b1a1ddfe95b9e273d4cdaf23067a0aaff9b1d1 Mon Sep 17 00:00:00 2001 From: Robert Godfrey Date: Fri, 7 Feb 2014 16:57:49 +0000 Subject: QPID-5504 : Refactoring to allow for nodes other than queues to be subscribed from, and nodes other than exchanges to be sent to (merged from separate branch) git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1565726 13f79535-47bb-0310-9956-ffa450edef68 --- .../berkeleydb/tuple/MessageMetaDataBinding.java | 2 +- .../tuple/PreparedTransactionBinding.java | 16 +- .../store/berkeleydb/upgrade/UpgradeFrom4To5.java | 2 +- .../store/berkeleydb/BDBMessageStoreTest.java | 36 + .../server/consumer/AbstractConsumerTarget.java | 73 ++ .../org/apache/qpid/server/consumer/Consumer.java | 81 ++ .../qpid/server/consumer/ConsumerTarget.java | 67 ++ .../qpid/server/exchange/AbstractExchange.java | 39 +- .../qpid/server/exchange/DefaultExchange.java | 6 +- .../org/apache/qpid/server/exchange/Exchange.java | 21 +- .../qpid/server/logging/actors/CurrentActor.java | 1 - .../qpid/server/logging/actors/GenericActor.java | 23 + .../server/logging/actors/SubscriptionActor.java | 46 - .../logging/subjects/SubscriptionLogSubject.java | 55 -- .../qpid/server/message/MessageDestination.java | 44 + .../qpid/server/message/MessageInstance.java | 175 +++- .../apache/qpid/server/message/MessageNode.java | 26 + .../apache/qpid/server/message/MessageSource.java | 105 +++ .../java/org/apache/qpid/server/model/Session.java | 2 +- .../org/apache/qpid/server/model/VirtualHost.java | 7 +- .../qpid/server/model/adapter/ConsumerAdapter.java | 31 +- .../qpid/server/model/adapter/QueueAdapter.java | 54 +- .../qpid/server/model/adapter/SessionAdapter.java | 29 +- .../server/model/adapter/VirtualHostAdapter.java | 15 +- .../qpid/server/plugin/SystemNodeCreator.java | 37 + .../qpid/server/protocol/CapacityChecker.java | 26 + .../org/apache/qpid/server/queue/AMQQueue.java | 104 +-- .../queue/AssignedConsumerMessageGroupManager.java | 168 ++++ .../org/apache/qpid/server/queue/BaseQueue.java | 12 +- .../qpid/server/queue/ConflationQueueList.java | 2 +- .../queue/DefinedGroupMessageGroupManager.java | 281 ++++++ .../qpid/server/queue/MessageGroupManager.java | 39 + .../apache/qpid/server/queue/OutOfOrderQueue.java | 14 +- .../qpid/server/queue/PriorityQueueList.java | 6 +- .../apache/qpid/server/queue/QueueConsumer.java | 480 +++++++++++ .../qpid/server/queue/QueueConsumerList.java | 280 ++++++ .../org/apache/qpid/server/queue/QueueContext.java | 2 +- .../org/apache/qpid/server/queue/QueueEntry.java | 191 +---- .../apache/qpid/server/queue/QueueEntryImpl.java | 99 ++- .../apache/qpid/server/queue/QueueEntryList.java | 2 +- .../org/apache/qpid/server/queue/QueueRunner.java | 4 +- .../apache/qpid/server/queue/SimpleAMQQueue.java | 510 ++++++----- .../qpid/server/queue/SimpleQueueEntryList.java | 6 +- .../org/apache/qpid/server/queue/SortedQueue.java | 7 +- .../qpid/server/queue/SortedQueueEntryList.java | 8 +- .../apache/qpid/server/queue/SubFlushRunner.java | 7 +- .../server/store/AbstractJDBCMessageStore.java | 20 +- .../store/DurableConfigurationStoreHelper.java | 5 +- .../qpid/server/store/StorableMessageMetaData.java | 2 +- .../org/apache/qpid/server/store/Transaction.java | 2 +- .../qpid/server/store/TransactionLogResource.java | 2 + .../AssignedSubscriptionMessageGroupManager.java | 170 ---- .../server/subscription/ClientDeliveryMethod.java | 31 - .../DefinedGroupMessageGroupManager.java | 280 ------ .../server/subscription/MessageGroupManager.java | 41 - .../server/subscription/RecordDeliveryMethod.java | 28 - .../qpid/server/subscription/Subscription.java | 123 --- .../qpid/server/subscription/SubscriptionList.java | 280 ------ .../server/txn/AsyncAutoCommitTransaction.java | 12 +- .../qpid/server/txn/AutoCommitTransaction.java | 12 +- .../qpid/server/txn/DistributedTransaction.java | 12 +- .../java/org/apache/qpid/server/txn/DtxBranch.java | 23 +- .../apache/qpid/server/txn/LocalTransaction.java | 12 +- .../apache/qpid/server/txn/ServerTransaction.java | 9 +- .../java/org/apache/qpid/server/util/Action.java | 26 + .../qpid/server/util/StateChangeListener.java | 26 + .../server/virtualhost/AbstractVirtualHost.java | 75 ++ .../qpid/server/virtualhost/VirtualHost.java | 6 + .../VirtualHostConfigRecoveryHandler.java | 24 +- .../apache/qpid/server/consumer/MockConsumer.java | 518 +++++++++++ .../qpid/server/exchange/TopicExchangeTest.java | 17 +- .../logging/actors/SubscriptionActorTest.java | 86 -- .../logging/messages/ConsumerMessagesTest.java | 86 ++ .../logging/messages/SubscriptionMessagesTest.java | 86 -- .../logging/messages/VirtualHostMessagesTest.java | 2 +- .../subjects/SubscriptionLogSubjectTest.java | 105 --- .../qpid/server/queue/AMQPriorityQueueTest.java | 28 +- .../apache/qpid/server/queue/ConsumerListTest.java | 445 ++++++++++ .../org/apache/qpid/server/queue/MockAMQQueue.java | 69 +- .../apache/qpid/server/queue/MockQueueEntry.java | 42 +- .../qpid/server/queue/QueueEntryImplTestBase.java | 44 +- .../qpid/server/queue/SimpleAMQQueueTest.java | 506 ++++++----- .../AbstractDurableConfigurationStoreTestCase.java | 6 +- .../store/MessageStoreQuotaEventsTestBase.java | 6 + .../qpid/server/store/TestMessageMetaData.java | 3 +- .../qpid/server/subscription/MockSubscription.java | 583 ------------- .../server/subscription/SubscriptionListTest.java | 429 ---------- .../qpid/server/txn/AutoCommitTransactionTest.java | 7 +- .../qpid/server/txn/LocalTransactionTest.java | 7 +- .../qpid/server/virtualhost/MockVirtualHost.java | 14 + .../server/protocol/v0_10/ConsumerTarget_0_10.java | 580 +++++++++++++ .../ExplicitAcceptDispositionChangeListener.java | 31 +- .../ImplicitAcceptDispositionChangeListener.java | 26 +- .../v0_10/MessageAcceptCompletionListener.java | 12 +- .../protocol/v0_10/MessageMetaData_0_10.java | 4 +- .../protocol/v0_10/ServerConnectionDelegate.java | 4 +- .../qpid/server/protocol/v0_10/ServerSession.java | 95 +-- .../protocol/v0_10/ServerSessionDelegate.java | 104 ++- .../server/protocol/v0_10/Subscription_0_10.java | 944 --------------------- .../qpid/server/protocol/v0_8/AMQChannel.java | 397 ++++----- .../server/protocol/v0_8/AMQProtocolEngine.java | 7 +- .../server/protocol/v0_8/AMQProtocolSession.java | 1 - .../server/protocol/v0_8/ClientDeliveryMethod.java | 32 + .../server/protocol/v0_8/ConsumerTarget_0_8.java | 552 ++++++++++++ .../protocol/v0_8/ExtractResendAndRequeue.java | 76 +- .../qpid/server/protocol/v0_8/IncomingMessage.java | 15 +- .../qpid/server/protocol/v0_8/MessageMetaData.java | 2 +- .../server/protocol/v0_8/RecordDeliveryMethod.java | 29 + .../server/protocol/v0_8/SubscriptionFactory.java | 68 -- .../protocol/v0_8/SubscriptionFactoryImpl.java | 109 --- .../server/protocol/v0_8/SubscriptionImpl.java | 858 ------------------- .../protocol/v0_8/UnacknowledgedMessageMap.java | 13 +- .../v0_8/UnacknowledgedMessageMapImpl.java | 37 +- .../v0_8/handler/BasicConsumeMethodHandler.java | 14 +- .../v0_8/handler/BasicGetMethodHandler.java | 37 +- .../v0_8/handler/BasicPublishMethodHandler.java | 3 +- .../v0_8/handler/BasicRecoverMethodHandler.java | 2 +- .../handler/BasicRecoverSyncMethodHandler.java | 2 +- .../v0_8/handler/BasicRejectMethodHandler.java | 69 +- .../protocol/v0_8/handler/QueueDeclareHandler.java | 9 +- .../protocol/v0_8/handler/TxRollbackHandler.java | 2 +- .../apache/qpid/server/protocol/v0_8/AckTest.java | 94 +- .../qpid/server/protocol/v0_8/AcknowledgeTest.java | 2 +- .../protocol/v0_8/ExtractResendAndRequeueTest.java | 171 ++-- .../protocol/v0_8/InternalTestProtocolSession.java | 30 +- .../protocol/v0_8/QueueBrowserUsesNoAckTest.java | 7 +- .../protocol/v0_8/SubscriptionFactoryImplTest.java | 96 --- .../qpid/server/protocol/v1_0/Connection_1_0.java | 25 +- .../server/protocol/v1_0/ConsumerTarget_1_0.java | 526 ++++++++++++ .../protocol/v1_0/MessageConverter_to_1_0.java | 3 +- .../server/protocol/v1_0/MessageMetaData_1_0.java | 4 +- .../protocol/v1_0/MessageSourceDestination.java | 59 ++ .../protocol/v1_0/NodeReceivingDestination.java | 106 +++ .../server/protocol/v1_0/QueueDestination.java | 14 +- .../qpid/server/protocol/v1_0/SendingLink_1_0.java | 114 ++- .../qpid/server/protocol/v1_0/Session_1_0.java | 25 +- .../server/protocol/v1_0/Subscription_1_0.java | 694 --------------- .../plugin/servlet/rest/MessageServlet.java | 7 +- .../qpid/server/logging/ConsumerLoggingTest.java | 455 ++++++++++ .../server/logging/SubscriptionLoggingTest.java | 458 ---------- .../qpid/systest/rest/ConnectionRestTest.java | 7 +- .../apache/qpid/test/client/RollbackOrderTest.java | 4 +- .../test/unit/client/MaxDeliveryCountTest.java | 3 +- qpid/java/test-profiles/Java010Excludes | 2 +- 144 files changed, 7159 insertions(+), 7432 deletions(-) create mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/consumer/AbstractConsumerTarget.java create mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/consumer/Consumer.java create mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/consumer/ConsumerTarget.java delete mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/logging/actors/SubscriptionActor.java delete mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/logging/subjects/SubscriptionLogSubject.java create mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/MessageDestination.java create mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/MessageNode.java create mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/MessageSource.java create mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/SystemNodeCreator.java create mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/protocol/CapacityChecker.java create mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/AssignedConsumerMessageGroupManager.java create mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/DefinedGroupMessageGroupManager.java create mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/MessageGroupManager.java create mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueConsumer.java create mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueConsumerList.java delete mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/AssignedSubscriptionMessageGroupManager.java delete mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/ClientDeliveryMethod.java delete mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/DefinedGroupMessageGroupManager.java delete mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/MessageGroupManager.java delete mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/RecordDeliveryMethod.java delete mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/Subscription.java delete mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/SubscriptionList.java create mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/Action.java create mode 100644 qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/StateChangeListener.java create mode 100644 qpid/java/broker-core/src/test/java/org/apache/qpid/server/consumer/MockConsumer.java delete mode 100644 qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/actors/SubscriptionActorTest.java create mode 100644 qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/messages/ConsumerMessagesTest.java delete mode 100644 qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/messages/SubscriptionMessagesTest.java delete mode 100644 qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/subjects/SubscriptionLogSubjectTest.java create mode 100644 qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/ConsumerListTest.java delete mode 100644 qpid/java/broker-core/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java delete mode 100644 qpid/java/broker-core/src/test/java/org/apache/qpid/server/subscription/SubscriptionListTest.java create mode 100644 qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ConsumerTarget_0_10.java delete mode 100644 qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/Subscription_0_10.java create mode 100644 qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/ClientDeliveryMethod.java create mode 100644 qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/ConsumerTarget_0_8.java create mode 100644 qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/RecordDeliveryMethod.java delete mode 100644 qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/SubscriptionFactory.java delete mode 100644 qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/SubscriptionFactoryImpl.java delete mode 100644 qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/SubscriptionImpl.java delete mode 100644 qpid/java/broker-plugins/amqp-0-8-protocol/src/test/java/org/apache/qpid/server/protocol/v0_8/SubscriptionFactoryImplTest.java create mode 100644 qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/ConsumerTarget_1_0.java create mode 100644 qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/MessageSourceDestination.java create mode 100644 qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/NodeReceivingDestination.java delete mode 100644 qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java create mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ConsumerLoggingTest.java delete mode 100644 qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java diff --git a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/MessageMetaDataBinding.java b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/MessageMetaDataBinding.java index 6925c9ee2b..e0a79fa1c1 100644 --- a/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/MessageMetaDataBinding.java +++ b/qpid/java/bdbstore/src/main/java/org/apache/qpid/server/store/berkeleydb/tuple/MessageMetaDataBinding.java @@ -69,7 +69,7 @@ public class MessageMetaDataBinding extends TupleBinding _state; + private final AtomicReference> _stateListener = + new AtomicReference>(); + + protected AbstractConsumerTarget(final State initialState) + { + _state = new AtomicReference(initialState); + } + + + public final State getState() + { + return _state.get(); + } + + protected final boolean updateState(State from, State to) + { + if(_state.compareAndSet(from, to)) + { + StateChangeListener listener = _stateListener.get(); + if(listener != null) + { + listener.stateChanged(this, from, to); + } + return true; + } + else + { + return false; + } + } + + + public final void setStateListener(StateChangeListener listener) + { + _stateListener.set(listener); + } + + public final StateChangeListener getStateListener() + { + return _stateListener.get(); + } + +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/consumer/Consumer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/consumer/Consumer.java new file mode 100644 index 0000000000..e082d6eee4 --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/consumer/Consumer.java @@ -0,0 +1,81 @@ +/* + * + * 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.consumer; + +import java.util.concurrent.atomic.AtomicLong; +import org.apache.qpid.AMQException; +import org.apache.qpid.server.logging.LogActor; +import org.apache.qpid.server.message.MessageInstance; +import org.apache.qpid.server.protocol.AMQSessionModel; +import org.apache.qpid.server.util.StateChangeListener; + +public interface Consumer +{ + AtomicLong SUB_ID_GENERATOR = new AtomicLong(0); + + void externalStateChange(); + + enum Option + { + ACQUIRES, + SEES_REQUEUES, + TRANSIENT, + EXCLUSIVE, + NO_LOCAL + } + + long getBytesOut(); + + long getMessagesOut(); + + long getUnacknowledgedBytes(); + + long getUnacknowledgedMessages(); + + AMQSessionModel getSessionModel(); + + void setNoLocal(boolean noLocal); + + long getId(); + + boolean isSuspended(); + + boolean isClosed(); + + boolean acquires(); + + boolean seesRequeues(); + + void close() throws AMQException; + + boolean trySendLock(); + + + void getSendLock(); + + void releaseSendLock(); + + boolean isActive(); + + String getName(); + + void flush() throws AMQException; +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/consumer/ConsumerTarget.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/consumer/ConsumerTarget.java new file mode 100644 index 0000000000..92579475ed --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/consumer/ConsumerTarget.java @@ -0,0 +1,67 @@ +/* + * + * 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.consumer; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.message.MessageInstance; +import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.protocol.AMQSessionModel; +import org.apache.qpid.server.util.StateChangeListener; + +public interface ConsumerTarget +{ + + + enum State + { + ACTIVE, SUSPENDED, CLOSED + } + + State getState(); + + void consumerAdded(Consumer sub); + + void consumerRemoved(Consumer sub); + + void setStateListener(StateChangeListener listener); + + long getUnacknowledgedBytes(); + + long getUnacknowledgedMessages(); + + AMQSessionModel getSessionModel(); + + void send(MessageInstance entry, boolean batch) throws AMQException; + + void flushBatched(); + + void queueDeleted(); + + void queueEmpty() throws AMQException; + + boolean allocateCredit(ServerMessage msg); + + void restoreCredit(ServerMessage queueEntry); + + boolean isSuspended(); + + boolean close(); +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java index 6a959df440..bc670bd848 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java @@ -26,6 +26,7 @@ import org.apache.qpid.AMQException; import org.apache.qpid.AMQInternalException; import org.apache.qpid.AMQSecurityException; import org.apache.qpid.server.binding.Binding; +import org.apache.qpid.server.consumer.Consumer; import org.apache.qpid.server.logging.LogSubject; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.messages.BindingMessages; @@ -33,14 +34,17 @@ import org.apache.qpid.server.logging.messages.ExchangeMessages; import org.apache.qpid.server.logging.subjects.BindingLogSubject; import org.apache.qpid.server.logging.subjects.ExchangeLogSubject; import org.apache.qpid.server.message.InstanceProperties; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.message.MessageReference; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.plugin.ExchangeType; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.BaseQueue; +import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.store.DurableConfigurationStoreHelper; import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.util.Action; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Collection; @@ -66,7 +70,7 @@ public abstract class AbstractExchange implements Exchange private VirtualHost _virtualHost; - private final List _closeTaskList = new CopyOnWriteArrayList(); + private final List> _closeTaskList = new CopyOnWriteArrayList>(); /** * Whether the exchange is automatically deleted once all queues have detached from it @@ -138,6 +142,12 @@ public abstract class AbstractExchange implements Exchange if(_closed.compareAndSet(false,true)) { + List bindings = new ArrayList(_bindings); + for(Binding binding : bindings) + { + removeBinding(binding); + } + if(_alternateExchange != null) { _alternateExchange.removeReference(this); @@ -145,9 +155,9 @@ public abstract class AbstractExchange implements Exchange CurrentActor.get().message(_logSubject, ExchangeMessages.DELETED()); - for(Task task : _closeTaskList) + for(Action task : _closeTaskList) { - task.onClose(this); + task.performAction(this); } _closeTaskList.clear(); } @@ -300,12 +310,12 @@ public abstract class AbstractExchange implements Exchange return !_referrers.isEmpty(); } - public void addCloseTask(final Task task) + public void addCloseTask(final Action task) { _closeTaskList.add(task); } - public void removeCloseTask(final Task task) + public void removeCloseTask(final Action task) { _closeTaskList.remove(task); } @@ -421,7 +431,7 @@ public abstract class AbstractExchange implements Exchange public final int send(final ServerMessage message, final InstanceProperties instanceProperties, final ServerTransaction txn, - final BaseQueue.PostEnqueueAction postEnqueueAction) + final Action> postEnqueueAction) { List queues = route(message, instanceProperties); @@ -579,8 +589,6 @@ public abstract class AbstractExchange implements Exchange { doRemoveBinding(b); queue.removeBinding(b); - removeCloseTask(b); - queue.removeQueueDeleteTask(b); if (b.isDurable()) { @@ -659,8 +667,6 @@ public abstract class AbstractExchange implements Exchange DurableConfigurationStoreHelper.createBinding(_virtualHost.getDurableConfigurationStore(), b); } - queue.addQueueDeleteTask(b); - addCloseTask(b); queue.addBinding(b); doAddBinding(b); b.logCreation(); @@ -673,7 +679,7 @@ public abstract class AbstractExchange implements Exchange } } - private final class BindingImpl extends Binding implements AMQQueue.Task, Task + private final class BindingImpl extends Binding { private final BindingLogSubject _logSubject; //TODO : persist creation time @@ -689,12 +695,6 @@ public abstract class AbstractExchange implements Exchange } - - public void doTask(final AMQQueue queue) throws AMQException - { - removeBinding(this); - } - public void onClose(final Exchange exchange) throws AMQSecurityException, AMQInternalException { removeBinding(this); @@ -729,11 +729,6 @@ public abstract class AbstractExchange implements Exchange } - public static interface Task - { - public void onClose(Exchange exchange) throws AMQSecurityException, AMQInternalException; - } - } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/DefaultExchange.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/DefaultExchange.java index 71d0f8b4dd..33c5218b4c 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/DefaultExchange.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/DefaultExchange.java @@ -32,18 +32,22 @@ import org.apache.qpid.AMQInternalException; import org.apache.qpid.AMQSecurityException; import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.server.binding.Binding; +import org.apache.qpid.server.consumer.Consumer; import org.apache.qpid.server.logging.LogSubject; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.messages.ExchangeMessages; import org.apache.qpid.server.message.InstanceProperties; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.message.MessageReference; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.plugin.ExchangeType; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.BaseQueue; +import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.queue.QueueRegistry; import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.util.Action; import org.apache.qpid.server.virtualhost.VirtualHost; public class DefaultExchange implements Exchange @@ -334,7 +338,7 @@ public class DefaultExchange implements Exchange public final int send(final ServerMessage message, final InstanceProperties instanceProperties, final ServerTransaction txn, - final BaseQueue.PostEnqueueAction postEnqueueAction) + final Action> postEnqueueAction) { final AMQQueue q = _virtualHost.getQueue(message.getRoutingKey()); if(q == null) diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/Exchange.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/Exchange.java index 18e912e972..6d83fdb2a1 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/Exchange.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/exchange/Exchange.java @@ -24,20 +24,16 @@ import org.apache.qpid.AMQException; import org.apache.qpid.AMQInternalException; import org.apache.qpid.AMQSecurityException; import org.apache.qpid.server.binding.Binding; -import org.apache.qpid.server.message.InstanceProperties; -import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.message.MessageDestination; import org.apache.qpid.server.plugin.ExchangeType; import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.BaseQueue; -import org.apache.qpid.server.txn.ServerTransaction; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Collection; -import java.util.List; import java.util.Map; import java.util.UUID; -public interface Exchange extends ExchangeReferrer +public interface Exchange extends ExchangeReferrer, MessageDestination { void initialise(UUID id, VirtualHost host, String name, boolean durable, boolean autoDelete) throws AMQException; @@ -94,19 +90,6 @@ public interface Exchange extends ExchangeReferrer void close() throws AMQException; - /** - * Routes a message - * @param message the message to be routed - * @param instanceProperties the instance properties - * @param txn the transaction to enqueue within - * @param postEnqueueAction action to perform on the result of every enqueue (may be null) - * @return the number of queues in which the message was enqueued performed - */ - int send(ServerMessage message, - InstanceProperties instanceProperties, - ServerTransaction txn, - BaseQueue.PostEnqueueAction postEnqueueAction); - /** * Determines whether a message would be isBound to a particular queue using a specific routing key and arguments * @param bindingKey diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/logging/actors/CurrentActor.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/logging/actors/CurrentActor.java index 6251471139..91d9ef7dbc 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/logging/actors/CurrentActor.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/logging/actors/CurrentActor.java @@ -35,7 +35,6 @@ import org.apache.qpid.server.logging.LogSubject; * 2) We can set new actors at the point we have enough information. i.e. * - Set a low level ConnectionActor when processing bytes from the wire. * - Set a ChannelActor when we are processing the frame - * - Set a SubscriptionActor when we are handling the subscription. *

* The code performing the logging need not worry about what type of actor is * currently set so can perform its logging. The resulting log entry though will diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/logging/actors/GenericActor.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/logging/actors/GenericActor.java index 0e418a95e2..4cf2bd4508 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/logging/actors/GenericActor.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/logging/actors/GenericActor.java @@ -42,6 +42,24 @@ public class GenericActor extends AbstractActor _defaultMessageLogger = defaultMessageLogger; } + public GenericActor(final String logSubject) + { + this(new LogSubject() + { + @Override + public String toLogString() + { + return logSubject; + } + }); + } + + + public GenericActor(LogSubject logSubject) + { + this(logSubject, CurrentActor.get().getRootMessageLogger()); + } + public GenericActor(LogSubject logSubject, RootMessageLogger rootLogger) { super(rootLogger); @@ -53,6 +71,11 @@ public class GenericActor extends AbstractActor return _logSubject.toLogString(); } + public LogSubject getLogSubject() + { + return _logSubject; + } + public static LogActor getInstance(final String logMessage, RootMessageLogger rootLogger) { return new GenericActor(new LogSubject() diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/logging/actors/SubscriptionActor.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/logging/actors/SubscriptionActor.java deleted file mode 100644 index 906cd6db3a..0000000000 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/logging/actors/SubscriptionActor.java +++ /dev/null @@ -1,46 +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.logging.actors; - -import org.apache.qpid.server.logging.RootMessageLogger; -import org.apache.qpid.server.logging.subjects.SubscriptionLogSubject; -import org.apache.qpid.server.subscription.Subscription; - -/** - * The subscription actor provides formatted logging for actions that are - * performed by the subscription. Such as STATE changes. - */ -public class SubscriptionActor extends AbstractActor -{ - private SubscriptionLogSubject _logSubject; - - public SubscriptionActor(RootMessageLogger logger, Subscription subscription) - { - super(logger); - - _logSubject = new SubscriptionLogSubject(subscription); - } - - public String getLogMessage() - { - return _logSubject.toLogString(); - } -} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/logging/subjects/SubscriptionLogSubject.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/logging/subjects/SubscriptionLogSubject.java deleted file mode 100644 index 0292fe3506..0000000000 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/logging/subjects/SubscriptionLogSubject.java +++ /dev/null @@ -1,55 +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.logging.subjects; - -import org.apache.qpid.server.subscription.Subscription; - -import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.SUBSCRIPTION_FORMAT; - -import java.text.MessageFormat; - -public class SubscriptionLogSubject extends AbstractLogSubject -{ - - /** - * Create an QueueLogSubject that Logs in the following format. - * - * @param subscription - */ - public SubscriptionLogSubject(Subscription subscription) - { - // Delegate the formatting of the Queue to the QueueLogSubject. So final - // log string format is: - // [ sub:(vh()/qu()) ] - - String queueString = new QueueLogSubject(subscription.getQueue()).toLogString(); - - setLogString("[" + MessageFormat.format(SUBSCRIPTION_FORMAT, - subscription.getSubscriptionID()) - + "(" - // queueString is [vh(/{0})/qu({1}) ] so need to trim - // ^ ^^ - + queueString.substring(1,queueString.length() - 3) - + ")" - + "] "); - - } -} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/MessageDestination.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/MessageDestination.java new file mode 100644 index 0000000000..967c629749 --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/MessageDestination.java @@ -0,0 +1,44 @@ +/* + * + * 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.message; + +import org.apache.qpid.server.consumer.Consumer; +import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.util.Action; + +public interface MessageDestination extends MessageNode +{ + + public String getName(); + + /** + * Routes a message + * @param message the message to be routed + * @param instanceProperties the instance properties + * @param txn the transaction to enqueue within + * @param postEnqueueAction action to perform on the result of every enqueue (may be null) + * @return the number of queues in which the message was enqueued performed + */ + int send(ServerMessage message, + InstanceProperties instanceProperties, + ServerTransaction txn, + Action> postEnqueueAction); +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/MessageInstance.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/MessageInstance.java index afd7ff0269..97cb66cce4 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/MessageInstance.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/MessageInstance.java @@ -21,9 +21,178 @@ package org.apache.qpid.server.message; -public interface MessageInstance +import org.apache.qpid.AMQException; +import org.apache.qpid.server.filter.Filterable; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.store.TransactionLogResource; +import org.apache.qpid.server.consumer.Consumer; +import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.util.Action; +import org.apache.qpid.server.util.StateChangeListener; + +public interface MessageInstance { + + /** + * Number of times this queue entry has been delivered. + * + * @return delivery count + */ + int getDeliveryCount(); + + void incrementDeliveryCount(); + + void decrementDeliveryCount(); + + void addStateChangeListener(StateChangeListener, State> listener); + + boolean removeStateChangeListener(StateChangeListener, State> listener); + + boolean acquiredByConsumer(); + + boolean isAcquiredBy(C consumer); + + void setRedelivered(); + + boolean isRedelivered(); + + C getDeliveredConsumer(); + + void reject(); + + boolean isRejectedBy(C consumer); + + boolean getDeliveredToConsumer(); + + boolean expired() throws AMQException; + + boolean acquire(C sub); + + int getMaximumDeliveryCount(); + + int routeToAlternate(Action> action, ServerTransaction txn); + + Filterable asFilterable(); + + public static enum State + { + AVAILABLE, + ACQUIRED, + DEQUEUED, + DELETED + } + + public abstract class EntryState + { + private EntryState() + { + } + + public abstract State getState(); + + /** + * Returns true if state is either DEQUEUED or DELETED. + * + * @return true if state is either DEQUEUED or DELETED. + */ + public boolean isDispensed() + { + State currentState = getState(); + return currentState == State.DEQUEUED || currentState == State.DELETED; + } + } + + + public final class AvailableState extends EntryState + { + + public State getState() + { + return State.AVAILABLE; + } + + public String toString() + { + return getState().name(); + } + } + + + public final class DequeuedState extends EntryState + { + + public State getState() + { + return State.DEQUEUED; + } + + public String toString() + { + return getState().name(); + } + } + + + public final class DeletedState extends EntryState + { + + public State getState() + { + return State.DELETED; + } + + public String toString() + { + return getState().name(); + } + } + + public final class NonConsumerAcquiredState extends EntryState + { + public State getState() + { + return State.ACQUIRED; + } + + public String toString() + { + return getState().name(); + } + } + + public final class ConsumerAcquiredState extends EntryState + { + private final C _consumer; + + public ConsumerAcquiredState(C consumer) + { + _consumer = consumer; + } + + + public State getState() + { + return State.ACQUIRED; + } + + public C getConsumer() + { + return _consumer; + } + + public String toString() + { + return "{" + getState().name() + " : " + _consumer +"}"; + } + } + + + final static EntryState AVAILABLE_STATE = new AvailableState(); + final static EntryState DELETED_STATE = new DeletedState(); + final static EntryState DEQUEUED_STATE = new DequeuedState(); + final static EntryState NON_CONSUMER_ACQUIRED_STATE = new NonConsumerAcquiredState(); + boolean isAvailable(); boolean acquire(); @@ -32,6 +201,8 @@ public interface MessageInstance void release(); + boolean resend() throws AMQException; + void delete(); boolean isDeleted(); @@ -39,4 +210,6 @@ public interface MessageInstance ServerMessage getMessage(); InstanceProperties getInstanceProperties(); + + TransactionLogResource getOwningResource(); } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/MessageNode.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/MessageNode.java new file mode 100644 index 0000000000..f4b751d2fd --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/MessageNode.java @@ -0,0 +1,26 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.message; + +public interface MessageNode +{ + String getName(); +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/MessageSource.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/MessageSource.java new file mode 100644 index 0000000000..06ff76f103 --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/message/MessageSource.java @@ -0,0 +1,105 @@ +/* + * + * 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.message; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.consumer.Consumer; +import org.apache.qpid.server.consumer.ConsumerTarget; +import org.apache.qpid.server.filter.FilterManager; +import org.apache.qpid.server.protocol.AMQSessionModel; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.security.AuthorizationHolder; +import org.apache.qpid.server.store.TransactionLogResource; + +import java.util.Collection; +import java.util.EnumSet; + +public interface MessageSource extends TransactionLogResource, MessageNode +{ + C addConsumer(ConsumerTarget target, FilterManager filters, + Class messageClass, + String consumerName, EnumSet options) throws AMQException; + + Collection getConsumers(); + + void addConsumerRegistrationListener(ConsumerRegistrationListener listener); + + void removeConsumerRegistrationListener(ConsumerRegistrationListener listener); + + AuthorizationHolder getAuthorizationHolder(); + + void setAuthorizationHolder(AuthorizationHolder principalHolder); + + void setExclusiveOwningSession(AMQSessionModel owner); + + AMQSessionModel getExclusiveOwningSession(); + + boolean isExclusive(); + + interface ConsumerRegistrationListener + { + void consumerAdded(AMQQueue queue, Consumer consumer); + void consumerRemoved(AMQQueue queue, Consumer consumer); + } + + /** + * ExistingExclusiveConsumer signals a failure to create a consumer, because an exclusive consumer + * already exists. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represent failure to create a consumer, because an exclusive consumer already exists. + *
+ * + * @todo Not an AMQP exception as no status code. + * + * @todo Move to top level, used outside this class. + */ + static final class ExistingExclusiveConsumer extends AMQException + { + + public ExistingExclusiveConsumer() + { + super(""); + } + } + + /** + * ExistingConsumerPreventsExclusive signals a failure to create an exclusive consumer, as a consumer + * already exists. + * + *

+ *
CRC Card
Responsibilities Collaborations + *
Represent failure to create an exclusive consumer, as a consumer already exists. + *
+ * + * @todo Not an AMQP exception as no status code. + * + * @todo Move to top level, used outside this class. + */ + static final class ExistingConsumerPreventsExclusive extends AMQException + { + public ExistingConsumerPreventsExclusive() + { + super(""); + } + } +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/Session.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/Session.java index e813d0c129..355a1cf3b1 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/Session.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/Session.java @@ -77,6 +77,6 @@ public interface Session extends ConfiguredObject CHANNEL_ID, PRODUCER_FLOW_BLOCKED)); - Collection getSubscriptions(); + Collection getConsumers(); Collection getPublishers(); } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java index ae07005679..2b5176aa65 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHost.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.model; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.store.MessageStore; @@ -144,11 +145,11 @@ public interface VirtualHost extends ConfiguredObject public static interface Transaction { - void dequeue(QueueEntry entry); + void dequeue(MessageInstance entry); - void copy(QueueEntry entry, Queue queue); + void copy(MessageInstance entry, Queue queue); - void move(QueueEntry entry, Queue queue); + void move(MessageInstance entry, Queue queue); } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/ConsumerAdapter.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/ConsumerAdapter.java index 696c59783e..cf6874030b 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/ConsumerAdapter.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/ConsumerAdapter.java @@ -22,33 +22,32 @@ package org.apache.qpid.server.model.adapter; import java.util.Map; import org.apache.qpid.server.model.ConfiguredObject; -import org.apache.qpid.server.model.Consumer; import org.apache.qpid.server.model.LifetimePolicy; import org.apache.qpid.server.model.State; import org.apache.qpid.server.model.Statistics; import org.apache.qpid.server.model.UUIDGenerator; -import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.consumer.Consumer; import java.security.AccessControlException; import java.util.Collection; import java.util.Collections; -public class ConsumerAdapter extends AbstractAdapter implements Consumer +public class ConsumerAdapter extends AbstractAdapter implements org.apache.qpid.server.model.Consumer { - private final Subscription _subscription; + private final Consumer _consumer; private final QueueAdapter _queue; private final SessionAdapter _session; private final ConsumerStatistics _statistics; public ConsumerAdapter(final QueueAdapter queueAdapter, final SessionAdapter sessionAdapter, - final Subscription subscription) + final Consumer consumer) { super(UUIDGenerator.generateConsumerUUID(queueAdapter.getVirtualHost().getName(), queueAdapter.getName(), - subscription.getSessionModel().getConnectionModel().getRemoteAddressString(), - String.valueOf(subscription.getSessionModel().getChannelId()), - subscription.getConsumerName()), queueAdapter.getTaskExecutor()); - _subscription = subscription; + consumer.getSessionModel().getConnectionModel().getRemoteAddressString(), + String.valueOf(consumer.getSessionModel().getChannelId()), + consumer.getName()), queueAdapter.getTaskExecutor()); + _consumer = consumer; _queue = queueAdapter; _session = sessionAdapter; _statistics = new ConsumerStatistics(); @@ -57,7 +56,7 @@ public class ConsumerAdapter extends AbstractAdapter implements Consumer public String getName() { - return _subscription.getConsumerName(); + return _consumer.getName(); } public String setName(final String currentName, final String desiredName) @@ -107,7 +106,7 @@ public class ConsumerAdapter extends AbstractAdapter implements Consumer @Override public Collection getAttributeNames() { - return Consumer.AVAILABLE_ATTRIBUTES; + return org.apache.qpid.server.model.Consumer.AVAILABLE_ATTRIBUTES; } @Override @@ -147,7 +146,7 @@ public class ConsumerAdapter extends AbstractAdapter implements Consumer } else if(DISTRIBUTION_MODE.equals(name)) { - return _subscription.acquires() ? "MOVE" : "COPY"; + return _consumer.acquires() ? "MOVE" : "COPY"; } else if(SETTLEMENT_MODE.equals(name)) { @@ -197,11 +196,11 @@ public class ConsumerAdapter extends AbstractAdapter implements Consumer { if(name.equals(BYTES_OUT)) { - return _subscription.getBytesOut(); + return _consumer.getBytesOut(); } else if(name.equals(MESSAGES_OUT)) { - return _subscription.getMessagesOut(); + return _consumer.getMessagesOut(); } else if(name.equals(STATE_CHANGED)) { @@ -209,11 +208,11 @@ public class ConsumerAdapter extends AbstractAdapter implements Consumer } else if(name.equals(UNACKNOWLEDGED_BYTES)) { - return _subscription.getUnacknowledgedBytes(); + return _consumer.getUnacknowledgedBytes(); } else if(name.equals(UNACKNOWLEDGED_MESSAGES)) { - return _subscription.getUnacknowledgedMessages(); + return _consumer.getUnacknowledgedMessages(); } return null; // TODO - Implement } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/QueueAdapter.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/QueueAdapter.java index 074f7c243b..d59b13902b 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/QueueAdapter.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/QueueAdapter.java @@ -35,7 +35,6 @@ import org.apache.qpid.server.binding.Binding; import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.model.ConfiguredObject; import org.apache.qpid.server.model.ConfiguredObjectFinder; -import org.apache.qpid.server.model.Consumer; import org.apache.qpid.server.model.Exchange; import org.apache.qpid.server.model.IllegalStateTransitionException; import org.apache.qpid.server.model.LifetimePolicy; @@ -47,10 +46,11 @@ import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.queue.*; import org.apache.qpid.server.store.DurableConfigurationStoreHelper; -import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.consumer.Consumer; import org.apache.qpid.server.util.MapValueConverter; -final class QueueAdapter extends AbstractAdapter implements Queue, AMQQueue.SubscriptionRegistrationListener, AMQQueue.NotificationListener +final class QueueAdapter extends AbstractAdapter implements Queue, + AMQQueue.ConsumerRegistrationListener, AMQQueue.NotificationListener { @SuppressWarnings("serial") static final Map ATTRIBUTE_TYPES = Collections.unmodifiableMap(new HashMap(){{ @@ -69,8 +69,8 @@ final class QueueAdapter extends AbstractAdapter implements Queue, AMQQueue.Subs private final AMQQueue _queue; private final Map _bindingAdapters = new HashMap(); - private Map _consumerAdapters = - new HashMap(); + private Map _consumerAdapters = + new HashMap(); private final VirtualHostAdapter _vhost; @@ -84,7 +84,7 @@ final class QueueAdapter extends AbstractAdapter implements Queue, AMQQueue.Subs addParent(org.apache.qpid.server.model.VirtualHost.class, virtualHostAdapter); _queue = queue; - _queue.addSubscriptionRegistrationListener(this); + _queue.addConsumerRegistrationListener(this); populateConsumers(); _statistics = new QueueStatisticsAdapter(queue); _queue.setNotificationListener(this); @@ -124,21 +124,21 @@ final class QueueAdapter extends AbstractAdapter implements Queue, AMQQueue.Subs private void populateConsumers() { - Collection actualSubscriptions = _queue.getConsumers(); + Collection actualConsumers = _queue.getConsumers(); synchronized (_consumerAdapters) { - Iterator iter = _consumerAdapters.keySet().iterator(); - for(org.apache.qpid.server.subscription.Subscription subscription : actualSubscriptions) + Iterator iter = _consumerAdapters.keySet().iterator(); + for(Consumer consumer : actualConsumers) { - if(!_consumerAdapters.containsKey(subscription)) + if(!_consumerAdapters.containsKey(consumer)) { - SessionAdapter sessionAdapter = getSessionAdapter(subscription.getSessionModel()); - ConsumerAdapter adapter = new ConsumerAdapter(this, sessionAdapter, subscription); - _consumerAdapters.put(subscription, adapter); + SessionAdapter sessionAdapter = getSessionAdapter(consumer.getSessionModel()); + ConsumerAdapter adapter = new ConsumerAdapter(this, sessionAdapter, consumer); + _consumerAdapters.put(consumer, adapter); if (sessionAdapter != null) { // Register ConsumerAdapter with the SessionAdapter. - sessionAdapter.subscriptionRegistered(subscription, adapter); + sessionAdapter.consumerRegistered(consumer, adapter); } } } @@ -153,11 +153,11 @@ final class QueueAdapter extends AbstractAdapter implements Queue, AMQQueue.Subs } } - public Collection getConsumers() + public Collection getConsumers() { synchronized (_consumerAdapters) { - return new ArrayList(_consumerAdapters.values()); + return new ArrayList(_consumerAdapters.values()); } } @@ -502,7 +502,7 @@ final class QueueAdapter extends AbstractAdapter implements Queue, AMQQueue.Subs @Override public Collection getChildren(Class clazz) { - if(clazz == Consumer.class) + if(clazz == org.apache.qpid.server.model.Consumer.class) { return (Collection) getConsumers(); } @@ -587,19 +587,19 @@ final class QueueAdapter extends AbstractAdapter implements Queue, AMQQueue.Subs return _queue; } - public void subscriptionRegistered(final AMQQueue queue, final Subscription subscription) + public void consumerAdded(final AMQQueue queue, final Consumer consumer) { ConsumerAdapter adapter = null; synchronized (_consumerAdapters) { - if(!_consumerAdapters.containsKey(subscription)) + if(!_consumerAdapters.containsKey(consumer)) { - SessionAdapter sessionAdapter = getSessionAdapter(subscription.getSessionModel()); - adapter = new ConsumerAdapter(this, sessionAdapter, subscription); - _consumerAdapters.put(subscription, adapter); + SessionAdapter sessionAdapter = getSessionAdapter(consumer.getSessionModel()); + adapter = new ConsumerAdapter(this, sessionAdapter, consumer); + _consumerAdapters.put(consumer, adapter); if (sessionAdapter != null) { // Register ConsumerAdapter with the SessionAdapter. - sessionAdapter.subscriptionRegistered(subscription, adapter); + sessionAdapter.consumerRegistered(consumer, adapter); } } } @@ -609,20 +609,20 @@ final class QueueAdapter extends AbstractAdapter implements Queue, AMQQueue.Subs } } - public void subscriptionUnregistered(final AMQQueue queue, final Subscription subscription) + public void consumerRemoved(final AMQQueue queue, final Consumer consumer) { ConsumerAdapter adapter = null; synchronized (_consumerAdapters) { - adapter = _consumerAdapters.remove(subscription); + adapter = _consumerAdapters.remove(consumer); } if(adapter != null) { - SessionAdapter sessionAdapter = getSessionAdapter(subscription.getSessionModel()); + SessionAdapter sessionAdapter = getSessionAdapter(consumer.getSessionModel()); if (sessionAdapter != null) { // Unregister ConsumerAdapter with the SessionAdapter. - sessionAdapter.subscriptionUnregistered(subscription); + sessionAdapter.consumerUnregistered(consumer); } childRemoved(adapter); } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/SessionAdapter.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/SessionAdapter.java index 31ce7e56fd..6b76eeefa0 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/SessionAdapter.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/SessionAdapter.java @@ -34,9 +34,8 @@ import org.apache.qpid.server.model.Publisher; import org.apache.qpid.server.model.Session; import org.apache.qpid.server.model.State; import org.apache.qpid.server.model.Statistics; -import org.apache.qpid.server.model.Consumer; import org.apache.qpid.server.model.UUIDGenerator; -import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.consumer.Consumer; import org.apache.qpid.server.configuration.updater.TaskExecutor; import org.apache.qpid.server.protocol.AMQSessionModel; @@ -47,7 +46,7 @@ final class SessionAdapter extends AbstractAdapter implements Session private AMQSessionModel _session; private SessionStatistics _statistics; - private Map _consumerAdapters = new HashMap(); + private Map _consumerAdapters = new HashMap(); public SessionAdapter(final AMQSessionModel session, TaskExecutor taskExecutor) { @@ -56,11 +55,11 @@ final class SessionAdapter extends AbstractAdapter implements Session _statistics = new SessionStatistics(); } - public Collection getSubscriptions() + public Collection getConsumers() { synchronized (_consumerAdapters) { - return new ArrayList(_consumerAdapters.values()); + return new ArrayList(_consumerAdapters.values()); } } @@ -119,29 +118,29 @@ final class SessionAdapter extends AbstractAdapter implements Session } /** - * Register a ConsumerAdapter (Subscription) with this Session keyed by the Subscription. - * @param subscription the org.apache.qpid.server.subscription.Subscription used to key the ConsumerAdapter. + * Register a ConsumerAdapter with this Session keyed by the Consumer. + * @param consumer the org.apache.qpid.server.consumer.Consumer used to key the ConsumerAdapter. * @param adapter the registered ConsumerAdapter. */ - void subscriptionRegistered(Subscription subscription, ConsumerAdapter adapter) + void consumerRegistered(Consumer consumer, ConsumerAdapter adapter) { synchronized (_consumerAdapters) { - _consumerAdapters.put(subscription, adapter); + _consumerAdapters.put(consumer, adapter); } childAdded(adapter); } /** - * Unregister a ConsumerAdapter (Subscription) with this Session keyed by the Subscription. - * @param subscription the org.apache.qpid.server.subscription.Subscription used to key the ConsumerAdapter. + * Unregister a ConsumerAdapter with this Session keyed by the Consumer. + * @param consumer the org.apache.qpid.server.consumer.Consumer used to key the ConsumerAdapter. */ - void subscriptionUnregistered(Subscription subscription) + void consumerUnregistered(Consumer consumer) { ConsumerAdapter adapter = null; synchronized (_consumerAdapters) { - adapter = _consumerAdapters.remove(subscription); + adapter = _consumerAdapters.remove(consumer); } if (adapter != null) { @@ -188,9 +187,9 @@ final class SessionAdapter extends AbstractAdapter implements Session @Override public Collection getChildren(Class clazz) { - if(clazz == Consumer.class) + if(clazz == org.apache.qpid.server.model.Consumer.class) { - return (Collection) getSubscriptions(); + return (Collection) getConsumers(); } else if(clazz == Publisher.class) { diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java index 300b6e6618..f2ce20c74d 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/model/adapter/VirtualHostAdapter.java @@ -44,6 +44,7 @@ import org.apache.qpid.AMQException; import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.configuration.VirtualHostConfiguration; import org.apache.qpid.server.configuration.XmlConfigurationUtilities.MyConfiguration; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.ConfiguredObject; @@ -759,11 +760,11 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual op.withinTransaction(new Transaction() { - public void dequeue(final QueueEntry entry) + public void dequeue(final MessageInstance entry) { if(entry.acquire()) { - txn.dequeue(entry.getQueue(), entry.getMessage(), new ServerTransaction.Action() + txn.dequeue(entry.getOwningResource(), entry.getMessage(), new ServerTransaction.Action() { public void postCommit() { @@ -777,7 +778,7 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual } } - public void copy(QueueEntry entry, Queue queue) + public void copy(MessageInstance entry, Queue queue) { final ServerMessage message = entry.getMessage(); final AMQQueue toQueue = ((QueueAdapter)queue).getAMQQueue(); @@ -788,7 +789,7 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual { try { - toQueue.enqueue(message); + toQueue.enqueue(message, null); } catch(AMQException e) { @@ -803,7 +804,7 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual } - public void move(final QueueEntry entry, Queue queue) + public void move(final MessageInstance entry, Queue queue) { final ServerMessage message = entry.getMessage(); final AMQQueue toQueue = ((QueueAdapter)queue).getAMQQueue(); @@ -817,7 +818,7 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual { try { - toQueue.enqueue(message); + toQueue.enqueue(message, null); } catch (AMQException e) { @@ -830,7 +831,7 @@ public final class VirtualHostAdapter extends AbstractAdapter implements Virtual entry.release(); } }); - txn.dequeue(entry.getQueue(), message, + txn.dequeue(entry.getOwningResource(), message, new ServerTransaction.Action() { diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/SystemNodeCreator.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/SystemNodeCreator.java new file mode 100644 index 0000000000..a117524254 --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/plugin/SystemNodeCreator.java @@ -0,0 +1,37 @@ +/* + * + * 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.plugin; + +import org.apache.qpid.server.message.MessageNode; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public interface SystemNodeCreator extends Pluggable +{ + interface SystemNodeRegistry + { + void registerSystemNode(MessageNode node); + void removeSystemNode(MessageNode node); + + VirtualHost getVirtualHost(); + } + + void register(SystemNodeRegistry registry); +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/protocol/CapacityChecker.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/protocol/CapacityChecker.java new file mode 100644 index 0000000000..0ba3095243 --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/protocol/CapacityChecker.java @@ -0,0 +1,26 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.protocol; + +public interface CapacityChecker +{ + void checkCapacity(AMQSessionModel channel); +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/AMQQueue.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/AMQQueue.java index 0cddd1ed3b..62927edc29 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/AMQQueue.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/AMQQueue.java @@ -26,19 +26,20 @@ import org.apache.qpid.server.configuration.QueueConfiguration; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.exchange.ExchangeReferrer; import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.server.protocol.AMQSessionModel; -import org.apache.qpid.server.security.AuthorizationHolder; -import org.apache.qpid.server.store.TransactionLogResource; -import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.message.MessageDestination; +import org.apache.qpid.server.message.MessageSource; +import org.apache.qpid.server.protocol.CapacityChecker; +import org.apache.qpid.server.consumer.Consumer; +import org.apache.qpid.server.util.Action; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Collection; import java.util.List; import java.util.Set; -public interface AMQQueue extends Comparable, ExchangeReferrer, TransactionLogResource, BaseQueue +public interface AMQQueue extends Comparable>, ExchangeReferrer, BaseQueue, + MessageSource, CapacityChecker, MessageDestination { - String getName(); public interface NotificationListener { @@ -65,45 +66,20 @@ public interface AMQQueue extends Comparable, ExchangeReferrer, Transa long getTotalEnqueueCount(); - public interface Context - { - QueueEntry getLastSeenEntry(); - } - void setNoLocal(boolean b); boolean isAutoDelete(); String getOwner(); - AuthorizationHolder getAuthorizationHolder(); - void setAuthorizationHolder(AuthorizationHolder principalHolder); - - void setExclusiveOwningSession(AMQSessionModel owner); - AMQSessionModel getExclusiveOwningSession(); VirtualHost getVirtualHost(); - void registerSubscription(final Subscription subscription, final boolean exclusive) throws AMQException; - - void unregisterSubscription(final Subscription subscription) throws AMQException; - - Collection getConsumers(); - - interface SubscriptionRegistrationListener - { - void subscriptionRegistered(AMQQueue queue, Subscription subscription); - void subscriptionUnregistered(AMQQueue queue, Subscription subscription); - } - - void addSubscriptionRegistrationListener(SubscriptionRegistrationListener listener); - void removeSubscriptionRegistrationListener(SubscriptionRegistrationListener listener); - int getConsumerCount(); int getActiveConsumerCount(); - boolean hasExclusiveSubscriber(); + boolean hasExclusiveConsumer(); boolean isUnused(); @@ -126,14 +102,14 @@ public interface AMQQueue extends Comparable, ExchangeReferrer, Transa void requeue(QueueEntry entry); - void dequeue(QueueEntry entry, Subscription sub); + void dequeue(QueueEntry entry, Consumer sub); void decrementUnackedMsgCount(QueueEntry queueEntry); - boolean resend(final QueueEntry entry, final Subscription subscription) throws AMQException; + boolean resend(final QueueEntry entry, final Consumer consumer) throws AMQException; - void addQueueDeleteTask(final Task task); - void removeQueueDeleteTask(final Task task); + void addQueueDeleteTask(Action task); + void removeQueueDeleteTask(Action task); @@ -209,16 +185,10 @@ public interface AMQQueue extends Comparable, ExchangeReferrer, Transa Set getNotificationChecks(); - void flushSubscription(final Subscription sub) throws AMQException; - - void deliverAsync(final Subscription sub); - void deliverAsync(); void stop(); - boolean isExclusive(); - Exchange getAlternateExchange(); void setAlternateExchange(Exchange exchange); @@ -226,56 +196,6 @@ public interface AMQQueue extends Comparable, ExchangeReferrer, Transa Collection getAvailableAttributes(); Object getAttribute(String attrName); - void checkCapacity(AMQSessionModel channel); - - /** - * ExistingExclusiveSubscription signals a failure to create a subscription, because an exclusive subscription - * already exists. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Represent failure to create a subscription, because an exclusive subscription already exists. - *
- * - * @todo Not an AMQP exception as no status code. - * - * @todo Move to top level, used outside this class. - */ - static final class ExistingExclusiveSubscription extends AMQException - { - - public ExistingExclusiveSubscription() - { - super(""); - } - } - - /** - * ExistingSubscriptionPreventsExclusive signals a failure to create an exclusive subscription, as a subscription - * already exists. - * - *

- *
CRC Card
Responsibilities Collaborations - *
Represent failure to create an exclusive subscription, as a subscription already exists. - *
- * - * @todo Not an AMQP exception as no status code. - * - * @todo Move to top level, used outside this class. - */ - static final class ExistingSubscriptionPreventsExclusive extends AMQException - { - public ExistingSubscriptionPreventsExclusive() - { - super(""); - } - } - - static interface Task - { - public void doTask(AMQQueue queue) throws AMQException; - } - void configure(QueueConfiguration config); void setExclusive(boolean exclusive); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/AssignedConsumerMessageGroupManager.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/AssignedConsumerMessageGroupManager.java new file mode 100644 index 0000000000..a9b36c1b24 --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/AssignedConsumerMessageGroupManager.java @@ -0,0 +1,168 @@ +/* + * + * 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.consumer.Consumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; + + +public class AssignedConsumerMessageGroupManager implements MessageGroupManager +{ + private static final Logger _logger = LoggerFactory.getLogger(AssignedConsumerMessageGroupManager.class); + + + private final String _groupId; + private final ConcurrentHashMap _groupMap = new ConcurrentHashMap(); + private final int _groupMask; + + public AssignedConsumerMessageGroupManager(final String groupId, final int maxGroups) + { + _groupId = groupId; + _groupMask = pow2(maxGroups)-1; + } + + private static int pow2(final int i) + { + int val = 1; + while(val < i) + { + val<<=1; + } + return val; + } + + public QueueConsumer getAssignedConsumer(final QueueEntry entry) + { + Object groupVal = entry.getMessage().getMessageHeader().getHeader(_groupId); + return groupVal == null ? null : _groupMap.get(groupVal.hashCode() & _groupMask); + } + + public boolean acceptMessage(QueueConsumer sub, QueueEntry entry) + { + if(assignMessage(sub, entry)) + { + return entry.acquire(sub); + } + else + { + return false; + } + } + + private boolean assignMessage(QueueConsumer sub, QueueEntry entry) + { + Object groupVal = entry.getMessage().getMessageHeader().getHeader(_groupId); + if(groupVal == null) + { + return true; + } + else + { + Integer group = groupVal.hashCode() & _groupMask; + QueueConsumer assignedSub = _groupMap.get(group); + if(assignedSub == sub) + { + return true; + } + else + { + if(assignedSub == null) + { + if(_logger.isDebugEnabled()) + { + _logger.debug("Assigning group " + groupVal + " to sub " + sub); + } + assignedSub = _groupMap.putIfAbsent(group, sub); + return assignedSub == null || assignedSub == sub; + } + else + { + return false; + } + } + } + } + + public QueueEntry findEarliestAssignedAvailableEntry(QueueConsumer sub) + { + EntryFinder visitor = new EntryFinder(sub); + sub.getQueue().visit(visitor); + return visitor.getEntry(); + } + + private class EntryFinder implements QueueEntryVisitor + { + private QueueEntry _entry; + private QueueConsumer _sub; + + public EntryFinder(final QueueConsumer sub) + { + _sub = sub; + } + + public boolean visit(final QueueEntry entry) + { + if(!entry.isAvailable()) + { + return false; + } + + Object groupId = entry.getMessage().getMessageHeader().getHeader(_groupId); + if(groupId == null) + { + return false; + } + + Integer group = groupId.hashCode() & _groupMask; + Consumer assignedSub = _groupMap.get(group); + if(assignedSub == _sub) + { + _entry = entry; + return true; + } + else + { + return false; + } + } + + public QueueEntry getEntry() + { + return _entry; + } + } + + public void clearAssignments(QueueConsumer sub) + { + Iterator subIter = _groupMap.values().iterator(); + while(subIter.hasNext()) + { + if(subIter.next() == sub) + { + subIter.remove(); + } + } + } +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/BaseQueue.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/BaseQueue.java index 7aba1a2342..c1c3bd37e6 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/BaseQueue.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/BaseQueue.java @@ -22,19 +22,15 @@ package org.apache.qpid.server.queue; import org.apache.qpid.AMQException; +import org.apache.qpid.server.consumer.Consumer; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.store.TransactionLogResource; +import org.apache.qpid.server.util.Action; public interface BaseQueue extends TransactionLogResource { - public static interface PostEnqueueAction - { - public void onEnqueue(QueueEntry entry); - } - - void enqueue(ServerMessage message) throws AMQException; - void enqueue(ServerMessage message, PostEnqueueAction action) throws AMQException; - void enqueue(ServerMessage message, boolean transactional, PostEnqueueAction action) throws AMQException; + void enqueue(ServerMessage message, Action> action) throws AMQException; boolean isDurable(); boolean isDeleted(); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/ConflationQueueList.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/ConflationQueueList.java index e4725e0e2a..7469e95394 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/ConflationQueueList.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/ConflationQueueList.java @@ -43,7 +43,7 @@ public class ConflationQueueList extends SimpleQueueEntryList private final QueueEntry _deleteInProgress = new SimpleQueueEntryImpl(this); private final QueueEntry _newerEntryAlreadyBeenAndGone = new SimpleQueueEntryImpl(this); - public ConflationQueueList(AMQQueue queue, String conflationKey) + public ConflationQueueList(AMQQueue queue, String conflationKey) { super(queue); _conflationKey = conflationKey; diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/DefinedGroupMessageGroupManager.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/DefinedGroupMessageGroupManager.java new file mode 100644 index 0000000000..4c74e5ba0b --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/DefinedGroupMessageGroupManager.java @@ -0,0 +1,281 @@ +/* + * + * 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.consumer.Consumer; +import org.apache.qpid.server.message.MessageInstance; +import org.apache.qpid.server.util.StateChangeListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.qpid.server.message.AMQMessageHeader; +import org.apache.qpid.server.message.ServerMessage; + +import java.util.HashMap; +import java.util.Map; + +public class DefinedGroupMessageGroupManager implements MessageGroupManager +{ + private static final Logger _logger = LoggerFactory.getLogger(DefinedGroupMessageGroupManager.class); + + private final String _groupId; + private final String _defaultGroup; + private final Map _groupMap = new HashMap(); + private final ConsumerResetHelper _resetHelper; + + private final class Group + { + private final Object _group; + private QueueConsumer _consumer; + private int _activeCount; + + private Group(final Object key, final QueueConsumer consumer) + { + _group = key; + _consumer = consumer; + } + + public boolean add() + { + if(_consumer != null) + { + _activeCount++; + return true; + } + else + { + return false; + } + } + + public void subtract() + { + if(--_activeCount == 0) + { + _resetHelper.resetSubPointersForGroups(_consumer, false); + _consumer = null; + _groupMap.remove(_group); + } + } + + @Override + public boolean equals(final Object o) + { + if (this == o) + { + return true; + } + if (o == null || getClass() != o.getClass()) + { + return false; + } + + Group group = (Group) o; + + return _group.equals(group._group); + } + + @Override + public int hashCode() + { + return _group.hashCode(); + } + + public boolean isValid() + { + return !(_consumer == null || (_activeCount == 0 && _consumer.isClosed())); + } + + public QueueConsumer getConsumer() + { + return _consumer; + } + + @Override + public String toString() + { + return "Group{" + + "_group=" + _group + + ", _consumer=" + _consumer + + ", _activeCount=" + _activeCount + + '}'; + } + } + + public DefinedGroupMessageGroupManager(final String groupId, String defaultGroup, ConsumerResetHelper resetHelper) + { + _groupId = groupId; + _defaultGroup = defaultGroup; + _resetHelper = resetHelper; + } + + public synchronized QueueConsumer getAssignedConsumer(final QueueEntry entry) + { + Object groupId = getKey(entry); + + Group group = _groupMap.get(groupId); + return group == null || !group.isValid() ? null : group.getConsumer(); + } + + public synchronized boolean acceptMessage(final QueueConsumer sub, final QueueEntry entry) + { + if(assignMessage(sub, entry)) + { + return entry.acquire(sub); + } + else + { + return false; + } + } + + private boolean assignMessage(final QueueConsumer sub, final QueueEntry entry) + { + Object groupId = getKey(entry); + Group group = _groupMap.get(groupId); + + if(group == null || !group.isValid()) + { + group = new Group(groupId, sub); + + _groupMap.put(groupId, group); + + // there's a small change that the group became empty between the point at which getNextAvailable() was + // called on the consumer, and when accept message is called... in that case we want to avoid delivering + // out of order + if(_resetHelper.isEntryAheadOfConsumer(entry, sub)) + { + return false; + } + } + + Consumer assignedSub = group.getConsumer(); + + if(assignedSub == sub) + { + entry.addStateChangeListener(new GroupStateChangeListener(group, entry)); + return true; + } + else + { + return false; + } + } + + public synchronized QueueEntry findEarliestAssignedAvailableEntry(final QueueConsumer sub) + { + EntryFinder visitor = new EntryFinder(sub); + sub.getQueue().visit(visitor); + return visitor.getEntry(); + } + + private class EntryFinder implements QueueEntryVisitor + { + private QueueEntry _entry; + private QueueConsumer _sub; + + public EntryFinder(final QueueConsumer sub) + { + _sub = sub; + } + + public boolean visit(final QueueEntry entry) + { + if(!entry.isAvailable()) + { + return false; + } + + Object groupId = getKey(entry); + + Group group = _groupMap.get(groupId); + if(group != null && group.getConsumer() == _sub) + { + _entry = entry; + return true; + } + else + { + return false; + } + } + + public QueueEntry getEntry() + { + return _entry; + } + } + + + public void clearAssignments(final QueueConsumer sub) + { + } + + private Object getKey(QueueEntry entry) + { + ServerMessage message = entry.getMessage(); + AMQMessageHeader messageHeader = message == null ? null : message.getMessageHeader(); + Object groupVal = messageHeader == null ? _defaultGroup : messageHeader.getHeader(_groupId); + if(groupVal == null) + { + groupVal = _defaultGroup; + } + return groupVal; + } + + private class GroupStateChangeListener implements StateChangeListener, QueueEntry.State> + { + private final Group _group; + + public GroupStateChangeListener(final Group group, + final MessageInstance entry) + { + _group = group; + } + + public void stateChanged(final MessageInstance entry, + final MessageInstance.State oldState, + final MessageInstance.State newState) + { + synchronized (DefinedGroupMessageGroupManager.this) + { + if(_group.isValid()) + { + if(oldState != newState) + { + if(newState == QueueEntry.State.ACQUIRED) + { + _group.add(); + } + else if(oldState == QueueEntry.State.ACQUIRED) + { + _group.subtract(); + } + } + } + else + { + entry.removeStateChangeListener(this); + } + } + } + } +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/MessageGroupManager.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/MessageGroupManager.java new file mode 100644 index 0000000000..740a96bf2d --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/MessageGroupManager.java @@ -0,0 +1,39 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.queue; + +public interface MessageGroupManager +{ + public interface ConsumerResetHelper + { + public void resetSubPointersForGroups(QueueConsumer consumer, boolean clearAssignments); + + boolean isEntryAheadOfConsumer(QueueEntry entry, QueueConsumer sub); + } + + QueueConsumer getAssignedConsumer(QueueEntry entry); + + boolean acceptMessage(QueueConsumer sub, QueueEntry entry); + + QueueEntry findEarliestAssignedAvailableEntry(QueueConsumer sub); + + void clearAssignments(QueueConsumer sub); +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/OutOfOrderQueue.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/OutOfOrderQueue.java index daa5db393a..6918ae683c 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/OutOfOrderQueue.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/OutOfOrderQueue.java @@ -20,8 +20,6 @@ */ package org.apache.qpid.server.queue; -import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.subscription.SubscriptionList; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Map; @@ -38,16 +36,16 @@ public abstract class OutOfOrderQueue extends SimpleAMQQueue } @Override - protected void checkSubscriptionsNotAheadOfDelivery(final QueueEntry entry) + protected void checkConsumersNotAheadOfDelivery(final QueueEntry entry) { - // check that all subscriptions are not in advance of the entry - SubscriptionList.SubscriptionNodeIterator subIter = getSubscriptionList().iterator(); + // check that all consumers are not in advance of the entry + QueueConsumerList.ConsumerNodeIterator subIter = getConsumerList().iterator(); while(subIter.advance() && !entry.isAcquired()) { - final Subscription subscription = subIter.getNode().getSubscription(); - if(!subscription.isClosed()) + final QueueConsumer consumer = subIter.getNode().getConsumer(); + if(!consumer.isClosed()) { - QueueContext context = (QueueContext) subscription.getQueueContext(); + QueueContext context = consumer.getQueueContext(); if(context != null) { QueueEntry released = context.getReleasedEntry(); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java index 66315af9fb..05d84327d4 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java @@ -24,7 +24,7 @@ import org.apache.qpid.server.message.ServerMessage; public class PriorityQueueList implements QueueEntryList { - private final AMQQueue _queue; + private final AMQQueue _queue; private final PriorityQueueEntrySubList[] _priorityLists; private final int _priorities; private final int _priorityOffset; @@ -46,7 +46,7 @@ public class PriorityQueueList implements QueueEntryList return _priorities; } - public AMQQueue getQueue() + public AMQQueue getQueue() { return _queue; } @@ -166,7 +166,7 @@ public class PriorityQueueList implements QueueEntryList { private int _listPriority; - public PriorityQueueEntrySubList(AMQQueue queue, int listPriority) + public PriorityQueueEntrySubList(AMQQueue queue, int listPriority) { super(queue); _listPriority = listPriority; diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueConsumer.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueConsumer.java new file mode 100644 index 0000000000..ff7840255a --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueConsumer.java @@ -0,0 +1,480 @@ +/* + * + * 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.server.filter.FilterManager; +import org.apache.qpid.server.logging.LogActor; +import org.apache.qpid.server.logging.LogSubject; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.actors.GenericActor; +import org.apache.qpid.server.logging.messages.SubscriptionMessages; +import org.apache.qpid.server.logging.subjects.QueueLogSubject; +import org.apache.qpid.server.message.MessageInstance; +import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.protocol.AMQSessionModel; +import org.apache.qpid.server.protocol.MessageConverterRegistry; +import org.apache.qpid.server.consumer.Consumer; +import org.apache.qpid.server.consumer.ConsumerTarget; +import org.apache.qpid.server.util.StateChangeListener; + +import java.text.MessageFormat; +import java.util.EnumMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.SUBSCRIPTION_FORMAT; + +class QueueConsumer implements Consumer +{ + + public static enum State + { + ACTIVE, + SUSPENDED, + CLOSED + } + + private static final Logger _logger = Logger.getLogger(QueueConsumer.class); + private final AtomicBoolean _targetClosed = new AtomicBoolean(false); + private final AtomicBoolean _closed = new AtomicBoolean(false); + private final long _id; + private final AtomicReference _state = new AtomicReference(State.ACTIVE); + private final Lock _stateChangeLock = new ReentrantLock(); + private final long _createTime = System.currentTimeMillis(); + private final MessageInstance.ConsumerAcquiredState _owningState = new MessageInstance.ConsumerAcquiredState(this); + private final boolean _acquires; + private final boolean _seesRequeues; + private final String _consumerName; + private final boolean _isTransient; + private final AtomicLong _deliveredCount = new AtomicLong(0); + private final AtomicLong _deliveredBytes = new AtomicLong(0); + private final FilterManager _filters; + private final Class _messageClass; + private final Object _sessionReference; + private SimpleAMQQueue _queue; + private GenericActor _logActor; + + static final EnumMap STATE_MAP = + new EnumMap(ConsumerTarget.State.class); + + static + { + STATE_MAP.put(ConsumerTarget.State.ACTIVE, State.ACTIVE); + STATE_MAP.put(ConsumerTarget.State.SUSPENDED, State.SUSPENDED); + STATE_MAP.put(ConsumerTarget.State.CLOSED, State.CLOSED); + } + + private final T _target; + private final SubFlushRunner _runner = new SubFlushRunner(this); + private volatile QueueContext _queueContext; + private StateChangeListener _stateListener = new StateChangeListener() + { + public void stateChanged(Consumer sub, State oldState, State newState) + { + CurrentActor.get().message(SubscriptionMessages.STATE(newState.toString())); + } + }; + private boolean _noLocal; + + QueueConsumer(final FilterManager filters, + final Class messageClass, + final boolean acquires, + final boolean seesRequeues, + final String consumerName, + final boolean isTransient, + T target) + { + _messageClass = messageClass; + _sessionReference = target.getSessionModel().getConnectionReference(); + _id = SUB_ID_GENERATOR.getAndIncrement(); + _filters = filters; + _acquires = acquires; + _seesRequeues = seesRequeues; + _consumerName = consumerName; + _isTransient = isTransient; + _target = target; + _target.setStateListener( + new StateChangeListener() + { + @Override + public void stateChanged(final ConsumerTarget object, + final ConsumerTarget.State oldState, + final ConsumerTarget.State newState) + { + targetStateChanged(oldState, newState); + } + }); + } + + private void targetStateChanged(final ConsumerTarget.State oldState, final ConsumerTarget.State newState) + { + if(oldState != newState) + { + if(newState == ConsumerTarget.State.CLOSED) + { + if(_targetClosed.compareAndSet(false,true)) + { + CurrentActor.get().message(getLogSubject(), SubscriptionMessages.CLOSE()); + } + } + else + { + CurrentActor.get().message(getLogSubject(),SubscriptionMessages.STATE(newState.toString())); + } + } + + if(newState == ConsumerTarget.State.CLOSED && oldState != newState && !_closed.get()) + { + try + { + close(); + } + catch (AMQException e) + { + _logger.error("Unable to remove to remove consumer", e); + throw new RuntimeException(e); + } + } + final StateChangeListener stateListener = + (StateChangeListener) getStateListener(); + if(stateListener != null) + { + stateListener.stateChanged(this, STATE_MAP.get(oldState), STATE_MAP.get(newState)); + } + } + + public T getTarget() + { + return _target; + } + + @Override + public void externalStateChange() + { + getQueue().deliverAsync(this); + } + + @Override + public long getUnacknowledgedBytes() + { + return _target.getUnacknowledgedBytes(); + } + + @Override + public long getUnacknowledgedMessages() + { + return _target.getUnacknowledgedMessages(); + } + + @Override + public AMQSessionModel getSessionModel() + { + return _target.getSessionModel(); + } + + @Override + public boolean isSuspended() + { + return _target.isSuspended(); + } + + @Override + public void close() throws AMQException + { + if(_closed.compareAndSet(false,true)) + { + getSendLock(); + try + { + _target.close(); + _target.consumerRemoved(this); + _queue.unregisterConsumer(this); + } + finally + { + releaseSendLock(); + } + + } + } + + void flushBatched() + { + _target.flushBatched(); + } + + void queueDeleted() + { + _target.queueDeleted(); + } + + boolean wouldSuspend(final MessageInstance msg) + { + return !_target.allocateCredit(msg.getMessage()); + } + + void restoreCredit(final MessageInstance queueEntry) + { + _target.restoreCredit(queueEntry.getMessage()); + } + + void queueEmpty() throws AMQException + { + _target.queueEmpty(); + } + + State getState() + { + return STATE_MAP.get(_target.getState()); + } + + public final SimpleAMQQueue getQueue() + { + return _queue; + } + + final void setQueue(SimpleAMQQueue queue, boolean exclusive) + { + if(getQueue() != null) + { + throw new IllegalStateException("Attempt to set queue for consumer " + this + " to " + queue + "when already set to " + getQueue()); + } + _queue = queue; + + String queueString = new QueueLogSubject(_queue).toLogString(); + + _logActor = new GenericActor("[" + MessageFormat.format(SUBSCRIPTION_FORMAT, getId()) + + "(" + // queueString is [vh(/{0})/qu({1}) ] so need to trim + // ^ ^^ + + queueString.substring(1,queueString.length() - 3) + + ")" + + "] "); + + + if (CurrentActor.get().getRootMessageLogger().isMessageEnabled(_logActor, _logActor.getLogSubject(), SubscriptionMessages.CREATE_LOG_HIERARCHY)) + { + final String filterLogString = getFilterLogString(); + CurrentActor.get().message(_logActor.getLogSubject(), SubscriptionMessages.CREATE(filterLogString, queue.isDurable() && exclusive, + filterLogString.length() > 0)); + } + } + + protected final LogSubject getLogSubject() + { + return _logActor.getLogSubject(); + } + + final LogActor getLogActor() + { + return _logActor; + } + + + @Override + public final void flush() throws AMQException + { + getQueue().flushConsumer(this); + } + + boolean resend(final MessageInstance entry) throws AMQException + { + return getQueue().resend((QueueEntry)entry, this); + } + + final SubFlushRunner getRunner() + { + return _runner; + } + + public final long getId() + { + return _id; + } + + public final StateChangeListener getStateListener() + { + return _stateListener; + } + + public final void setStateListener(StateChangeListener listener) + { + _stateListener = listener; + } + + final QueueContext getQueueContext() + { + return _queueContext; + } + + final void setQueueContext(QueueContext queueContext) + { + _queueContext = queueContext; + } + + protected boolean updateState(State from, State to) + { + return _state.compareAndSet(from, to); + } + + public final boolean isActive() + { + return getState() == State.ACTIVE; + } + + public final boolean isClosed() + { + return getState() == State.CLOSED; + } + + public final void setNoLocal(boolean noLocal) + { + _noLocal = noLocal; + } + + public final boolean hasInterest(MessageInstance entry) + { + //check that the message hasn't been rejected + if (entry.isRejectedBy(this)) + { + + return false; + } + + if (entry.getMessage().getClass() == _messageClass) + { + if(_noLocal) + { + Object connectionRef = entry.getMessage().getConnectionReference(); + if (connectionRef != null && connectionRef == _sessionReference) + { + return false; + } + } + } + else + { + // no interest in messages we can't convert + if(_messageClass != null && MessageConverterRegistry.getConverter(entry.getMessage().getClass(), + _messageClass)==null) + { + return false; + } + } + return (_filters == null) || _filters.allAllow(entry.asFilterable()); + } + + protected String getFilterLogString() + { + StringBuilder filterLogString = new StringBuilder(); + String delimiter = ", "; + boolean hasEntries = false; + if (_filters != null && _filters.hasFilters()) + { + filterLogString.append(_filters.toString()); + hasEntries = true; + } + + if (!acquires()) + { + if (hasEntries) + { + filterLogString.append(delimiter); + } + filterLogString.append("Browser"); + hasEntries = true; + } + + return filterLogString.toString(); + } + + public final boolean trySendLock() + { + return _stateChangeLock.tryLock(); + } + + public final void getSendLock() + { + _stateChangeLock.lock(); + } + + public final void releaseSendLock() + { + _stateChangeLock.unlock(); + } + + public final long getCreateTime() + { + return _createTime; + } + + final MessageInstance.ConsumerAcquiredState getOwningState() + { + return _owningState; + } + + public final boolean acquires() + { + return _acquires; + } + + public final boolean seesRequeues() + { + return _seesRequeues; + } + + public final String getName() + { + return _consumerName; + } + + public final boolean isTransient() + { + return _isTransient; + } + + public final long getBytesOut() + { + return _deliveredBytes.longValue(); + } + + public final long getMessagesOut() + { + return _deliveredCount.longValue(); + } + + final void send(final QueueEntry entry, final boolean batch) throws AMQException + { + _deliveredCount.incrementAndGet(); + ServerMessage message = entry.getMessage(); + if(message == null) + { + throw new AMQException("message was null!"); + } + _deliveredBytes.addAndGet(message.getSize()); + _target.send(entry, batch); + } +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueConsumerList.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueConsumerList.java new file mode 100644 index 0000000000..82e9d58cf3 --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueConsumerList.java @@ -0,0 +1,280 @@ +/* +* +* 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.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +class QueueConsumerList +{ + private final ConsumerNode _head = new ConsumerNode(); + + private final AtomicReference _tail = new AtomicReference(_head); + private final AtomicReference _subNodeMarker = new AtomicReference(_head); + private final AtomicInteger _size = new AtomicInteger(); + + public static final class ConsumerNode + { + private final AtomicBoolean _deleted = new AtomicBoolean(); + private final AtomicReference _next = new AtomicReference(); + private final QueueConsumer _sub; + + public ConsumerNode() + { + //used for sentinel head and dummy node construction + _sub = null; + _deleted.set(true); + } + + public ConsumerNode(final QueueConsumer sub) + { + //used for regular node construction + _sub = sub; + } + + /** + * Retrieves the first non-deleted node following the current node. + * Any deleted non-tail nodes encountered during the search are unlinked. + * + * @return the next non-deleted node, or null if none was found. + */ + public ConsumerNode findNext() + { + ConsumerNode next = nextNode(); + while(next != null && next.isDeleted()) + { + final ConsumerNode newNext = next.nextNode(); + if(newNext != null) + { + //try to move our _next reference forward to the 'newNext' + //node to unlink the deleted node + _next.compareAndSet(next, newNext); + next = nextNode(); + } + else + { + //'newNext' is null, meaning 'next' is the current tail. Can't unlink + //the tail node for thread safety reasons, just use the null. + next = null; + } + } + + return next; + } + + /** + * Gets the immediately next referenced node in the structure. + * + * @return the immediately next node in the structure, or null if at the tail. + */ + protected ConsumerNode nextNode() + { + return _next.get(); + } + + /** + * Used to initialise the 'next' reference. Will only succeed if the reference was not previously set. + * + * @param node the ConsumerNode to set as 'next' + * @return whether the operation succeeded + */ + private boolean setNext(final ConsumerNode node) + { + return _next.compareAndSet(null, node); + } + + public boolean isDeleted() + { + return _deleted.get(); + } + + public boolean delete() + { + return _deleted.compareAndSet(false,true); + } + + public QueueConsumer getConsumer() + { + return _sub; + } + } + + private void insert(final ConsumerNode node, final boolean count) + { + for (;;) + { + ConsumerNode tail = _tail.get(); + ConsumerNode next = tail.nextNode(); + if (tail == _tail.get()) + { + if (next == null) + { + if (tail.setNext(node)) + { + _tail.compareAndSet(tail, node); + if(count) + { + _size.incrementAndGet(); + } + return; + } + } + else + { + _tail.compareAndSet(tail, next); + } + } + } + } + + public void add(final QueueConsumer sub) + { + ConsumerNode node = new ConsumerNode(sub); + insert(node, true); + } + + public boolean remove(final QueueConsumer sub) + { + ConsumerNode prevNode = _head; + ConsumerNode node = _head.nextNode(); + + while(node != null) + { + if(sub.equals(node.getConsumer()) && node.delete()) + { + _size.decrementAndGet(); + + ConsumerNode tail = _tail.get(); + if(node == tail) + { + //we cant remove the last node from the structure for + //correctness reasons, however we have just 'deleted' + //the tail. Inserting an empty dummy node after it will + //let us scavenge the node containing the Consumer. + insert(new ConsumerNode(), false); + } + + //advance the next node reference in the 'prevNode' to scavenge + //the newly 'deleted' node for the Consumer. + prevNode.findNext(); + + nodeMarkerCleanup(node); + + return true; + } + + prevNode = node; + node = node.findNext(); + } + + return false; + } + + private void nodeMarkerCleanup(final ConsumerNode node) + { + ConsumerNode markedNode = _subNodeMarker.get(); + if(node == markedNode) + { + //if the marked node is the one we are removing, then + //replace it with a dummy pointing at the next node. + //this is OK as the marked node is only used to index + //into the list and find the next node to use. + //Because we inserted a dummy if node was the + //tail, markedNode.nextNode() can never be null. + ConsumerNode dummy = new ConsumerNode(); + dummy.setNext(markedNode.nextNode()); + + //if the CAS fails the marked node has changed, thus + //we don't care about the dummy and just forget it + _subNodeMarker.compareAndSet(markedNode, dummy); + } + else if(markedNode != null) + { + //if the marked node was already deleted then it could + //hold subsequently removed nodes after it in the list + //in memory. Scavenge it to ensure their actual removal. + if(markedNode != _head && markedNode.isDeleted()) + { + markedNode.findNext(); + } + } + } + + public boolean updateMarkedNode(final ConsumerNode expected, final ConsumerNode nextNode) + { + return _subNodeMarker.compareAndSet(expected, nextNode); + } + + /** + * Get the current marked ConsumerNode. This should only be used only to index into the list and find the next node + * after the mark, since if the previously marked node was subsequently deleted the item returned may be a dummy node + * with reference to the next node. + * + * @return the previously marked node (or a dummy if it was subsequently deleted) + */ + public ConsumerNode getMarkedNode() + { + return _subNodeMarker.get(); + } + + + public static class ConsumerNodeIterator + { + private ConsumerNode _lastNode; + + ConsumerNodeIterator(ConsumerNode startNode) + { + _lastNode = startNode; + } + + public ConsumerNode getNode() + { + return _lastNode; + } + + public boolean advance() + { + ConsumerNode nextNode = _lastNode.findNext(); + _lastNode = nextNode; + + return _lastNode != null; + } + } + + public ConsumerNodeIterator iterator() + { + return new ConsumerNodeIterator(_head); + } + + public ConsumerNode getHead() + { + return _head; + } + + public int size() + { + return _size.get(); + } +} + + + diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueContext.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueContext.java index 79279b44c7..861bd3dea1 100755 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueContext.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueContext.java @@ -23,7 +23,7 @@ package org.apache.qpid.server.queue; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; -final class QueueContext implements AMQQueue.Context +final class QueueContext { private volatile QueueEntry _lastSeenEntry; private volatile QueueEntry _releasedEntry; diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueEntry.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueEntry.java index 2aa1d1f473..6a42088c47 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueEntry.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueEntry.java @@ -20,207 +20,20 @@ */ package org.apache.qpid.server.queue; -import org.apache.qpid.AMQException; -import org.apache.qpid.server.filter.Filterable; import org.apache.qpid.server.message.MessageInstance; -import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.txn.ServerTransaction; -public interface QueueEntry extends MessageInstance, Comparable +public interface QueueEntry extends MessageInstance, Comparable { - - - public static enum State - { - AVAILABLE, - ACQUIRED, - EXPIRED, - DEQUEUED, - DELETED; - - - } - - public static interface StateChangeListener - { - public void stateChanged(QueueEntry entry, State oldSate, State newState); - } - - public abstract class EntryState - { - private EntryState() - { - } - - public abstract State getState(); - - /** - * Returns true if state is either DEQUEUED or DELETED. - * - * @return true if state is either DEQUEUED or DELETED. - */ - public boolean isDispensed() - { - State currentState = getState(); - return currentState == State.DEQUEUED || currentState == State.DELETED; - } - } - - - public final class AvailableState extends EntryState - { - - public State getState() - { - return State.AVAILABLE; - } - - public String toString() - { - return getState().name(); - } - } - - - public final class DequeuedState extends EntryState - { - - public State getState() - { - return State.DEQUEUED; - } - - public String toString() - { - return getState().name(); - } - } - - - public final class DeletedState extends EntryState - { - - public State getState() - { - return State.DELETED; - } - - public String toString() - { - return getState().name(); - } - } - - public final class ExpiredState extends EntryState - { - - public State getState() - { - return State.EXPIRED; - } - - public String toString() - { - return getState().name(); - } - } - - - public final class NonSubscriptionAcquiredState extends EntryState - { - public State getState() - { - return State.ACQUIRED; - } - - public String toString() - { - return getState().name(); - } - } - - public final class SubscriptionAcquiredState extends EntryState - { - private final Subscription _subscription; - - public SubscriptionAcquiredState(Subscription subscription) - { - _subscription = subscription; - } - - - public State getState() - { - return State.ACQUIRED; - } - - public Subscription getSubscription() - { - return _subscription; - } - - public String toString() - { - return "{" + getState().name() + " : " + _subscription +"}"; - } - } - - - final static EntryState AVAILABLE_STATE = new AvailableState(); - final static EntryState DELETED_STATE = new DeletedState(); - final static EntryState DEQUEUED_STATE = new DequeuedState(); - final static EntryState NON_SUBSCRIPTION_ACQUIRED_STATE = new NonSubscriptionAcquiredState(); - - - - - AMQQueue getQueue(); + AMQQueue getQueue(); long getSize(); - boolean getDeliveredToConsumer(); - - boolean expired() throws AMQException; - - boolean acquire(Subscription sub); - - boolean acquiredBySubscription(); - boolean isAcquiredBy(Subscription subscription); - - void setRedelivered(); - - boolean isRedelivered(); - - Subscription getDeliveredSubscription(); - - void reject(); - - boolean isRejectedBy(long subscriptionId); - - int routeToAlternate(final BaseQueue.PostEnqueueAction action, ServerTransaction txn); - boolean isQueueDeleted(); QueueEntry getNextNode(); QueueEntry getNextValidEntry(); - void addStateChangeListener(StateChangeListener listener); - boolean removeStateChangeListener(StateChangeListener listener); - - - /** - * Number of times this queue entry has been delivered. - * - * @return delivery count - */ - int getDeliveryCount(); - - void incrementDeliveryCount(); - - void decrementDeliveryCount(); - - Filterable asFilterable(); } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java index 461d493437..8b81a87903 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java @@ -26,11 +26,15 @@ import org.apache.qpid.AMQException; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.filter.Filterable; import org.apache.qpid.server.message.InstanceProperties; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.message.MessageReference; import org.apache.qpid.server.message.ServerMessage; -import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.store.TransactionLogResource; +import org.apache.qpid.server.consumer.Consumer; import org.apache.qpid.server.txn.LocalTransaction; import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.util.Action; +import org.apache.qpid.server.util.StateChangeListener; import java.util.EnumMap; import java.util.HashSet; @@ -46,7 +50,7 @@ public abstract class QueueEntryImpl implements QueueEntry private final QueueEntryList _queueEntryList; - private MessageReference _message; + private final MessageReference _message; private Set _rejectedBy = null; @@ -59,7 +63,7 @@ public abstract class QueueEntryImpl implements QueueEntry (QueueEntryImpl.class, EntryState.class, "_state"); - private volatile Set _stateChangeListeners; + private volatile Set, State>> _stateChangeListeners; private static final AtomicReferenceFieldUpdater @@ -134,7 +138,7 @@ public abstract class QueueEntryImpl implements QueueEntry return _entryId; } - public AMQQueue getQueue() + public AMQQueue getQueue() { return _queueEntryList.getQueue(); } @@ -183,7 +187,7 @@ public abstract class QueueEntryImpl implements QueueEntry public boolean acquire() { - return acquire(NON_SUBSCRIPTION_ACQUIRED_STATE); + return acquire(NON_CONSUMER_ACQUIRED_STATE); } private boolean acquire(final EntryState state) @@ -198,7 +202,7 @@ public abstract class QueueEntryImpl implements QueueEntry return acquired; } - public boolean acquire(Subscription sub) + public boolean acquire(QueueConsumer sub) { final boolean acquired = acquire(sub.getOwningState()); if(acquired) @@ -208,17 +212,17 @@ public abstract class QueueEntryImpl implements QueueEntry return acquired; } - public boolean acquiredBySubscription() + public boolean acquiredByConsumer() { - return (_state instanceof SubscriptionAcquiredState); + return (_state instanceof ConsumerAcquiredState); } - public boolean isAcquiredBy(Subscription subscription) + public boolean isAcquiredBy(QueueConsumer consumer) { EntryState state = _state; - return state instanceof SubscriptionAcquiredState - && ((SubscriptionAcquiredState)state).getSubscription() == subscription; + return state instanceof ConsumerAcquiredState + && ((ConsumerAcquiredState)state).getConsumer() == consumer; } public void release() @@ -228,14 +232,9 @@ public abstract class QueueEntryImpl implements QueueEntry if((state.getState() == State.ACQUIRED) &&_stateUpdater.compareAndSet(this, state, AVAILABLE_STATE)) { - if(state instanceof SubscriptionAcquiredState) + if(state instanceof ConsumerAcquiredState) { getQueue().decrementUnackedMsgCount(this); - Subscription subscription = ((SubscriptionAcquiredState)state).getSubscription(); - if (subscription != null) - { - subscription.releaseQueueEntry(this); - } } if(!getQueue().isDeleted()) @@ -265,12 +264,12 @@ public abstract class QueueEntryImpl implements QueueEntry return Boolean.TRUE.equals(_instanceProperties.getProperty(InstanceProperties.Property.REDELIVERED)); } - public Subscription getDeliveredSubscription() + public QueueConsumer getDeliveredConsumer() { EntryState state = _state; - if (state instanceof SubscriptionAcquiredState) + if (state instanceof ConsumerAcquiredState) { - return ((SubscriptionAcquiredState) state).getSubscription(); + return (QueueConsumer) ((ConsumerAcquiredState) state).getConsumer(); } else { @@ -280,16 +279,16 @@ public abstract class QueueEntryImpl implements QueueEntry public void reject() { - Subscription subscription = getDeliveredSubscription(); + QueueConsumer consumer = getDeliveredConsumer(); - if (subscription != null) + if (consumer != null) { if (_rejectedBy == null) { _rejectedBy = new HashSet(); } - _rejectedBy.add(subscription.getSubscriptionID()); + _rejectedBy.add(consumer.getId()); } else { @@ -297,12 +296,12 @@ public abstract class QueueEntryImpl implements QueueEntry } } - public boolean isRejectedBy(long subscriptionId) + public boolean isRejectedBy(QueueConsumer consumer) { - if (_rejectedBy != null) // We have subscriptions that rejected this message + if (_rejectedBy != null) // We have consumers that rejected this message { - return _rejectedBy.contains(subscriptionId); + return _rejectedBy.contains(consumer.getId()); } else // This message hasn't been rejected yet. { @@ -316,12 +315,10 @@ public abstract class QueueEntryImpl implements QueueEntry if((state.getState() == State.ACQUIRED) &&_stateUpdater.compareAndSet(this, state, DEQUEUED_STATE)) { - Subscription s = null; - if (state instanceof SubscriptionAcquiredState) + Consumer s = null; + if (state instanceof ConsumerAcquiredState) { getQueue().decrementUnackedMsgCount(this); - s = ((SubscriptionAcquiredState) state).getSubscription(); - s.onDequeue(this); } getQueue().dequeue(this,s); @@ -336,7 +333,7 @@ public abstract class QueueEntryImpl implements QueueEntry private void notifyStateChange(final State oldState, final State newState) { - for(StateChangeListener l : _stateChangeListeners) + for(StateChangeListener, State> l : _stateChangeListeners) { l.stateChanged(this, oldState, newState); } @@ -367,7 +364,7 @@ public abstract class QueueEntryImpl implements QueueEntry dispose(); } - public int routeToAlternate(final BaseQueue.PostEnqueueAction action, ServerTransaction txn) + public int routeToAlternate(final Action> action, ServerTransaction txn) { final AMQQueue currentQueue = getQueue(); Exchange alternateExchange = currentQueue.getAlternateExchange(); @@ -379,7 +376,10 @@ public abstract class QueueEntryImpl implements QueueEntry txn = new LocalTransaction(getQueue().getVirtualHost().getMessageStore()); } - int enqueues = alternateExchange.send(getMessage(), getInstanceProperties(), txn, action); + int enqueues = alternateExchange.send(getMessage(), + getInstanceProperties(), + txn, + action); txn.dequeue(currentQueue, getMessage(), new ServerTransaction.Action() { @@ -412,21 +412,21 @@ public abstract class QueueEntryImpl implements QueueEntry return getQueue().isDeleted(); } - public void addStateChangeListener(StateChangeListener listener) + public void addStateChangeListener(StateChangeListener, State> listener) { - Set listeners = _stateChangeListeners; + Set, State>> listeners = _stateChangeListeners; if(listeners == null) { - _listenersUpdater.compareAndSet(this, null, new CopyOnWriteArraySet()); + _listenersUpdater.compareAndSet(this, null, new CopyOnWriteArraySet, State>>()); listeners = _stateChangeListeners; } listeners.add(listener); } - public boolean removeStateChangeListener(StateChangeListener listener) + public boolean removeStateChangeListener(StateChangeListener, State> listener) { - Set listeners = _stateChangeListeners; + Set, State>> listeners = _stateChangeListeners; if(listeners != null) { return listeners.remove(listener); @@ -461,6 +461,12 @@ public abstract class QueueEntryImpl implements QueueEntry return _deliveryCount; } + @Override + public int getMaximumDeliveryCount() + { + return getQueue().getMaximumDeliveryCount(); + } + public void incrementDeliveryCount() { _deliveryCountUpdater.incrementAndGet(this); @@ -485,6 +491,23 @@ public abstract class QueueEntryImpl implements QueueEntry '}'; } + @Override + public boolean resend() throws AMQException + { + QueueConsumer sub = getDeliveredConsumer(); + if(sub != null) + { + return sub.resend(this); + } + return false; + } + + @Override + public TransactionLogResource getOwningResource() + { + return getQueue(); + } + private static class EntryInstanceProperties implements InstanceProperties { private final EnumMap _properties = new EnumMap(Property.class); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java index 641aaa0a08..ad1f703f51 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java @@ -24,7 +24,7 @@ import org.apache.qpid.server.message.ServerMessage; public interface QueueEntryList { - AMQQueue getQueue(); + AMQQueue getQueue(); Q add(ServerMessage message); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueRunner.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueRunner.java index 22a2029494..005d9b66b3 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueRunner.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/QueueRunner.java @@ -32,8 +32,8 @@ import org.apache.qpid.transport.TransportException; /** * QueueRunners are Runnables used to process a queue when requiring - * asynchronous message delivery to subscriptions, which is necessary - * when straight-through delivery of a message to a subscription isn't + * asynchronous message delivery to consumers, which is necessary + * when straight-through delivery of a message to a consumer isn't * possible during the enqueue operation. */ public class QueueRunner implements Runnable diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java index 87d11a892e..f4a9794fcd 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java @@ -18,15 +18,7 @@ */ package org.apache.qpid.server.queue; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; +import java.util.*; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; @@ -43,27 +35,33 @@ import org.apache.qpid.server.binding.Binding; import org.apache.qpid.server.configuration.BrokerProperties; import org.apache.qpid.server.configuration.QueueConfiguration; import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.filter.FilterManager; import org.apache.qpid.server.logging.LogActor; import org.apache.qpid.server.logging.LogSubject; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.actors.QueueActor; import org.apache.qpid.server.logging.messages.QueueMessages; import org.apache.qpid.server.logging.subjects.QueueLogSubject; +import org.apache.qpid.server.message.InstanceProperties; +import org.apache.qpid.server.message.MessageDestination; +import org.apache.qpid.server.message.MessageInstance; +import org.apache.qpid.server.message.MessageReference; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.model.Queue; import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.security.AuthorizationHolder; -import org.apache.qpid.server.subscription.AssignedSubscriptionMessageGroupManager; -import org.apache.qpid.server.subscription.DefinedGroupMessageGroupManager; -import org.apache.qpid.server.subscription.MessageGroupManager; -import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.subscription.SubscriptionList; +import org.apache.qpid.server.consumer.Consumer; +import org.apache.qpid.server.consumer.ConsumerTarget; import org.apache.qpid.server.txn.AutoCommitTransaction; import org.apache.qpid.server.txn.LocalTransaction; import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.util.Action; +import org.apache.qpid.server.util.StateChangeListener; import org.apache.qpid.server.virtualhost.VirtualHost; -public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, MessageGroupManager.SubscriptionResetHelper +public class SimpleAMQQueue implements AMQQueue, + StateChangeListener, + MessageGroupManager.ConsumerResetHelper { private static final Logger _logger = Logger.getLogger(SimpleAMQQueue.class); @@ -98,9 +96,9 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes private final QueueEntryList _entries; - private final SubscriptionList _subscriptionList = new SubscriptionList(); + private final QueueConsumerList _consumerList = new QueueConsumerList(); - private volatile Subscription _exclusiveSubscriber; + private volatile QueueConsumer _exclusiveSubscriber; @@ -120,13 +118,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes private final AtomicLong _persistentMessageDequeueSize = new AtomicLong(); private final AtomicLong _persistentMessageEnqueueCount = new AtomicLong(); private final AtomicLong _persistentMessageDequeueCount = new AtomicLong(); - private final AtomicInteger _consumerCountHigh = new AtomicInteger(0); - private final AtomicLong _msgTxnEnqueues = new AtomicLong(0); - private final AtomicLong _byteTxnEnqueues = new AtomicLong(0); - private final AtomicLong _msgTxnDequeues = new AtomicLong(0); - private final AtomicLong _byteTxnDequeues = new AtomicLong(0); private final AtomicLong _unackedMsgCount = new AtomicLong(0); - private final AtomicLong _unackedMsgCountHigh = new AtomicLong(0); private final AtomicLong _unackedMsgBytes = new AtomicLong(); private final AtomicInteger _bindingCountHigh = new AtomicInteger(); @@ -165,7 +157,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes private final Set _blockedChannels = new ConcurrentSkipListSet(); private final AtomicBoolean _deleted = new AtomicBoolean(false); - private final List _deleteTaskList = new CopyOnWriteArrayList(); + private final List> _deleteTaskList = new CopyOnWriteArrayList>(); private LogSubject _logSubject; @@ -187,8 +179,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes private int _maximumDeliveryCount; private final MessageGroupManager _messageGroupManager; - private final Collection _subscriptionListeners = - new ArrayList(); + private final Collection _consumerListeners = + new ArrayList(); private AMQQueue.NotificationListener _notificationListener; private final long[] _lastNotificationTimes = new long[NotificationCheck.values().length]; @@ -257,7 +249,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } else { - _messageGroupManager = new AssignedSubscriptionMessageGroupManager(String.valueOf(arguments.get( + _messageGroupManager = new AssignedConsumerMessageGroupManager(String.valueOf(arguments.get( Queue.MESSAGE_GROUP_KEY)), DEFAULT_MAX_GROUPS); } } @@ -388,11 +380,17 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes return _name; } - // ------ Manage Subscriptions + // ------ Manage Consumers - public synchronized void registerSubscription(final Subscription subscription, final boolean exclusive) - throws AMQSecurityException, ExistingExclusiveSubscription, ExistingSubscriptionPreventsExclusive + + @Override + public synchronized QueueConsumer addConsumer(final ConsumerTarget target, + final FilterManager filters, + final Class messageClass, + final String consumerName, + EnumSet optionSet) throws AMQException { + // Access control if (!getVirtualHost().getSecurityManager().authoriseConsume(this)) { @@ -400,58 +398,61 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } - if (hasExclusiveSubscriber()) + if (hasExclusiveConsumer()) { - throw new ExistingExclusiveSubscription(); + throw new ExistingExclusiveConsumer(); } - if (exclusive && !subscription.isTransient()) + + boolean exclusive = optionSet.contains(Consumer.Option.EXCLUSIVE); + boolean isTransient = optionSet.contains(Consumer.Option.TRANSIENT); + + if (exclusive && !isTransient && getConsumerCount() != 0) { - if (getConsumerCount() != 0) - { - throw new ExistingSubscriptionPreventsExclusive(); - } - else - { - _exclusiveSubscriber = subscription; - } + throw new ExistingConsumerPreventsExclusive(); + } + + QueueConsumer consumer = new QueueConsumer(filters, messageClass, + optionSet.contains(Consumer.Option.ACQUIRES), + optionSet.contains(Consumer.Option.SEES_REQUEUES), + consumerName, optionSet.contains(Consumer.Option.TRANSIENT), target); + target.consumerAdded(consumer); + + + if (exclusive && !isTransient) + { + _exclusiveSubscriber = consumer; } - if(subscription.isActive()) + if(consumer.isActive()) { _activeSubscriberCount.incrementAndGet(); } - subscription.setStateListener(this); - subscription.setQueueContext(new QueueContext(_entries.getHead())); + + consumer.setStateListener(this); + consumer.setQueueContext(new QueueContext(_entries.getHead())); if (!isDeleted()) { - subscription.setQueue(this, exclusive); + consumer.setQueue(this, exclusive); if(_nolocal) { - subscription.setNoLocal(_nolocal); + consumer.setNoLocal(_nolocal); } - synchronized (_subscriptionListeners) + synchronized (_consumerListeners) { - for(SubscriptionRegistrationListener listener : _subscriptionListeners) + for(ConsumerRegistrationListener listener : _consumerListeners) { - listener.subscriptionRegistered(this, subscription); + listener.consumerAdded(this, consumer); } } - _subscriptionList.add(subscription); - - //Increment consumerCountHigh if necessary. (un)registerSubscription are both - //synchronized methods so we don't need additional synchronization here - if(_consumerCountHigh.get() < getConsumerCount()) - { - _consumerCountHigh.incrementAndGet(); - } + _consumerList.add(consumer); if (isDeleted()) { - subscription.queueDeleted(this); + consumer.queueDeleted(); } } else @@ -459,42 +460,49 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes // TODO } - deliverAsync(subscription); + deliverAsync(consumer); + + return consumer; } - public synchronized void unregisterSubscription(final Subscription subscription) throws AMQException + synchronized void unregisterConsumer(final QueueConsumer consumer) throws AMQException { - if (subscription == null) + if (consumer == null) { - throw new NullPointerException("subscription argument is null"); + throw new NullPointerException("consumer argument is null"); } - boolean removed = _subscriptionList.remove(subscription); + boolean removed = _consumerList.remove(consumer); if (removed) { - subscription.close(); + consumer.close(); // No longer can the queue have an exclusive consumer setExclusiveSubscriber(null); - subscription.setQueueContext(null); + consumer.setQueueContext(null); + + if(!isDeleted() && isExclusive() && getConsumerCount() == 0) + { + setAuthorizationHolder(null); + } if(_messageGroupManager != null) { - resetSubPointersForGroups(subscription, true); + resetSubPointersForGroups(consumer, true); } - synchronized (_subscriptionListeners) + synchronized (_consumerListeners) { - for(SubscriptionRegistrationListener listener : _subscriptionListeners) + for(ConsumerRegistrationListener listener : _consumerListeners) { - listener.subscriptionUnregistered(this, subscription); + listener.consumerRemoved(this, consumer); } } // auto-delete queues must be deleted if there are no remaining subscribers - if (_autoDelete && getDeleteOnNoConsumers() && !subscription.isTransient() && getConsumerCount() == 0 ) + if (_autoDelete && getDeleteOnNoConsumers() && !consumer.isTransient() && getConsumerCount() == 0 ) { if (_logger.isInfoEnabled()) { @@ -503,57 +511,57 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes getVirtualHost().removeQueue(this); - // 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); + // we need to manually fire the event to the removed consumer (which was the last one left for this + // queue. This is because the delete method uses the consumer set which has just been cleared + consumer.queueDeleted(); } } } - public Collection getConsumers() + public Collection getConsumers() { - List consumers = new ArrayList(); - SubscriptionList.SubscriptionNodeIterator iter = _subscriptionList.iterator(); + List consumers = new ArrayList(); + QueueConsumerList.ConsumerNodeIterator iter = _consumerList.iterator(); while(iter.advance()) { - consumers.add(iter.getNode().getSubscription()); + consumers.add(iter.getNode().getConsumer()); } return consumers; } - public void addSubscriptionRegistrationListener(final SubscriptionRegistrationListener listener) + public void addConsumerRegistrationListener(final ConsumerRegistrationListener listener) { - synchronized (_subscriptionListeners) + synchronized (_consumerListeners) { - _subscriptionListeners.add(listener); + _consumerListeners.add(listener); } } - public void removeSubscriptionRegistrationListener(final SubscriptionRegistrationListener listener) + public void removeConsumerRegistrationListener(final ConsumerRegistrationListener listener) { - synchronized (_subscriptionListeners) + synchronized (_consumerListeners) { - _subscriptionListeners.remove(listener); + _consumerListeners.remove(listener); } } - public void resetSubPointersForGroups(Subscription subscription, boolean clearAssignments) + public void resetSubPointersForGroups(QueueConsumer consumer, boolean clearAssignments) { - QueueEntry entry = _messageGroupManager.findEarliestAssignedAvailableEntry(subscription); + QueueEntry entry = _messageGroupManager.findEarliestAssignedAvailableEntry(consumer); if(clearAssignments) { - _messageGroupManager.clearAssignments(subscription); + _messageGroupManager.clearAssignments(consumer); } if(entry != null) { - SubscriptionList.SubscriptionNodeIterator subscriberIter = _subscriptionList.iterator(); + QueueConsumerList.ConsumerNodeIterator subscriberIter = _consumerList.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(); + QueueConsumer sub = subscriberIter.getNode().getConsumer(); // we don't make browsers send the same stuff twice if (sub.seesRequeues()) @@ -617,23 +625,9 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } // ------ Enqueue / Dequeue - public void enqueue(ServerMessage message) throws AMQException - { - enqueue(message, null); - } - - public void enqueue(ServerMessage message, PostEnqueueAction action) throws AMQException - { - enqueue(message, false, action); - } - public void enqueue(ServerMessage message, boolean transactional, PostEnqueueAction action) throws AMQException + public void enqueue(ServerMessage message, Action> action) throws AMQException { - - if(transactional) - { - incrementTxnEnqueueStats(message); - } incrementQueueCount(); incrementQueueSize(message); @@ -641,35 +635,35 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes QueueEntry entry; - final Subscription exclusiveSub = _exclusiveSubscriber; + final QueueConsumer exclusiveSub = _exclusiveSubscriber; entry = _entries.add(message); if(action != null || (exclusiveSub == null && _queueRunner.isIdle())) { /* - iterate over subscriptions and if any is at the end of the queue and can deliver this message, then deliver the message + iterate over consumers and if any is at the end of the queue and can deliver this message, then deliver the message */ - SubscriptionList.SubscriptionNode node = _subscriptionList.getMarkedNode(); - SubscriptionList.SubscriptionNode nextNode = node.findNext(); + QueueConsumerList.ConsumerNode node = _consumerList.getMarkedNode(); + QueueConsumerList.ConsumerNode nextNode = node.findNext(); if (nextNode == null) { - nextNode = _subscriptionList.getHead().findNext(); + nextNode = _consumerList.getHead().findNext(); } while (nextNode != null) { - if (_subscriptionList.updateMarkedNode(node, nextNode)) + if (_consumerList.updateMarkedNode(node, nextNode)) { break; } else { - node = _subscriptionList.getMarkedNode(); + node = _consumerList.getMarkedNode(); nextNode = node.findNext(); if (nextNode == null) { - nextNode = _subscriptionList.getHead().findNext(); + nextNode = _consumerList.getHead().findNext(); } } } @@ -683,13 +677,13 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes if (nextNode == null) { loops--; - nextNode = _subscriptionList.getHead(); + nextNode = _consumerList.getHead(); } else { - // if subscription at end, and active, offer - Subscription sub = nextNode.getSubscription(); - deliverToSubscription(sub, entry); + // if consumer at end, and active, offer + QueueConsumer sub = nextNode.getConsumer(); + deliverToConsumer(sub, entry); } nextNode = nextNode.findNext(); @@ -699,7 +693,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes if (entry.isAvailable()) { - checkSubscriptionsNotAheadOfDelivery(entry); + checkConsumersNotAheadOfDelivery(entry); if (exclusiveSub != null) { @@ -715,12 +709,12 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes if(action != null) { - action.onEnqueue(entry); + action.performAction(entry); } } - private void deliverToSubscription(final Subscription sub, final QueueEntry entry) + private void deliverToConsumer(final QueueConsumer sub, final QueueEntry entry) throws AMQException { @@ -729,14 +723,14 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes try { if (!sub.isSuspended() - && subscriptionReadyAndHasInterest(sub, entry) + && consumerReadyAndHasInterest(sub, entry) && mightAssign(sub, entry) && !sub.wouldSuspend(entry)) { if (sub.acquires() && !assign(sub, entry)) { // restore credit here that would have been taken away by wouldSuspend since we didn't manage - // to acquire the entry for this subscription + // to acquire the entry for this consumer sub.restoreCredit(entry); } else @@ -752,7 +746,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } } - private boolean assign(final Subscription sub, final QueueEntry entry) + private boolean assign(final QueueConsumer sub, final QueueEntry entry) { if(_messageGroupManager == null) { @@ -766,17 +760,17 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } } - private boolean mightAssign(final Subscription sub, final QueueEntry entry) + private boolean mightAssign(final QueueConsumer sub, final QueueEntry entry) { if(_messageGroupManager == null || !sub.acquires()) { return true; } - Subscription assigned = _messageGroupManager.getAssignedSubscription(entry); + QueueConsumer assigned = _messageGroupManager.getAssignedConsumer(entry); return (assigned == null) || (assigned == sub); } - protected void checkSubscriptionsNotAheadOfDelivery(final QueueEntry entry) + protected void checkConsumersNotAheadOfDelivery(final QueueEntry entry) { // This method is only required for queues which mess with ordering // Simple Queues don't :-) @@ -810,19 +804,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes getAtomicQueueCount().incrementAndGet(); } - private void incrementTxnEnqueueStats(final ServerMessage message) - { - _msgTxnEnqueues.incrementAndGet(); - _byteTxnEnqueues.addAndGet(message.getSize()); - } - - private void incrementTxnDequeueStats(QueueEntry entry) - { - _msgTxnDequeues.incrementAndGet(); - _byteTxnDequeues.addAndGet(entry.getSize()); - } - - private void deliverMessage(final Subscription sub, final QueueEntry entry, boolean batch) + private void deliverMessage(final QueueConsumer sub, final QueueEntry entry, boolean batch) throws AMQException { setLastSeenEntry(sub, entry); @@ -833,15 +815,15 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes sub.send(entry, batch); } - private boolean subscriptionReadyAndHasInterest(final Subscription sub, final QueueEntry entry) throws AMQException + private boolean consumerReadyAndHasInterest(final QueueConsumer sub, final QueueEntry entry) throws AMQException { return sub.hasInterest(entry) && (getNextAvailableEntry(sub) == entry); } - private void setLastSeenEntry(final Subscription sub, final QueueEntry entry) + private void setLastSeenEntry(final QueueConsumer sub, final QueueEntry entry) { - QueueContext subContext = (QueueContext) sub.getQueueContext(); + QueueContext subContext = sub.getQueueContext(); if (subContext != null) { QueueEntry releasedEntry = subContext.getReleasedEntry(); @@ -854,10 +836,10 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } } - private void updateSubRequeueEntry(final Subscription sub, final QueueEntry entry) + private void updateSubRequeueEntry(final QueueConsumer sub, final QueueEntry entry) { - QueueContext subContext = (QueueContext) sub.getQueueContext(); + QueueContext subContext = sub.getQueueContext(); if(subContext != null) { QueueEntry oldEntry; @@ -874,11 +856,11 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes public void requeue(QueueEntry entry) { - SubscriptionList.SubscriptionNodeIterator subscriberIter = _subscriptionList.iterator(); + QueueConsumerList.ConsumerNodeIterator subscriberIter = _consumerList.iterator(); // iterate over all the subscribers, and if they are in advance of this queue entry then move them backwards while (subscriberIter.advance() && entry.isAvailable()) { - Subscription sub = subscriberIter.getNode().getSubscription(); + QueueConsumer sub = subscriberIter.getNode().getConsumer(); // we don't make browsers send the same stuff twice if (sub.seesRequeues()) @@ -891,20 +873,15 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } - public void dequeue(QueueEntry entry, Subscription sub) + public void dequeue(QueueEntry entry, Consumer sub) { decrementQueueCount(); decrementQueueSize(entry); - if (entry.acquiredBySubscription()) + if (entry.acquiredByConsumer()) { _deliveredMessages.decrementAndGet(); } - if(sub != null && sub.isSessionTransactional()) - { - incrementTxnDequeueStats(entry); - } - checkCapacity(); } @@ -928,17 +905,17 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes _dequeueCount.incrementAndGet(); } - public boolean resend(final QueueEntry entry, final Subscription subscription) throws AMQException + public boolean resend(final QueueEntry entry, final Consumer consumer) 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. */ + /* TODO : This is wrong as the consumer may be suspended, we should instead change the state of the message + entry to resend and move back the consumer pointer. */ - subscription.getSendLock(); + consumer.getSendLock(); try { - if (!subscription.isClosed()) + if (!consumer.isClosed()) { - deliverMessage(subscription, entry, false); + deliverMessage((QueueConsumer) consumer, entry, false); return true; } else @@ -948,7 +925,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } finally { - subscription.releaseSendLock(); + consumer.releaseSendLock(); } } @@ -956,12 +933,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes public int getConsumerCount() { - return _subscriptionList.size(); - } - - public int getConsumerCountHigh() - { - return _consumerCountHigh.get(); + return _consumerList.size(); } public int getActiveConsumerCount() @@ -1039,16 +1011,16 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } - public void stateChange(Subscription sub, Subscription.State oldState, Subscription.State newState) + public void stateChanged(QueueConsumer sub, QueueConsumer.State oldState, QueueConsumer.State newState) { - if (oldState == Subscription.State.ACTIVE && newState != Subscription.State.ACTIVE) + if (oldState == QueueConsumer.State.ACTIVE && newState != QueueConsumer.State.ACTIVE) { _activeSubscriberCount.decrementAndGet(); } - else if (newState == Subscription.State.ACTIVE) + else if (newState == QueueConsumer.State.ACTIVE) { - if (oldState != Subscription.State.ACTIVE) + if (oldState != QueueConsumer.State.ACTIVE) { _activeSubscriberCount.incrementAndGet(); @@ -1072,12 +1044,12 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes return _atomicQueueSize; } - public boolean hasExclusiveSubscriber() + public boolean hasExclusiveConsumer() { return _exclusiveSubscriber != null; } - private void setExclusiveSubscriber(Subscription exclusiveSubscriber) + private void setExclusiveSubscriber(QueueConsumer exclusiveSubscriber) { _exclusiveSubscriber = exclusiveSubscriber; } @@ -1093,9 +1065,9 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes return _entries; } - protected SubscriptionList getSubscriptionList() + protected QueueConsumerList getConsumerList() { - return _subscriptionList; + return _consumerList; } @@ -1300,12 +1272,12 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes }); } - public void addQueueDeleteTask(final Task task) + public void addQueueDeleteTask(final Action task) { _deleteTaskList.add(task); } - public void removeQueueDeleteTask(final Task task) + public void removeQueueDeleteTask(final Action task) { _deleteTaskList.remove(task); } @@ -1322,19 +1294,21 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes if (!_deleted.getAndSet(true)) { - for (Binding b : _bindings) + final ArrayList bindingCopy = new ArrayList(_bindings); + + for (Binding b : bindingCopy) { b.getExchange().removeBinding(b); } - SubscriptionList.SubscriptionNodeIterator subscriptionIter = _subscriptionList.iterator(); + QueueConsumerList.ConsumerNodeIterator consumerNodeIterator = _consumerList.iterator(); - while (subscriptionIter.advance()) + while (consumerNodeIterator.advance()) { - Subscription s = subscriptionIter.getNode().getSubscription(); + QueueConsumer s = consumerNodeIterator.getNode().getConsumer(); if (s != null) { - s.queueDeleted(this); + s.queueDeleted(); } } @@ -1375,9 +1349,9 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } - for (Task task : _deleteTaskList) + for (Action task : _deleteTaskList) { - task.doTask(this); + task.performAction(this); } _deleteTaskList.clear(); @@ -1461,7 +1435,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } - public void deliverAsync(Subscription sub) + public void deliverAsync(QueueConsumer sub) { if(_exclusiveSubscriber == null) { @@ -1469,28 +1443,23 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } else { - SubFlushRunner flusher = (SubFlushRunner) sub.get(SUB_FLUSH_RUNNER); - if(flusher == null) - { - flusher = new SubFlushRunner(sub); - sub.set(SUB_FLUSH_RUNNER, flusher); - } + SubFlushRunner flusher = sub.getRunner(); flusher.execute(_asyncDelivery); } } - public void flushSubscription(Subscription sub) throws AMQException + void flushConsumer(QueueConsumer sub) throws AMQException { // Access control if (!getVirtualHost().getSecurityManager().authoriseConsume(this)) { throw new AMQSecurityException("Permission denied: " + getName()); } - flushSubscription(sub, Long.MAX_VALUE); + flushConsumer(sub, Long.MAX_VALUE); } - public boolean flushSubscription(Subscription sub, long iterations) throws AMQException + boolean flushConsumer(QueueConsumer sub, long iterations) throws AMQException { boolean atTail = false; final boolean keepSendLockHeld = iterations <= SimpleAMQQueue.MAX_ASYNC_DELIVERIES; @@ -1511,8 +1480,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes sub.getSendLock(); } - atTail = attemptDelivery(sub, true); - if (atTail && getNextAvailableEntry(sub) == null) + atTail = attemptDelivery((QueueConsumer)sub, true); + if (atTail && getNextAvailableEntry((QueueConsumer)sub) == null) { queueEmpty = true; } @@ -1546,21 +1515,21 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } - // if there's (potentially) more than one subscription the others will potentially not have been advanced to the + // if there's (potentially) more than one consumer the others will potentially not have been advanced to the // next entry they are interested in yet. This would lead to holding on to references to expired messages, etc // which would give us memory "leak". - if (!hasExclusiveSubscriber()) + if (!hasExclusiveConsumer()) { - advanceAllSubscriptions(); + advanceAllConsumers(); } return atTail; } /** - * Attempt delivery for the given subscription. + * Attempt delivery for the given consumer. * - * Looks up the next node for the subscription and attempts to deliver it. + * Looks up the next node for the consumer and attempts to deliver it. * * * @param sub @@ -1568,7 +1537,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes * @return true if we have completed all possible deliveries for this sub. * @throws AMQException */ - private boolean attemptDelivery(Subscription sub, boolean batch) throws AMQException + private boolean attemptDelivery(QueueConsumer sub, boolean batch) throws AMQException { boolean atTail = false; @@ -1587,7 +1556,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes if (sub.acquires() && !assign(sub, node)) { // restore credit here that would have been taken away by wouldSuspend since we didn't manage - // to acquire the entry for this subscription + // to acquire the entry for this consumer sub.restoreCredit(node); } else @@ -1598,7 +1567,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } else // Not enough Credit for message and wouldSuspend { - //QPID-1187 - Treat the subscription as suspended for this message + //QPID-1187 - Treat the consumer as suspended for this message // and wait for the message to be removed to continue delivery. subActive = false; node.addStateChangeListener(new QueueEntryListener(sub)); @@ -1611,13 +1580,13 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes return atTail || !subActive; } - protected void advanceAllSubscriptions() throws AMQException + protected void advanceAllConsumers() throws AMQException { - SubscriptionList.SubscriptionNodeIterator subscriberIter = _subscriptionList.iterator(); - while (subscriberIter.advance()) + QueueConsumerList.ConsumerNodeIterator consumerNodeIterator = _consumerList.iterator(); + while (consumerNodeIterator.advance()) { - SubscriptionList.SubscriptionNode subNode = subscriberIter.getNode(); - Subscription sub = subNode.getSubscription(); + QueueConsumerList.ConsumerNode subNode = consumerNodeIterator.getNode(); + QueueConsumer sub = subNode.getConsumer(); if(sub.acquires()) { getNextAvailableEntry(sub); @@ -1629,10 +1598,10 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } } - private QueueEntry getNextAvailableEntry(final Subscription sub) + private QueueEntry getNextAvailableEntry(final QueueConsumer sub) throws AMQException { - QueueContext context = (QueueContext) sub.getQueueContext(); + QueueContext context = sub.getQueueContext(); if(context != null) { QueueEntry lastSeen = context.getLastSeenEntry(); @@ -1670,9 +1639,9 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } } - public boolean isEntryAheadOfSubscription(QueueEntry entry, Subscription sub) + public boolean isEntryAheadOfConsumer(QueueEntry entry, QueueConsumer sub) { - QueueContext context = (QueueContext) sub.getQueueContext(); + QueueContext context = sub.getQueueContext(); if(context != null) { QueueEntry releasedNode = context.getReleasedEntry(); @@ -1689,14 +1658,14 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes * * A queue Runner is started whenever a state change occurs, e.g when a new * message arrives on the queue and cannot be immediately delivered to a - * subscription (i.e. asynchronous delivery is required). Unless there are - * SubFlushRunners operating (due to subscriptions unsuspending) which are + * consumer (i.e. asynchronous delivery is required). Unless there are + * SubFlushRunners operating (due to consumers unsuspending) which are * capable of accepting/delivering all messages then these messages would * otherwise remain on the queue. * * processQueue should be running while there are messages on the queue AND - * there are subscriptions that can deliver them. If there are no - * subscriptions capable of delivering the remaining messages on the queue + * there are consumers that can deliver them. If there are no + * consumers capable of delivering the remaining messages on the queue * then processQueue should stop to prevent spinning. * * Since processQueue is runs in a fixed size Executor, it should not run @@ -1720,7 +1689,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes boolean lastLoop = false; int iterations = MAX_ASYNC_DELIVERIES; - final int numSubs = _subscriptionList.size(); + final int numSubs = _consumerList.size(); final int perSub = Math.max(iterations / Math.max(numSubs,1), 1); @@ -1731,8 +1700,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes // So whilst delivery/rejection is going on a processQueue thread will be running while (iterations != 0 && ((previousStateChangeCount != (stateChangeCount = _stateChangeCount.get())) || deliveryIncomplete)) { - // we want to have one extra loop after 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 + // we want to have one extra loop after every consumer has reached the point where it cannot move + // further, just in case the advance of one consumer in the last loop allows a different consumer to // move forward in the next iteration if (previousStateChangeCount != stateChangeCount) @@ -1744,14 +1713,14 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } previousStateChangeCount = stateChangeCount; - boolean allSubscriptionsDone = true; - boolean subscriptionDone; + boolean allConsumersDone = true; + boolean consumerDone; - SubscriptionList.SubscriptionNodeIterator subscriptionIter = _subscriptionList.iterator(); + QueueConsumerList.ConsumerNodeIterator consumerNodeIterator = _consumerList.iterator(); //iterate over the subscribers and try to advance their pointer - while (subscriptionIter.advance()) + while (consumerNodeIterator.advance()) { - Subscription sub = subscriptionIter.getNode().getSubscription(); + QueueConsumer sub = consumerNodeIterator.getNode().getConsumer(); sub.getSendLock(); try @@ -1759,8 +1728,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes for(int i = 0 ; i < perSub; i++) { //attempt delivery. returns true if no further delivery currently possible to this sub - subscriptionDone = attemptDelivery(sub, true); - if (subscriptionDone) + consumerDone = attemptDelivery(sub, true); + if (consumerDone) { sub.flushBatched(); if (lastLoop && !sub.isSuspended()) @@ -1771,9 +1740,9 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } else { - //this subscription can accept additional deliveries, so we must + //this consumer can accept additional deliveries, so we must //keep going after this (if iteration slicing allows it) - allSubscriptionsDone = false; + allConsumersDone = false; lastLoop = false; if(--iterations == 0) { @@ -1792,24 +1761,24 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } } - if(allSubscriptionsDone && lastLoop) + if(allConsumersDone && lastLoop) { //We have done an extra loop already and there are again //again no further delivery attempts possible, only //keep going if state change demands it. deliveryIncomplete = false; } - else if(allSubscriptionsDone) + else if(allConsumersDone) { - //All subscriptions reported being done, but we have to do + //All consumers reported being done, but we have to do //an extra loop if the iterations are not exhausted and //there is still any work to be done - deliveryIncomplete = _subscriptionList.size() != 0; + deliveryIncomplete = _consumerList.size() != 0; lastLoop = true; } else { - //some subscriptions can still accept more messages, + //some consumers can still accept more messages, //keep going if iteration count allows. lastLoop = false; deliveryIncomplete = true; @@ -1984,12 +1953,12 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes return _notificationChecks; } - private final class QueueEntryListener implements QueueEntry.StateChangeListener + private final class QueueEntryListener implements StateChangeListener, QueueEntry.State> { - private final Subscription _sub; + private final QueueConsumer _sub; - public QueueEntryListener(final Subscription sub) + public QueueEntryListener(final QueueConsumer sub) { _sub = sub; } @@ -2005,7 +1974,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes return System.identityHashCode(_sub); } - public void stateChanged(QueueEntry entry, QueueEntry.State oldSate, QueueEntry.State newState) + public void stateChanged(MessageInstance entry, QueueEntry.State oldSate, QueueEntry.State newState) { entry.removeStateChangeListener(this); deliverAsync(_sub); @@ -2076,26 +2045,6 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes return _dequeueSize.get(); } - public long getByteTxnEnqueues() - { - return _byteTxnEnqueues.get(); - } - - public long getByteTxnDequeues() - { - return _byteTxnDequeues.get(); - } - - public long getMsgTxnEnqueues() - { - return _msgTxnEnqueues.get(); - } - - public long getMsgTxnDequeues() - { - return _msgTxnDequeues.get(); - } - public long getPersistentByteEnqueues() { return _persistentMessageEnqueueSize.get(); @@ -2123,11 +2072,6 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes return getName(); } - public long getUnackedMessageCountHigh() - { - return _unackedMsgCountHigh.get(); - } - public long getUnackedMessageCount() { return _unackedMsgCount.get(); @@ -2146,17 +2090,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes private void incrementUnackedMsgCount(QueueEntry entry) { - long unackedMsgCount = _unackedMsgCount.incrementAndGet(); + _unackedMsgCount.incrementAndGet(); _unackedMsgBytes.addAndGet(entry.getSize()); - - long unackedMsgCountHigh; - while(unackedMsgCount > (unackedMsgCountHigh = _unackedMsgCountHigh.get())) - { - if(_unackedMsgCountHigh.compareAndSet(unackedMsgCountHigh, unackedMsgCount)) - { - break; - } - } } public LogActor getLogActor() @@ -2224,4 +2159,39 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes return (String) _arguments.get(Queue.DESCRIPTION); } + public final int send(final ServerMessage message, + final InstanceProperties instanceProperties, + final ServerTransaction txn, + final Action> postEnqueueAction) + { + txn.enqueue(this,message, new ServerTransaction.Action() + { + MessageReference _reference = message.newReference(); + + public void postCommit() + { + try + { + SimpleAMQQueue.this.enqueue(message, postEnqueueAction); + } + catch (AMQException e) + { + // TODO + throw new RuntimeException(e); + } + finally + { + _reference.release(); + } + } + + public void onRollback() + { + _reference.release(); + } + }); + return 1; + + } + } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java index b8d8ec19f4..101771c7cc 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java @@ -39,7 +39,7 @@ public class SimpleQueueEntryList implements QueueEntryList _queue; static final AtomicReferenceFieldUpdater _nextUpdater = SimpleQueueEntryImpl._nextUpdater; @@ -49,7 +49,7 @@ public class SimpleQueueEntryList implements QueueEntryList _unscavengedHWM = new AtomicReference(); - public SimpleQueueEntryList(AMQQueue queue) + public SimpleQueueEntryList(AMQQueue queue) { _queue = queue; _head = new SimpleQueueEntryImpl(this); @@ -71,7 +71,7 @@ public class SimpleQueueEntryList implements QueueEntryList getQueue() { return _queue; } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SortedQueue.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SortedQueue.java index b3566df0c4..cad1aa6d4f 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SortedQueue.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SortedQueue.java @@ -20,7 +20,10 @@ package org.apache.qpid.server.queue; import org.apache.qpid.AMQException; +import org.apache.qpid.server.consumer.Consumer; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.util.Action; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Map; @@ -29,7 +32,7 @@ import java.util.UUID; public class SortedQueue extends OutOfOrderQueue { //Lock object to synchronize enqueue. Used instead of the object - //monitor to prevent lock order issues with subscription sendLocks + //monitor to prevent lock order issues with consumer sendLocks //and consumer updates in the super classes private final Object _sortedQueueLock = new Object(); private final String _sortedPropertyName; @@ -48,7 +51,7 @@ public class SortedQueue extends OutOfOrderQueue return _sortedPropertyName; } - public void enqueue(ServerMessage message, PostEnqueueAction action) throws AMQException + public void enqueue(ServerMessage message, Action> action) throws AMQException { synchronized (_sortedQueueLock) { diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryList.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryList.java index 85559157a9..336ee566eb 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryList.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SortedQueueEntryList.java @@ -28,7 +28,7 @@ import org.apache.qpid.server.queue.SortedQueueEntryImpl.Colour; * Uses the red/black tree algorithm specified in "Introduction to Algorithms". * ISBN-10: 0262033844 * ISBN-13: 978-0262033848 - * @see http://en.wikipedia.org/wiki/Red-black_tree + * see http://en.wikipedia.org/wiki/Red-black_tree */ public class SortedQueueEntryList implements QueueEntryList { @@ -36,17 +36,17 @@ public class SortedQueueEntryList implements QueueEntryList _queue; private final String _propertyName; - public SortedQueueEntryList(final AMQQueue queue, final String propertyName) + public SortedQueueEntryList(final AMQQueue queue, final String propertyName) { _queue = queue; _head = new SortedQueueEntryImpl(this); _propertyName = propertyName; } - public AMQQueue getQueue() + public AMQQueue getQueue() { return _queue; } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SubFlushRunner.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SubFlushRunner.java index 47a7d733dd..c30a48b03a 100755 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SubFlushRunner.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/queue/SubFlushRunner.java @@ -25,7 +25,6 @@ import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.transport.TransportException; import java.util.concurrent.Executor; @@ -38,7 +37,7 @@ class SubFlushRunner implements Runnable private static final Logger _logger = Logger.getLogger(SubFlushRunner.class); - private final Subscription _sub; + private final QueueConsumer _sub; private static int IDLE = 0; private static int SCHEDULED = 1; @@ -51,7 +50,7 @@ class SubFlushRunner implements Runnable private static final long ITERATIONS = SimpleAMQQueue.MAX_ASYNC_DELIVERIES; private final AtomicBoolean _stateChange = new AtomicBoolean(); - public SubFlushRunner(Subscription sub) + public SubFlushRunner(QueueConsumer sub) { _sub = sub; } @@ -65,7 +64,7 @@ class SubFlushRunner implements Runnable try { CurrentActor.set(_sub.getLogActor()); - complete = getQueue().flushSubscription(_sub, ITERATIONS); + complete = getQueue().flushConsumer(_sub, ITERATIONS); } catch (AMQException e) { diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java index e490ac38c1..d80fa656e7 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/AbstractJDBCMessageStore.java @@ -1065,7 +1065,7 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC stmt.setString(4, "E"); for(Transaction.Record record : enqueues) { - stmt.setString(5, record.getQueue().getId().toString()); + stmt.setString(5, record.getResource().getId().toString()); stmt.setLong(6, record.getMessage().getMessageNumber()); stmt.executeUpdate(); } @@ -1076,7 +1076,7 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC stmt.setString(4, "D"); for(Transaction.Record record : dequeues) { - stmt.setString(5, record.getQueue().getId().toString()); + stmt.setString(5, record.getResource().getId().toString()); stmt.setLong(6, record.getMessage().getMessageNumber()); stmt.executeUpdate(); } @@ -1199,7 +1199,7 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC buf.position(1); buf = buf.slice(); - metaData.writeToBuffer(0, buf); + metaData.writeToBuffer(buf); ByteArrayInputStream bis = new ByteArrayInputStream(underlying); try { @@ -1371,7 +1371,7 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC } @Override - public TransactionLogResource getQueue() + public TransactionLogResource getResource() { return this; } @@ -1400,11 +1400,23 @@ abstract public class AbstractJDBCMessageStore implements MessageStore, DurableC throw new UnsupportedOperationException(); } + @Override + public String getName() + { + return _queueId.toString(); + } + @Override public UUID getId() { return _queueId; } + + @Override + public boolean isDurable() + { + return true; + } } protected void recoverXids(TransactionLogRecoveryHandler.DtxRecordRecoveryHandler dtxrh) throws SQLException diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreHelper.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreHelper.java index a688b493e1..de7369f5ed 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreHelper.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/DurableConfigurationStoreHelper.java @@ -29,6 +29,7 @@ import java.util.Map; import java.util.Set; import org.apache.qpid.AMQStoreException; +import org.apache.qpid.server.consumer.Consumer; import org.apache.qpid.server.model.Binding; import org.apache.qpid.server.model.Exchange; import org.apache.qpid.server.model.LifetimePolicy; @@ -46,7 +47,7 @@ public class DurableConfigurationStoreHelper Queue.EXCLUSIVE, Queue.ALTERNATE_EXCHANGE)); - public static void updateQueue(DurableConfigurationStore store, AMQQueue queue) throws AMQStoreException + public static void updateQueue(DurableConfigurationStore store, AMQQueue queue) throws AMQStoreException { Map attributesMap = new LinkedHashMap(); attributesMap.put(Queue.NAME, queue.getName()); @@ -71,7 +72,7 @@ public class DurableConfigurationStoreHelper store.update(queue.getId(), QUEUE, attributesMap); } - public static void createQueue(DurableConfigurationStore store, AMQQueue queue) + public static void createQueue(DurableConfigurationStore store, AMQQueue queue) throws AMQStoreException { Map attributesMap = new HashMap(); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/StorableMessageMetaData.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/StorableMessageMetaData.java index 9ae6cca8e6..4fd452649d 100755 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/StorableMessageMetaData.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/StorableMessageMetaData.java @@ -29,7 +29,7 @@ public interface StorableMessageMetaData int getStorableSize(); - int writeToBuffer(int offsetInMetaData, ByteBuffer dest); + int writeToBuffer(ByteBuffer dest); int getContentSize(); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/Transaction.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/Transaction.java index 66bcfff32b..74b91dec2d 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/Transaction.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/Transaction.java @@ -70,7 +70,7 @@ public interface Transaction public static interface Record { - TransactionLogResource getQueue(); + TransactionLogResource getResource(); EnqueueableMessage getMessage(); } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/TransactionLogResource.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/TransactionLogResource.java index 576dca847d..18b3125641 100755 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/TransactionLogResource.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/store/TransactionLogResource.java @@ -24,5 +24,7 @@ import java.util.UUID; public interface TransactionLogResource { + String getName(); public UUID getId(); + boolean isDurable(); } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/AssignedSubscriptionMessageGroupManager.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/AssignedSubscriptionMessageGroupManager.java deleted file mode 100644 index ae7e11afa4..0000000000 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/AssignedSubscriptionMessageGroupManager.java +++ /dev/null @@ -1,170 +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.subscription; - -import org.apache.qpid.server.queue.QueueEntryVisitor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.apache.qpid.server.queue.QueueEntry; - -import java.util.Iterator; -import java.util.concurrent.ConcurrentHashMap; - - -public class AssignedSubscriptionMessageGroupManager implements MessageGroupManager -{ - private static final Logger _logger = LoggerFactory.getLogger(AssignedSubscriptionMessageGroupManager.class); - - - private final String _groupId; - private final ConcurrentHashMap _groupMap = new ConcurrentHashMap(); - private final int _groupMask; - - public AssignedSubscriptionMessageGroupManager(final String groupId, final int maxGroups) - { - _groupId = groupId; - _groupMask = pow2(maxGroups)-1; - } - - private static int pow2(final int i) - { - int val = 1; - while(val < i) - { - val<<=1; - } - return val; - } - - public Subscription getAssignedSubscription(final QueueEntry entry) - { - Object groupVal = entry.getMessage().getMessageHeader().getHeader(_groupId); - return groupVal == null ? null : _groupMap.get(groupVal.hashCode() & _groupMask); - } - - public boolean acceptMessage(Subscription sub, QueueEntry entry) - { - if(assignMessage(sub, entry)) - { - return entry.acquire(sub); - } - else - { - return false; - } - } - - private boolean assignMessage(Subscription sub, QueueEntry entry) - { - Object groupVal = entry.getMessage().getMessageHeader().getHeader(_groupId); - if(groupVal == null) - { - return true; - } - else - { - Integer group = groupVal.hashCode() & _groupMask; - Subscription assignedSub = _groupMap.get(group); - if(assignedSub == sub) - { - return true; - } - else - { - if(assignedSub == null) - { - if(_logger.isDebugEnabled()) - { - _logger.debug("Assigning group " + groupVal + " to sub " + sub); - } - assignedSub = _groupMap.putIfAbsent(group, sub); - return assignedSub == null || assignedSub == sub; - } - else - { - return false; - } - } - } - } - - public QueueEntry findEarliestAssignedAvailableEntry(Subscription sub) - { - EntryFinder visitor = new EntryFinder(sub); - sub.getQueue().visit(visitor); - return visitor.getEntry(); - } - - private class EntryFinder implements QueueEntryVisitor - { - private QueueEntry _entry; - private Subscription _sub; - - public EntryFinder(final Subscription sub) - { - _sub = sub; - } - - public boolean visit(final QueueEntry entry) - { - if(!entry.isAvailable()) - { - return false; - } - - Object groupId = entry.getMessage().getMessageHeader().getHeader(_groupId); - if(groupId == null) - { - return false; - } - - Integer group = groupId.hashCode() & _groupMask; - Subscription assignedSub = _groupMap.get(group); - if(assignedSub == _sub) - { - _entry = entry; - return true; - } - else - { - return false; - } - } - - public QueueEntry getEntry() - { - return _entry; - } - } - - public void clearAssignments(Subscription sub) - { - Iterator subIter = _groupMap.values().iterator(); - while(subIter.hasNext()) - { - if(subIter.next() == sub) - { - subIter.remove(); - } - } - } -} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/ClientDeliveryMethod.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/ClientDeliveryMethod.java deleted file mode 100644 index 45a1978af1..0000000000 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/ClientDeliveryMethod.java +++ /dev/null @@ -1,31 +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.subscription; - -import org.apache.qpid.AMQException; -import org.apache.qpid.server.message.InstanceProperties; -import org.apache.qpid.server.message.ServerMessage; - -public interface ClientDeliveryMethod -{ - void deliverToClient(final Subscription sub, final ServerMessage message, final InstanceProperties props, - final long deliveryTag) throws AMQException; -} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/DefinedGroupMessageGroupManager.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/DefinedGroupMessageGroupManager.java deleted file mode 100644 index 55110c46de..0000000000 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/DefinedGroupMessageGroupManager.java +++ /dev/null @@ -1,280 +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.subscription; - -import org.apache.qpid.server.queue.QueueEntryVisitor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.apache.qpid.server.message.AMQMessageHeader; -import org.apache.qpid.server.message.ServerMessage; -import org.apache.qpid.server.queue.QueueEntry; - -import java.util.HashMap; -import java.util.Map; - -public class DefinedGroupMessageGroupManager implements MessageGroupManager -{ - private static final Logger _logger = LoggerFactory.getLogger(DefinedGroupMessageGroupManager.class); - - private final String _groupId; - private final String _defaultGroup; - private final Map _groupMap = new HashMap(); - private final SubscriptionResetHelper _resetHelper; - - private final class Group - { - private final Object _group; - private Subscription _subscription; - private int _activeCount; - - private Group(final Object key, final Subscription subscription) - { - _group = key; - _subscription = subscription; - } - - public boolean add() - { - if(_subscription != null) - { - _activeCount++; - return true; - } - else - { - return false; - } - } - - public void subtract() - { - if(--_activeCount == 0) - { - _resetHelper.resetSubPointersForGroups(_subscription, false); - _subscription = null; - _groupMap.remove(_group); - } - } - - @Override - public boolean equals(final Object o) - { - if (this == o) - { - return true; - } - if (o == null || getClass() != o.getClass()) - { - return false; - } - - Group group = (Group) o; - - return _group.equals(group._group); - } - - @Override - public int hashCode() - { - return _group.hashCode(); - } - - public boolean isValid() - { - return !(_subscription == null || (_activeCount == 0 && _subscription.isClosed())); - } - - public Subscription getSubscription() - { - return _subscription; - } - - @Override - public String toString() - { - return "Group{" + - "_group=" + _group + - ", _subscription=" + _subscription + - ", _activeCount=" + _activeCount + - '}'; - } - } - - public DefinedGroupMessageGroupManager(final String groupId, String defaultGroup, SubscriptionResetHelper resetHelper) - { - _groupId = groupId; - _defaultGroup = defaultGroup; - _resetHelper = resetHelper; - } - - public synchronized Subscription getAssignedSubscription(final QueueEntry entry) - { - Object groupId = getKey(entry); - - Group group = _groupMap.get(groupId); - return group == null || !group.isValid() ? null : group.getSubscription(); - } - - public synchronized boolean acceptMessage(final Subscription sub, final QueueEntry entry) - { - if(assignMessage(sub, entry)) - { - return entry.acquire(sub); - } - else - { - return false; - } - } - - private boolean assignMessage(final Subscription sub, final QueueEntry entry) - { - Object groupId = getKey(entry); - Group group = _groupMap.get(groupId); - - if(group == null || !group.isValid()) - { - group = new Group(groupId, sub); - - _groupMap.put(groupId, group); - - // there's a small change that the group became empty between the point at which getNextAvailable() was - // called on the subscription, and when accept message is called... in that case we want to avoid delivering - // out of order - if(_resetHelper.isEntryAheadOfSubscription(entry, sub)) - { - return false; - } - } - - Subscription assignedSub = group.getSubscription(); - - if(assignedSub == sub) - { - entry.addStateChangeListener(new GroupStateChangeListener(group, entry)); - return true; - } - else - { - return false; - } - } - - public synchronized QueueEntry findEarliestAssignedAvailableEntry(final Subscription sub) - { - EntryFinder visitor = new EntryFinder(sub); - sub.getQueue().visit(visitor); - return visitor.getEntry(); - } - - private class EntryFinder implements QueueEntryVisitor - { - private QueueEntry _entry; - private Subscription _sub; - - public EntryFinder(final Subscription sub) - { - _sub = sub; - } - - public boolean visit(final QueueEntry entry) - { - if(!entry.isAvailable()) - { - return false; - } - - Object groupId = getKey(entry); - - Group group = _groupMap.get(groupId); - if(group != null && group.getSubscription() == _sub) - { - _entry = entry; - return true; - } - else - { - return false; - } - } - - public QueueEntry getEntry() - { - return _entry; - } - } - - - public void clearAssignments(final Subscription sub) - { - } - - private Object getKey(QueueEntry entry) - { - ServerMessage message = entry.getMessage(); - AMQMessageHeader messageHeader = message == null ? null : message.getMessageHeader(); - Object groupVal = messageHeader == null ? _defaultGroup : messageHeader.getHeader(_groupId); - if(groupVal == null) - { - groupVal = _defaultGroup; - } - return groupVal; - } - - private class GroupStateChangeListener implements QueueEntry.StateChangeListener - { - private final Group _group; - - public GroupStateChangeListener(final Group group, - final QueueEntry entry) - { - _group = group; - } - - public void stateChanged(final QueueEntry entry, - final QueueEntry.State oldState, - final QueueEntry.State newState) - { - synchronized (DefinedGroupMessageGroupManager.this) - { - if(_group.isValid()) - { - if(oldState != newState) - { - if(newState == QueueEntry.State.ACQUIRED) - { - _group.add(); - } - else if(oldState == QueueEntry.State.ACQUIRED) - { - _group.subtract(); - } - } - } - else - { - entry.removeStateChangeListener(this); - } - } - } - } -} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/MessageGroupManager.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/MessageGroupManager.java deleted file mode 100644 index 8ce4ce3344..0000000000 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/MessageGroupManager.java +++ /dev/null @@ -1,41 +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.subscription; - -import org.apache.qpid.server.queue.QueueEntry; - -public interface MessageGroupManager -{ - public interface SubscriptionResetHelper - { - public void resetSubPointersForGroups(Subscription subscription, boolean clearAssignments); - - boolean isEntryAheadOfSubscription(QueueEntry entry, Subscription sub); - } - - Subscription getAssignedSubscription(QueueEntry entry); - - boolean acceptMessage(Subscription sub, QueueEntry entry); - - QueueEntry findEarliestAssignedAvailableEntry(Subscription sub); - - void clearAssignments(Subscription sub); -} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/RecordDeliveryMethod.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/RecordDeliveryMethod.java deleted file mode 100644 index e2ed4104de..0000000000 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/RecordDeliveryMethod.java +++ /dev/null @@ -1,28 +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.subscription; - -import org.apache.qpid.server.queue.QueueEntry; - -public interface RecordDeliveryMethod -{ - void recordMessageDelivery(final Subscription sub, final QueueEntry entry, final long deliveryTag); -} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/Subscription.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/Subscription.java deleted file mode 100644 index fde3d3809c..0000000000 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/Subscription.java +++ /dev/null @@ -1,123 +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.subscription; - -import java.util.concurrent.atomic.AtomicLong; -import org.apache.qpid.AMQException; -import org.apache.qpid.server.logging.LogActor; -import org.apache.qpid.server.protocol.AMQSessionModel; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.QueueEntry; - -public interface Subscription -{ - AtomicLong SUB_ID_GENERATOR = new AtomicLong(0); - - LogActor getLogActor(); - - boolean isTransient(); - - long getBytesOut(); - - long getMessagesOut(); - - long getUnacknowledgedBytes(); - - long getUnacknowledgedMessages(); - - public static enum State - { - ACTIVE, - SUSPENDED, - CLOSED - } - - public static interface StateListener - { - public void stateChange(Subscription sub, State oldState, State newState); - } - - AMQQueue getQueue(); - AMQSessionModel getSessionModel(); - - QueueEntry.SubscriptionAcquiredState getOwningState(); - - void setQueue(AMQQueue queue, boolean exclusive); - - void setNoLocal(boolean noLocal); - - long getSubscriptionID(); - - boolean isSuspended(); - - boolean hasInterest(QueueEntry msg); - - boolean isClosed(); - - boolean acquires(); - - boolean seesRequeues(); - - void close(); - - void send(QueueEntry entry, boolean batch) throws AMQException; - - void flushBatched(); - - void queueDeleted(AMQQueue queue); - - - boolean wouldSuspend(QueueEntry msg); - - boolean trySendLock(); - - - void getSendLock(); - - void releaseSendLock(); - - void releaseQueueEntry(final QueueEntry queueEntryImpl); - - void onDequeue(final QueueEntry queueEntry); - - void restoreCredit(final QueueEntry queueEntry); - - void setStateListener(final StateListener listener); - - public State getState(); - - AMQQueue.Context getQueueContext(); - - void setQueueContext(AMQQueue.Context queueContext); - - - boolean isActive(); - - public void set(String key, Object value); - - public Object get(String key); - - boolean isSessionTransactional(); - - void queueEmpty() throws AMQException; - - String getConsumerName(); -} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/SubscriptionList.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/SubscriptionList.java deleted file mode 100644 index 23d4fb241f..0000000000 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/subscription/SubscriptionList.java +++ /dev/null @@ -1,280 +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.subscription; - -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - -public class SubscriptionList -{ - private final SubscriptionNode _head = new SubscriptionNode(); - - private final AtomicReference _tail = new AtomicReference(_head); - private final AtomicReference _subNodeMarker = new AtomicReference(_head); - private final AtomicInteger _size = new AtomicInteger(); - - public static final class SubscriptionNode - { - private final AtomicBoolean _deleted = new AtomicBoolean(); - private final AtomicReference _next = new AtomicReference(); - private final Subscription _sub; - - public SubscriptionNode() - { - //used for sentinel head and dummy node construction - _sub = null; - _deleted.set(true); - } - - public SubscriptionNode(final Subscription sub) - { - //used for regular node construction - _sub = sub; - } - - /** - * Retrieves the first non-deleted node following the current node. - * Any deleted non-tail nodes encountered during the search are unlinked. - * - * @return the next non-deleted node, or null if none was found. - */ - public SubscriptionNode findNext() - { - SubscriptionNode next = nextNode(); - while(next != null && next.isDeleted()) - { - final SubscriptionNode newNext = next.nextNode(); - if(newNext != null) - { - //try to move our _next reference forward to the 'newNext' - //node to unlink the deleted node - _next.compareAndSet(next, newNext); - next = nextNode(); - } - else - { - //'newNext' is null, meaning 'next' is the current tail. Can't unlink - //the tail node for thread safety reasons, just use the null. - next = null; - } - } - - return next; - } - - /** - * Gets the immediately next referenced node in the structure. - * - * @return the immediately next node in the structure, or null if at the tail. - */ - protected SubscriptionNode nextNode() - { - return _next.get(); - } - - /** - * Used to initialise the 'next' reference. Will only succeed if the reference was not previously set. - * - * @param node the SubscriptionNode to set as 'next' - * @return whether the operation succeeded - */ - private boolean setNext(final SubscriptionNode node) - { - return _next.compareAndSet(null, node); - } - - public boolean isDeleted() - { - return _deleted.get(); - } - - public boolean delete() - { - return _deleted.compareAndSet(false,true); - } - - public Subscription getSubscription() - { - return _sub; - } - } - - private void insert(final SubscriptionNode node, final boolean count) - { - for (;;) - { - SubscriptionNode tail = _tail.get(); - SubscriptionNode next = tail.nextNode(); - if (tail == _tail.get()) - { - if (next == null) - { - if (tail.setNext(node)) - { - _tail.compareAndSet(tail, node); - if(count) - { - _size.incrementAndGet(); - } - return; - } - } - else - { - _tail.compareAndSet(tail, next); - } - } - } - } - - public void add(final Subscription sub) - { - SubscriptionNode node = new SubscriptionNode(sub); - insert(node, true); - } - - public boolean remove(final Subscription sub) - { - SubscriptionNode prevNode = _head; - SubscriptionNode node = _head.nextNode(); - - while(node != null) - { - if(sub.equals(node.getSubscription()) && node.delete()) - { - _size.decrementAndGet(); - - SubscriptionNode tail = _tail.get(); - if(node == tail) - { - //we cant remove the last node from the structure for - //correctness reasons, however we have just 'deleted' - //the tail. Inserting an empty dummy node after it will - //let us scavenge the node containing the Subscription. - insert(new SubscriptionNode(), false); - } - - //advance the next node reference in the 'prevNode' to scavenge - //the newly 'deleted' node for the Subscription. - prevNode.findNext(); - - nodeMarkerCleanup(node); - - return true; - } - - prevNode = node; - node = node.findNext(); - } - - return false; - } - - private void nodeMarkerCleanup(final SubscriptionNode node) - { - SubscriptionNode markedNode = _subNodeMarker.get(); - if(node == markedNode) - { - //if the marked node is the one we are removing, then - //replace it with a dummy pointing at the next node. - //this is OK as the marked node is only used to index - //into the list and find the next node to use. - //Because we inserted a dummy if node was the - //tail, markedNode.nextNode() can never be null. - SubscriptionNode dummy = new SubscriptionNode(); - dummy.setNext(markedNode.nextNode()); - - //if the CAS fails the marked node has changed, thus - //we don't care about the dummy and just forget it - _subNodeMarker.compareAndSet(markedNode, dummy); - } - else if(markedNode != null) - { - //if the marked node was already deleted then it could - //hold subsequently removed nodes after it in the list - //in memory. Scavenge it to ensure their actual removal. - if(markedNode != _head && markedNode.isDeleted()) - { - markedNode.findNext(); - } - } - } - - public boolean updateMarkedNode(final SubscriptionNode expected, final SubscriptionNode nextNode) - { - return _subNodeMarker.compareAndSet(expected, nextNode); - } - - /** - * Get the current marked SubscriptionNode. This should only be used only to index into the list and find the next node - * after the mark, since if the previously marked node was subsequently deleted the item returned may be a dummy node - * with reference to the next node. - * - * @return the previously marked node (or a dummy if it was subsequently deleted) - */ - public SubscriptionNode getMarkedNode() - { - return _subNodeMarker.get(); - } - - - public static class SubscriptionNodeIterator - { - private SubscriptionNode _lastNode; - - SubscriptionNodeIterator(SubscriptionNode startNode) - { - _lastNode = startNode; - } - - public SubscriptionNode getNode() - { - return _lastNode; - } - - public boolean advance() - { - SubscriptionNode nextNode = _lastNode.findNext(); - _lastNode = nextNode; - - return _lastNode != null; - } - } - - public SubscriptionNodeIterator iterator() - { - return new SubscriptionNodeIterator(_head); - } - - public SubscriptionNode getHead() - { - return _head; - } - - public int size() - { - return _size.get(); - } -} - - - diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/AsyncAutoCommitTransaction.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/AsyncAutoCommitTransaction.java index 1f5a4907ed..57c67f54cd 100755 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/AsyncAutoCommitTransaction.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/AsyncAutoCommitTransaction.java @@ -25,12 +25,14 @@ import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.AMQStoreException; import org.apache.qpid.server.message.EnqueueableMessage; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.StoreFuture; import org.apache.qpid.server.store.Transaction; +import org.apache.qpid.server.store.TransactionLogResource; import java.util.Collection; import java.util.List; @@ -88,7 +90,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction } - public void dequeue(BaseQueue queue, EnqueueableMessage message, Action postTransactionAction) + public void dequeue(TransactionLogResource queue, EnqueueableMessage message, Action postTransactionAction) { Transaction txn = null; try @@ -158,15 +160,15 @@ public class AsyncAutoCommitTransaction implements ServerTransaction } } - public void dequeue(Collection queueEntries, Action postTransactionAction) + public void dequeue(Collection queueEntries, Action postTransactionAction) { Transaction txn = null; try { - for(QueueEntry entry : queueEntries) + for(MessageInstance entry : queueEntries) { ServerMessage message = entry.getMessage(); - BaseQueue queue = entry.getQueue(); + TransactionLogResource queue = entry.getOwningResource(); if(message.isPersistent() && queue.isDurable()) { @@ -210,7 +212,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction } - public void enqueue(BaseQueue queue, EnqueueableMessage message, Action postTransactionAction) + public void enqueue(TransactionLogResource queue, EnqueueableMessage message, Action postTransactionAction) { Transaction txn = null; try diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java index b057998456..4ea48c6a24 100755 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java @@ -25,11 +25,13 @@ import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.AMQStoreException; import org.apache.qpid.server.message.EnqueueableMessage; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.Transaction; +import org.apache.qpid.server.store.TransactionLogResource; import java.util.Collection; import java.util.List; @@ -73,7 +75,7 @@ public class AutoCommitTransaction implements ServerTransaction immediateAction.postCommit(); } - public void dequeue(BaseQueue queue, EnqueueableMessage message, Action postTransactionAction) + public void dequeue(TransactionLogResource queue, EnqueueableMessage message, Action postTransactionAction) { Transaction txn = null; try @@ -105,15 +107,15 @@ public class AutoCommitTransaction implements ServerTransaction } - public void dequeue(Collection queueEntries, Action postTransactionAction) + public void dequeue(Collection queueEntries, Action postTransactionAction) { Transaction txn = null; try { - for(QueueEntry entry : queueEntries) + for(MessageInstance entry : queueEntries) { ServerMessage message = entry.getMessage(); - BaseQueue queue = entry.getQueue(); + TransactionLogResource queue = entry.getOwningResource(); if(message.isPersistent() && queue.isDurable()) { @@ -152,7 +154,7 @@ public class AutoCommitTransaction implements ServerTransaction } - public void enqueue(BaseQueue queue, EnqueueableMessage message, Action postTransactionAction) + public void enqueue(TransactionLogResource queue, EnqueueableMessage message, Action postTransactionAction) { Transaction txn = null; try diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/DistributedTransaction.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/DistributedTransaction.java index d48b09d912..4a7c16a7cd 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/DistributedTransaction.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/DistributedTransaction.java @@ -22,10 +22,12 @@ package org.apache.qpid.server.txn; import org.apache.qpid.server.message.EnqueueableMessage; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.TransactionLogResource; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.transport.Xid; @@ -74,7 +76,7 @@ public class DistributedTransaction implements ServerTransaction } } - public void dequeue(BaseQueue queue, EnqueueableMessage message, Action postTransactionAction) + public void dequeue(TransactionLogResource queue, EnqueueableMessage message, Action postTransactionAction) { if(_branch != null) { @@ -87,13 +89,13 @@ public class DistributedTransaction implements ServerTransaction } } - public void dequeue(Collection messages, Action postTransactionAction) + public void dequeue(Collection messages, Action postTransactionAction) { if(_branch != null) { - for(QueueEntry entry : messages) + for(MessageInstance entry : messages) { - _branch.dequeue(entry.getQueue(), entry.getMessage()); + _branch.dequeue(entry.getOwningResource(), entry.getMessage()); } _branch.addPostTransactionAction(postTransactionAction); } @@ -103,7 +105,7 @@ public class DistributedTransaction implements ServerTransaction } } - public void enqueue(BaseQueue queue, EnqueueableMessage message, Action postTransactionAction) + public void enqueue(TransactionLogResource queue, EnqueueableMessage message, Action postTransactionAction) { if(_branch != null) { diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/DtxBranch.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/DtxBranch.java index 6b12862690..2505548ab8 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/DtxBranch.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/DtxBranch.java @@ -34,6 +34,7 @@ import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.Transaction; +import org.apache.qpid.server.store.TransactionLogResource; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.transport.Xid; @@ -335,7 +336,7 @@ public class DtxBranch { if(enqueue.isDurable()) { - _transaction.enqueueMessage(enqueue.getQueue(), enqueue.getMessage()); + _transaction.enqueueMessage(enqueue.getResource(), enqueue.getMessage()); } } @@ -344,7 +345,7 @@ public class DtxBranch { if(enqueue.isDurable()) { - _transaction.dequeueMessage(enqueue.getQueue(), enqueue.getMessage()); + _transaction.dequeueMessage(enqueue.getResource(), enqueue.getMessage()); } } } @@ -356,31 +357,31 @@ public class DtxBranch } - public void dequeue(BaseQueue queue, EnqueueableMessage message) + public void dequeue(TransactionLogResource resource, EnqueueableMessage message) { - _dequeueRecords.add(new Record(queue, message)); + _dequeueRecords.add(new Record(resource, message)); } - public void enqueue(BaseQueue queue, EnqueueableMessage message) + public void enqueue(TransactionLogResource queue, EnqueueableMessage message) { _enqueueRecords.add(new Record(queue, message)); } private static final class Record implements Transaction.Record { - private final BaseQueue _queue; + private final TransactionLogResource _resource; private final EnqueueableMessage _message; - public Record(BaseQueue queue, EnqueueableMessage message) + public Record(TransactionLogResource resource, EnqueueableMessage message) { - _queue = queue; + _resource = resource; _message = message; } - public BaseQueue getQueue() + public TransactionLogResource getResource() { - return _queue; + return _resource; } public EnqueueableMessage getMessage() @@ -390,7 +391,7 @@ public class DtxBranch public boolean isDurable() { - return _message.isPersistent() && _queue.isDurable(); + return _message.isPersistent() && _resource.isDurable(); } } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java index 81aabc6bd3..4b02d4f8ec 100755 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java @@ -21,7 +21,9 @@ package org.apache.qpid.server.txn; import org.apache.qpid.server.message.EnqueueableMessage; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.store.StoreFuture; +import org.apache.qpid.server.store.TransactionLogResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -91,7 +93,7 @@ public class LocalTransaction implements ServerTransaction _postTransactionActions.add(postTransactionAction); } - public void dequeue(BaseQueue queue, EnqueueableMessage message, Action postTransactionAction) + public void dequeue(TransactionLogResource queue, EnqueueableMessage message, Action postTransactionAction) { sync(); _postTransactionActions.add(postTransactionAction); @@ -118,7 +120,7 @@ public class LocalTransaction implements ServerTransaction } } - public void dequeue(Collection queueEntries, Action postTransactionAction) + public void dequeue(Collection queueEntries, Action postTransactionAction) { sync(); _postTransactionActions.add(postTransactionAction); @@ -126,10 +128,10 @@ public class LocalTransaction implements ServerTransaction try { - for(QueueEntry entry : queueEntries) + for(MessageInstance entry : queueEntries) { ServerMessage message = entry.getMessage(); - BaseQueue queue = entry.getQueue(); + TransactionLogResource queue = entry.getOwningResource(); if(message.isPersistent() && queue.isDurable()) { @@ -195,7 +197,7 @@ public class LocalTransaction implements ServerTransaction } } - public void enqueue(BaseQueue queue, EnqueueableMessage message, Action postTransactionAction) + public void enqueue(TransactionLogResource queue, EnqueueableMessage message, Action postTransactionAction) { sync(); _postTransactionActions.add(postTransactionAction); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java index 240ad154ba..cae5fa73bf 100755 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java @@ -24,8 +24,9 @@ import java.util.Collection; import java.util.List; import org.apache.qpid.server.message.EnqueueableMessage; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.queue.BaseQueue; -import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.store.TransactionLogResource; /** @@ -79,21 +80,21 @@ public interface ServerTransaction * * A store operation will result only for a persistent message on a durable queue. */ - void dequeue(BaseQueue queue, EnqueueableMessage message, Action postTransactionAction); + void dequeue(TransactionLogResource queue, EnqueueableMessage message, Action postTransactionAction); /** * Dequeue a message(s) from queue(s) registering a post transaction action. * * Store operations will result only for a persistent messages on durable queues. */ - void dequeue(Collection messages, Action postTransactionAction); + void dequeue(Collection messages, Action postTransactionAction); /** * Enqueue a message to a queue registering a post transaction action. * * A store operation will result only for a persistent message on a durable queue. */ - void enqueue(BaseQueue queue, EnqueueableMessage message, Action postTransactionAction); + void enqueue(TransactionLogResource queue, EnqueueableMessage message, Action postTransactionAction); /** * Enqueue a message(s) to queue(s) registering a post transaction action. diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/Action.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/Action.java new file mode 100644 index 0000000000..0d53b4d03b --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/Action.java @@ -0,0 +1,26 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.util; + +public interface Action +{ + void performAction(T object); +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/StateChangeListener.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/StateChangeListener.java new file mode 100644 index 0000000000..b5dc90cfb6 --- /dev/null +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/util/StateChangeListener.java @@ -0,0 +1,26 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.util; + +public interface StateChangeListener +{ + void stateChanged(T object, E oldState, E newState); +} diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java index 5859ce3c68..9a23e00f90 100644 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/AbstractVirtualHost.java @@ -47,8 +47,13 @@ import org.apache.qpid.server.exchange.ExchangeFactory; import org.apache.qpid.server.exchange.ExchangeRegistry; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.messages.VirtualHostMessages; +import org.apache.qpid.server.message.MessageDestination; +import org.apache.qpid.server.message.MessageNode; +import org.apache.qpid.server.message.MessageSource; import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.plugin.ExchangeType; +import org.apache.qpid.server.plugin.QpidServiceLoader; +import org.apache.qpid.server.plugin.SystemNodeCreator; import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.protocol.LinkRegistry; @@ -99,6 +104,7 @@ public abstract class AbstractVirtualHost implements VirtualHost, IConnectionReg private final DtxRegistry _dtxRegistry; private final AMQQueueFactory _queueFactory; + private final SystemNodeRegistry _systemNodeRegistry = new SystemNodeRegistry(); private volatile State _state = State.INITIALISING; @@ -107,6 +113,13 @@ public abstract class AbstractVirtualHost implements VirtualHost, IConnectionReg private final Map _linkRegistry = new HashMap(); private boolean _blocked; + private final Map _systemNodeDestinations = + Collections.synchronizedMap(new HashMap()); + + private final Map _systemNodeSources = + Collections.synchronizedMap(new HashMap()); + + public AbstractVirtualHost(VirtualHostRegistry virtualHostRegistry, StatisticsGatherer brokerStatisticsGatherer, SecurityManager parentSecurityManager, @@ -149,6 +162,8 @@ public abstract class AbstractVirtualHost implements VirtualHost, IConnectionReg _exchangeRegistry = new DefaultExchangeRegistry(this, _queueRegistry); + registerSystemNodes(); + initialiseStatistics(); initialiseStorage(hostConfig, virtualHost); @@ -157,6 +172,16 @@ public abstract class AbstractVirtualHost implements VirtualHost, IConnectionReg getMessageStore().addEventListener(this, Event.PERSISTENT_MESSAGE_SIZE_UNDERFULL); } + private void registerSystemNodes() + { + QpidServiceLoader qpidServiceLoader = new QpidServiceLoader(); + Iterable factories = qpidServiceLoader.instancesOf(SystemNodeCreator.class); + for(SystemNodeCreator creator : factories) + { + creator.register(_systemNodeRegistry); + } + } + abstract protected void initialiseStorage(VirtualHostConfiguration hostConfig, org.apache.qpid.server.model.VirtualHost virtualHost) throws Exception; @@ -440,6 +465,13 @@ public abstract class AbstractVirtualHost implements VirtualHost, IConnectionReg return _queueRegistry.getQueue(name); } + @Override + public MessageSource getMessageSource(final String name) + { + MessageSource systemSource = _systemNodeSources.get(name); + return systemSource == null ? getQueue(name) : systemSource; + } + @Override public AMQQueue getQueue(UUID id) { @@ -524,6 +556,14 @@ public abstract class AbstractVirtualHost implements VirtualHost, IConnectionReg } + + @Override + public MessageDestination getMessageDestination(final String name) + { + MessageDestination destination = _systemNodeDestinations.get(name); + return destination == null ? getExchange(name) : destination; + } + @Override public Exchange getExchange(String name) { @@ -927,4 +967,39 @@ public abstract class AbstractVirtualHost implements VirtualHost, IConnectionReg } } } + + private class SystemNodeRegistry implements SystemNodeCreator.SystemNodeRegistry + { + @Override + public void registerSystemNode(final MessageNode node) + { + if(node instanceof MessageDestination) + { + _systemNodeDestinations.put(node.getName(), (MessageDestination) node); + } + if(node instanceof MessageSource) + { + _systemNodeSources.put(node.getName(), (MessageSource)node); + } + } + + @Override + public void removeSystemNode(final MessageNode node) + { + if(node instanceof MessageDestination) + { + _systemNodeDestinations.remove(node.getName()); + } + if(node instanceof MessageSource) + { + _systemNodeSources.remove(node.getName()); + } + } + + @Override + public VirtualHost getVirtualHost() + { + return AbstractVirtualHost.this; + } + } } diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java index 2ebbedccd4..7034311d84 100755 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java @@ -30,6 +30,9 @@ import org.apache.qpid.common.Closeable; import org.apache.qpid.server.configuration.VirtualHostConfiguration; import org.apache.qpid.server.connection.IConnectionRegistry; import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.message.MessageDestination; +import org.apache.qpid.server.message.MessageNode; +import org.apache.qpid.server.message.MessageSource; import org.apache.qpid.server.plugin.ExchangeType; import org.apache.qpid.server.protocol.LinkRegistry; import org.apache.qpid.server.queue.AMQQueue; @@ -49,6 +52,7 @@ public interface VirtualHost extends DurableConfigurationStore.Source, Closeable String getName(); AMQQueue getQueue(String name); + MessageSource getMessageSource(String name); AMQQueue getQueue(UUID id); @@ -76,6 +80,8 @@ public interface VirtualHost extends DurableConfigurationStore.Source, Closeable void removeExchange(Exchange exchange, boolean force) throws AMQException; + MessageDestination getMessageDestination(String name); + Exchange getExchange(String name); Exchange getExchange(UUID id); diff --git a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java index b7d3cf872b..6e36cdfa94 100755 --- a/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java +++ b/qpid/java/broker-core/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java @@ -119,7 +119,7 @@ public class VirtualHostConfigRecoveryHandler implements } for(Transaction.Record record : enqueues) { - final AMQQueue queue = _virtualHost.getQueue(record.getQueue().getId()); + final AMQQueue queue = _virtualHost.getQueue(record.getResource().getId()); if(queue != null) { final long messageId = record.getMessage().getMessageNumber(); @@ -141,7 +141,7 @@ public class VirtualHostConfigRecoveryHandler implements try { - queue.enqueue(message, true, null); + queue.enqueue(message, null); ref.release(); } catch (AMQException e) @@ -173,13 +173,13 @@ public class VirtualHostConfigRecoveryHandler implements StringBuilder xidString = xidAsString(id); CurrentActor.get().message(_logSubject, TransactionLogMessages.XA_INCOMPLETE_QUEUE(xidString.toString(), - record.getQueue().getId().toString())); + record.getResource().getId().toString())); } } for(Transaction.Record record : dequeues) { - final AMQQueue queue = _virtualHost.getQueue(record.getQueue().getId()); + final AMQQueue queue = _virtualHost.getQueue(record.getResource().getId()); if(queue != null) { final long messageId = record.getMessage().getMessageNumber(); @@ -223,7 +223,7 @@ public class VirtualHostConfigRecoveryHandler implements StringBuilder xidString = xidAsString(id); CurrentActor.get().message(_logSubject, TransactionLogMessages.XA_INCOMPLETE_QUEUE(xidString.toString(), - record.getQueue().getId().toString())); + record.getResource().getId().toString())); } } @@ -292,7 +292,7 @@ public class VirtualHostConfigRecoveryHandler implements count = 0; } - queue.enqueue(message); + queue.enqueue(message,null); _queueRecoveries.put(queueName, ++count); } @@ -311,11 +311,23 @@ public class VirtualHostConfigRecoveryHandler implements TransactionLogResource mockQueue = new TransactionLogResource() { + @Override + public String getName() + { + return "<>"; + } + @Override public UUID getId() { return queueId; } + + @Override + public boolean isDurable() + { + return false; + } }; txn.dequeueMessage(mockQueue, new DummyMessage(messageId)); txn.commitTranAsync(); diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/consumer/MockConsumer.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/consumer/MockConsumer.java new file mode 100644 index 0000000000..a9b99503ec --- /dev/null +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/consumer/MockConsumer.java @@ -0,0 +1,518 @@ +/* +* +* 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.consumer; + +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.filter.FilterManager; +import org.apache.qpid.server.filter.Filterable; +import org.apache.qpid.server.filter.MessageFilter; +import org.apache.qpid.server.filter.SimpleFilterManager; +import org.apache.qpid.server.logging.LogSubject; +import org.apache.qpid.server.message.MessageInstance; +import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.model.Port; +import org.apache.qpid.server.model.Transport; +import org.apache.qpid.server.protocol.AMQConnectionModel; +import org.apache.qpid.server.protocol.AMQSessionModel; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.stats.StatisticsCounter; +import org.apache.qpid.server.util.StateChangeListener; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +public class MockConsumer implements ConsumerTarget +{ + + private final List _messageIds; + private boolean _closed = false; + private String tag = "mocktag"; + private AMQQueue queue = null; + private StateChangeListener _listener = null; + private State _state = State.ACTIVE; + private ArrayList messages = new ArrayList(); + private final Lock _stateChangeLock = new ReentrantLock(); + + private boolean _isActive = true; + + public MockConsumer() + { + _messageIds = null; + } + + public MockConsumer(List messageIds) + { + _messageIds = messageIds; + } + + public boolean close() + { + _closed = true; + if (_listener != null) + { + _listener.stateChanged(this, _state, State.CLOSED); + } + _state = State.CLOSED; + return true; + } + + public String getName() + { + return tag; + } + + public FilterManager getFilters() + { + if(_messageIds != null) + { + SimpleFilterManager filters = new SimpleFilterManager(); + filters.add(new MessageFilter() + { + @Override + public boolean matches(final Filterable message) + { + final String messageId = message.getMessageHeader().getMessageId(); + return _messageIds.contains(messageId); + } + }); + return filters; + } + else + { + return null; + } + } + + public long getUnacknowledgedBytes() + { + return 0; // TODO - Implement + } + + public long getUnacknowledgedMessages() + { + return 0; // TODO - Implement + } + + public AMQQueue getQueue() + { + return queue; + } + + public AMQSessionModel getSessionModel() + { + return new MockSessionModel(); + } + + public boolean isActive() + { + return _isActive ; + } + + + + public boolean isClosed() + { + return _closed; + } + + + public boolean isSuspended() + { + return false; + } + + public void queueDeleted() + { + } + + public void restoreCredit(ServerMessage message) + { + } + + public void send(MessageInstance entry, boolean batch) throws AMQException + { + if (messages.contains(entry)) + { + entry.setRedelivered(); + } + messages.add(entry); + } + + public void flushBatched() + { + + } + + public State getState() + { + return _state; + } + + @Override + public void consumerAdded(final Consumer sub) + { + } + + @Override + public void consumerRemoved(final Consumer sub) + { + + } + + public void setState(State state) + { + State oldState = _state; + _state = state; + if(_listener != null) + { + _listener.stateChanged(this, oldState, state); + } + } + + @Override + public void setStateListener(final StateChangeListener listener) + { + _listener = listener; + } + + public ArrayList getMessages() + { + return messages; + } + + + public void queueEmpty() throws AMQException + { + } + + @Override + public boolean allocateCredit(final ServerMessage msg) + { + return true; + } + + public void setActive(final boolean isActive) + { + _isActive = isActive; + } + + private static class MockSessionModel implements AMQSessionModel + { + private final UUID _id = UUID.randomUUID(); + + @Override + public UUID getId() + { + return _id; + } + + @Override + public AMQConnectionModel getConnectionModel() + { + return new MockConnectionModel(); + } + + @Override + public String getClientID() + { + return null; + } + + @Override + public void close() throws AMQException + { + } + + @Override + public LogSubject getLogSubject() + { + return null; + } + + @Override + public void checkTransactionStatus(long openWarn, long openClose, + long idleWarn, long idleClose) throws AMQException + { + } + + @Override + public void block(AMQQueue queue) + { + } + + @Override + public void unblock(AMQQueue queue) + { + } + + @Override + public void block() + { + } + + @Override + public void unblock() + { + } + + @Override + public boolean getBlocking() + { + return false; + } + + @Override + public Object getConnectionReference() + { + return this; + } + + @Override + public int getUnacknowledgedMessageCount() + { + return 0; + } + + @Override + public Long getTxnCount() + { + return null; + } + + @Override + public Long getTxnStart() + { + return null; + } + + @Override + public Long getTxnCommits() + { + return null; + } + + @Override + public Long getTxnRejects() + { + return null; + } + + @Override + public int getChannelId() + { + return 0; + } + + @Override + public int getConsumerCount() + { + return 0; + } + + @Override + public int compareTo(AMQSessionModel o) + { + return getId().compareTo(o.getId()); + } + + @Override + public void close(AMQConstant cause, String message) throws AMQException + { + } + } + + private static class MockConnectionModel implements AMQConnectionModel + { + @Override + public void initialiseStatistics() + { + } + + @Override + public void registerMessageReceived(long messageSize, long timestamp) + { + } + + @Override + public void registerMessageDelivered(long messageSize) + { + } + + @Override + public StatisticsCounter getMessageDeliveryStatistics() + { + return null; + } + + @Override + public StatisticsCounter getMessageReceiptStatistics() + { + return null; + } + + @Override + public StatisticsCounter getDataDeliveryStatistics() + { + return null; + } + + @Override + public StatisticsCounter getDataReceiptStatistics() + { + return null; + } + + @Override + public void resetStatistics() + { + + } + + @Override + public void close(AMQConstant cause, String message) + throws AMQException + { + } + + @Override + public void closeSession(AMQSessionModel session, AMQConstant cause, + String message) throws AMQException + { + } + + @Override + public long getConnectionId() + { + return 0; + } + + @Override + public List getSessionModels() + { + return null; + } + + @Override + public void block() + { + } + + @Override + public void unblock() + { + } + + @Override + public LogSubject getLogSubject() + { + return null; + } + + @Override + public String getUserName() + { + return null; + } + + @Override + public boolean isSessionNameUnique(byte[] name) + { + return false; + } + + @Override + public String getRemoteAddressString() + { + return "remoteAddress:1234"; + } + + @Override + public String getClientId() + { + return null; + } + + @Override + public String getClientVersion() + { + return null; + } + + @Override + public String getClientProduct() + { + return null; + } + + @Override + public String getPrincipalAsString() + { + return null; + } + + @Override + public long getSessionCountLimit() + { + return 0; + } + + @Override + public long getLastIoTime() + { + return 0; + } + + @Override + public Port getPort() + { + return null; + } + + @Override + public Transport getTransport() + { + return null; + } + + @Override + public void stop() + { + } + + @Override + public boolean isStopped() + { + return false; + } + + @Override + public String getVirtualHostName() + { + return null; + } + } +} diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java index 764549626a..ea9d0ac693 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java @@ -25,6 +25,7 @@ import junit.framework.Assert; import org.apache.qpid.AMQException; import org.apache.qpid.server.binding.Binding; +import org.apache.qpid.server.consumer.Consumer; import org.apache.qpid.server.message.InstanceProperties; import org.apache.qpid.server.message.MessageReference; import org.apache.qpid.server.message.ServerMessage; @@ -85,7 +86,7 @@ public class TopicExchangeTest extends QpidTestCase public void testDirectMatch() throws AMQException { - AMQQueue queue = _vhost.createQueue(UUIDGenerator.generateRandomUUID(), "ab", false, null, false, false, + AMQQueue queue = _vhost.createQueue(UUIDGenerator.generateRandomUUID(), "ab", false, null, false, false, false, null); _exchange.registerQueue(new Binding(null, "a.b",queue, _exchange, null)); @@ -108,7 +109,7 @@ public class TopicExchangeTest extends QpidTestCase public void testStarMatch() throws AMQException { - AMQQueue queue = _vhost.createQueue(UUIDGenerator.generateRandomUUID(), "a*", false, null, false, false, false, null); + AMQQueue queue = _vhost.createQueue(UUIDGenerator.generateRandomUUID(), "a*", false, null, false, false, false, null); _exchange.registerQueue(new Binding(null, "a.*",queue, _exchange, null)); @@ -139,7 +140,7 @@ public class TopicExchangeTest extends QpidTestCase public void testHashMatch() throws AMQException { - AMQQueue queue = _vhost.createQueue(UUIDGenerator.generateRandomUUID(), "a#", false, null, false, false, false, null); + AMQQueue queue = _vhost.createQueue(UUIDGenerator.generateRandomUUID(), "a#", false, null, false, false, false, null); _exchange.registerQueue(new Binding(null, "a.#",queue, _exchange, null)); @@ -190,7 +191,7 @@ public class TopicExchangeTest extends QpidTestCase public void testMidHash() throws AMQException { - AMQQueue queue = _vhost.createQueue(UUIDGenerator.generateRandomUUID(), "a", false, null, false, false, + AMQQueue queue = _vhost.createQueue(UUIDGenerator.generateRandomUUID(), "a", false, null, false, false, false, null); _exchange.registerQueue(new Binding(null, "a.*.#.b",queue, _exchange, null)); @@ -216,7 +217,7 @@ public class TopicExchangeTest extends QpidTestCase public void testMatchAfterHash() throws AMQException { - AMQQueue queue = _vhost.createQueue(UUIDGenerator.generateRandomUUID(), "a#", false, null, false, false, + AMQQueue queue = _vhost.createQueue(UUIDGenerator.generateRandomUUID(), "a#", false, null, false, false, false, null); _exchange.registerQueue(new Binding(null, "a.*.#.b.c",queue, _exchange, null)); @@ -255,7 +256,7 @@ public class TopicExchangeTest extends QpidTestCase public void testHashAfterHash() throws AMQException { - AMQQueue queue = _vhost.createQueue(UUIDGenerator.generateRandomUUID(), "a#", false, null, false, false, + AMQQueue queue = _vhost.createQueue(UUIDGenerator.generateRandomUUID(), "a#", false, null, false, false, false, null); _exchange.registerQueue(new Binding(null, "a.*.#.b.c.#.d",queue, _exchange, null)); @@ -277,7 +278,7 @@ public class TopicExchangeTest extends QpidTestCase public void testHashHash() throws AMQException { - AMQQueue queue = _vhost.createQueue(UUIDGenerator.generateRandomUUID(), "a#", false, null, false, false, + AMQQueue queue = _vhost.createQueue(UUIDGenerator.generateRandomUUID(), "a#", false, null, false, false, false, null); _exchange.registerQueue(new Binding(null, "a.#.*.#.d",queue, _exchange, null)); @@ -321,7 +322,7 @@ public class TopicExchangeTest extends QpidTestCase when(message.getMessageNumber()).thenReturn(messageNumber); for(BaseQueue q : queues) { - q.enqueue(message); + q.enqueue(message, null); } return queues.size(); diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/actors/SubscriptionActorTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/actors/SubscriptionActorTest.java deleted file mode 100644 index a0a2a7b648..0000000000 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/actors/SubscriptionActorTest.java +++ /dev/null @@ -1,86 +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.logging.actors; - -import org.apache.qpid.server.subscription.MockSubscription; -import org.apache.qpid.server.util.BrokerTestHelper; - -import java.util.List; - -/** - * Test : AMQPConnectionActorTest - * Validate the AMQPConnectionActor class. - * - * The test creates a new AMQPActor and then logs a message using it. - * - * The test then verifies that the logged message was the only one created and - * that the message contains the required message. - */ -public class SubscriptionActorTest extends BaseConnectionActorTestCase -{ - - @Override - public void setUp() throws Exception - { - super.setUp(); - - MockSubscription mockSubscription = new MockSubscription(); - - mockSubscription.setQueue(BrokerTestHelper.createQueue(getName(), getVirtualHost()), false); - - setAmqpActor(new SubscriptionActor(getRootLogger(), mockSubscription)); - } - - /** - * Test the AMQPActor logging as a Subscription logger. - * - * The test sends a message then verifies that it entered the logs. - * - * The log message should be fully replaced (no '{n}' values) and should - * contain subscription identification. - */ - public void testSubscription() - { - final String message = sendTestLogMessage(getAmqpActor()); - - List logs = getRawLogger().getLogMessages(); - - assertEquals("Message log size not as expected.", 1, logs.size()); - - // Verify that the logged message is present in the output - assertTrue("Message was not found in log message", - logs.get(0).toString().contains(message)); - - // Verify that all the values were presented to the MessageFormatter - // so we will not end up with '{n}' entries in the log. - assertFalse("Verify that the string does not contain any '{'.", - logs.get(0).toString().contains("{")); - - // Verify that the message has the correct type - assertTrue("Message contains the [sub: prefix", - logs.get(0).toString().contains("[sub:")); - - // Verify that the logged message does not contains the 'ch:' marker - assertFalse("Message was logged with a channel identifier." + logs.get(0), - logs.get(0).toString().contains("/ch:")); - } - -} diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/messages/ConsumerMessagesTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/messages/ConsumerMessagesTest.java new file mode 100644 index 0000000000..52a53e8d38 --- /dev/null +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/messages/ConsumerMessagesTest.java @@ -0,0 +1,86 @@ +/* + * + * 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.logging.messages; + +import java.util.List; + +/** + * Test SUB Log Messages + */ +public class ConsumerMessagesTest extends AbstractTestMessages +{ + public void testSubscriptionCreateALL() + { + String arguments = "arguments"; + + _logMessage = SubscriptionMessages.CREATE(arguments, true, true); + List log = performLog(); + + String[] expected = {"Create :", "Durable", "Arguments :", arguments}; + + validateLogMessage(log, "SUB-1001", expected); + } + + public void testSubscriptionCreateDurable() + { + _logMessage = SubscriptionMessages.CREATE(null, true, false); + List log = performLog(); + + String[] expected = {"Create :", "Durable"}; + + validateLogMessage(log, "SUB-1001", expected); + } + + public void testSubscriptionCreateArguments() + { + String arguments = "arguments"; + + _logMessage = SubscriptionMessages.CREATE(arguments, false, true); + List log = performLog(); + + String[] expected = {"Create :","Arguments :", arguments}; + + validateLogMessage(log, "SUB-1001", expected); + } + + + public void testSubscriptionClose() + { + _logMessage = SubscriptionMessages.CLOSE(); + List log = performLog(); + + String[] expected = {"Close"}; + + validateLogMessage(log, "SUB-1002", expected); + } + + public void testSubscriptionState() + { + String state = "ACTIVE"; + + _logMessage = SubscriptionMessages.STATE(state); + List log = performLog(); + + String[] expected = {"State :", state}; + + validateLogMessage(log, "SUB-1003", expected); + } +} diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/messages/SubscriptionMessagesTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/messages/SubscriptionMessagesTest.java deleted file mode 100644 index b2bc351f8f..0000000000 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/messages/SubscriptionMessagesTest.java +++ /dev/null @@ -1,86 +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.logging.messages; - -import java.util.List; - -/** - * Test SUB Log Messages - */ -public class SubscriptionMessagesTest extends AbstractTestMessages -{ - public void testSubscriptionCreateALL() - { - String arguments = "arguments"; - - _logMessage = SubscriptionMessages.CREATE(arguments, true, true); - List log = performLog(); - - String[] expected = {"Create :", "Durable", "Arguments :", arguments}; - - validateLogMessage(log, "SUB-1001", expected); - } - - public void testSubscriptionCreateDurable() - { - _logMessage = SubscriptionMessages.CREATE(null, true, false); - List log = performLog(); - - String[] expected = {"Create :", "Durable"}; - - validateLogMessage(log, "SUB-1001", expected); - } - - public void testSubscriptionCreateArguments() - { - String arguments = "arguments"; - - _logMessage = SubscriptionMessages.CREATE(arguments, false, true); - List log = performLog(); - - String[] expected = {"Create :","Arguments :", arguments}; - - validateLogMessage(log, "SUB-1001", expected); - } - - - public void testSubscriptionClose() - { - _logMessage = SubscriptionMessages.CLOSE(); - List log = performLog(); - - String[] expected = {"Close"}; - - validateLogMessage(log, "SUB-1002", expected); - } - - public void testSubscriptionState() - { - String state = "ACTIVE"; - - _logMessage = SubscriptionMessages.STATE(state); - List log = performLog(); - - String[] expected = {"State :", state}; - - validateLogMessage(log, "SUB-1003", expected); - } -} diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/messages/VirtualHostMessagesTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/messages/VirtualHostMessagesTest.java index 17d68ef7c3..c1068b4a0b 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/messages/VirtualHostMessagesTest.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/messages/VirtualHostMessagesTest.java @@ -38,7 +38,7 @@ public class VirtualHostMessagesTest extends AbstractTestMessages validateLogMessage(log, "VHT-1001", expected); } - public void testSubscriptionClosed() + public void testVirtualhostClosed() { _logMessage = VirtualHostMessages.CLOSED(); List log = performLog(); diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/subjects/SubscriptionLogSubjectTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/subjects/SubscriptionLogSubjectTest.java deleted file mode 100644 index 3afba28cd6..0000000000 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/logging/subjects/SubscriptionLogSubjectTest.java +++ /dev/null @@ -1,105 +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.logging.subjects; - -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.MockAMQQueue; -import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.util.BrokerTestHelper; -import org.apache.qpid.server.virtualhost.VirtualHost; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -/** - * Validate SubscriptionLogSubjects are logged as expected - */ -public class SubscriptionLogSubjectTest extends AbstractTestLogSubject -{ - - private static final long SUBSCRIPTION_ID = 1; - private AMQQueue _queue; - private VirtualHost _testVhost; - private Subscription _subscription; - - @Override - public void setUp() throws Exception - { - super.setUp(); - - _testVhost = BrokerTestHelper.createVirtualHost("test"); - - _queue = new MockAMQQueue("SubscriptionLogSubjectTest"); - ((MockAMQQueue) _queue).setVirtualHost(_testVhost); - - _subscription = mock(Subscription.class); - when(_subscription.getQueue()).thenReturn(_queue); - when(_subscription.getSubscriptionID()).thenReturn(SUBSCRIPTION_ID); - - _subject = new SubscriptionLogSubject(_subscription); - } - - @Override - public void tearDown() throws Exception - { - if (_testVhost != null) - { - _testVhost.close(); - } - super.tearDown(); - } - - /** - * Validate that the logged Subject message is as expected: - * MESSAGE [Blank][sub:0(vh(/test)/qu(SubscriptionLogSubjectTest))] - * - * @param message the message whose format needs validation - */ - @Override - protected void validateLogStatement(String message) - { - String subscriptionSlice = getSlice("sub:" - + _subscription.getSubscriptionID(), - message); - - assertNotNull("Unable to locate subscription 'sub:" + - _subscription.getSubscriptionID() + "'"); - - - - // Pull out the qu(..) section from the subscription message - // Split it into three parts - // MESSAGE [Blank][sub:0(vh(/ - // test)/ - // qu(SubscriptionLogSubjectTest))] - // Take the last bit and drop off the extra )] - String[] parts = message.split("/"); - assertEquals("Message part count wrong", 3, parts.length); - String subscription = parts[2].substring(0, parts[2].indexOf(")") + 1); - - // Adding the ')' is a bit ugly but SubscriptionLogSubject is the only - // Subject that nests () and so the simple parser of checking for the - // next ')' falls down. - verifyVirtualHost(subscriptionSlice+ ")", _queue.getVirtualHost()); - - verifyQueue(subscription, _queue); - } -} diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java index a468fa072b..ced00dc578 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/AMQPriorityQueueTest.java @@ -25,10 +25,14 @@ import junit.framework.AssertionFailedError; import org.apache.qpid.AMQException; import org.apache.qpid.server.message.AMQMessageHeader; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.message.ServerMessage; import java.util.ArrayList; +import java.util.EnumSet; + import org.apache.qpid.server.model.Queue; +import org.apache.qpid.server.consumer.Consumer; import static org.mockito.Mockito.when; @@ -47,25 +51,25 @@ public class AMQPriorityQueueTest extends SimpleAMQQueueTest // Enqueue messages in order SimpleAMQQueue queue = getQueue(); - queue.enqueue(createMessage(1L, (byte) 10)); - queue.enqueue(createMessage(2L, (byte) 4)); - queue.enqueue(createMessage(3L, (byte) 0)); + queue.enqueue(createMessage(1L, (byte) 10), null); + queue.enqueue(createMessage(2L, (byte) 4), null); + queue.enqueue(createMessage(3L, (byte) 0), null); // Enqueue messages in reverse order - queue.enqueue(createMessage(4L, (byte) 0)); - queue.enqueue(createMessage(5L, (byte) 4)); - queue.enqueue(createMessage(6L, (byte) 10)); + queue.enqueue(createMessage(4L, (byte) 0), null); + queue.enqueue(createMessage(5L, (byte) 4), null); + queue.enqueue(createMessage(6L, (byte) 10), null); // Enqueue messages out of order - queue.enqueue(createMessage(7L, (byte) 4)); - queue.enqueue(createMessage(8L, (byte) 10)); - queue.enqueue(createMessage(9L, (byte) 0)); + queue.enqueue(createMessage(7L, (byte) 4), null); + queue.enqueue(createMessage(8L, (byte) 10), null); + queue.enqueue(createMessage(9L, (byte) 0), null); // Register subscriber - queue.registerSubscription(getSubscription(), false); + queue.addConsumer(getConsumer(), null, null, "test", EnumSet.noneOf(Consumer.Option.class)); Thread.sleep(150); - ArrayList msgs = getSubscription().getMessages(); + ArrayList msgs = getConsumer().getMessages(); try { assertEquals(1L, msgs.get(0).getMessage().getMessageNumber()); @@ -84,7 +88,7 @@ public class AMQPriorityQueueTest extends SimpleAMQQueueTest { // Show message order on failure. int index = 1; - for (QueueEntry qe : msgs) + for (MessageInstance qe : msgs) { System.err.println(index + ":" + qe.getMessage().getMessageNumber()); index++; diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/ConsumerListTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/ConsumerListTest.java new file mode 100644 index 0000000000..35508bb2c4 --- /dev/null +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/ConsumerListTest.java @@ -0,0 +1,445 @@ +/* + * + * 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.protocol.AMQSessionModel; +import org.apache.qpid.server.consumer.Consumer; +import org.apache.qpid.server.consumer.ConsumerTarget; +import org.apache.qpid.test.utils.QpidTestCase; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ConsumerListTest extends QpidTestCase +{ + private QueueConsumerList _subList; + private QueueConsumer _sub1; + private QueueConsumer _sub2; + private QueueConsumer _sub3; + private QueueConsumerList.ConsumerNode _node; + + protected void setUp() + { + _subList = new QueueConsumerList(); + + _sub1 = newMockConsumer(); + _sub2 = newMockConsumer(); + _sub3 = newMockConsumer(); + + _subList.add(_sub1); + _subList.add(_sub2); + _subList.add(_sub3); + + _node = _subList.getHead(); + } + + + private QueueConsumer newMockConsumer() + { + ConsumerTarget target = mock(ConsumerTarget.class); + when(target.getSessionModel()).thenReturn(mock(AMQSessionModel.class)); + return new QueueConsumer(null,null,true,true,"sub",false,target); + } + + /** + * Test that if the first (non-head) node in the list is deleted (but is still present), + * it is not returned when searching through the list for the next viable node, and the + * subsequent viable node is returned instead. + */ + public void testFindNextSkipsFirstDeletedNode() + { + assertTrue("Deleting consumer node should have succeeded", + getNodeForConsumer(_subList, _sub1).delete()); + + assertNotNull("Returned node should not be null", _node = _node.findNext()); + assertEquals("Should have returned node for 2nd consumer", _sub2, _node.getConsumer()); + + assertNotNull("Returned node should not be null", _node = _node.findNext()); + assertEquals("Should have returned node for 3rd consumer", _sub3, _node.getConsumer()); + } + + /** + * Test that if a central node in the list is deleted (but is still present), + * it is not returned when searching through the list for the next viable node, + * and the subsequent viable node is returned instead. + */ + public void testFindNextSkipsCentralDeletedNode() + { + assertNotNull("Returned node should not be null", _node = _node.findNext()); + + assertTrue("Deleting consumer node should have succeeded", + getNodeForConsumer(_subList, _sub2).delete()); + + assertNotNull("Returned node should not be null", _node = _node.findNext()); + assertEquals("Should have returned node for 3rd consumer", _sub3, _node.getConsumer()); + } + + /** + * Test that if the last node in the list is deleted (but is still present), + * it is not returned when searching through the list for the next viable node, + * and null is returned instead. + */ + public void testFindNextSkipsLastDeletedNode() + { + assertNotNull("Returned node should not be null", _node = _node.findNext()); + assertEquals("Should have returned node for 1st consumer", _sub1, _node.getConsumer()); + + assertNotNull("Returned node should not be null", _node = _node.findNext()); + assertEquals("Should have returned node for 2nd consumer", _sub2, _node.getConsumer()); + + assertTrue("Deleting consumer node should have succeeded", + getNodeForConsumer(_subList, _sub3).delete()); + + assertNull("Returned node should be null", _node = _node.findNext()); + } + + /** + * Test that if multiple nodes in the list are deleted (but still present), they + * are not returned when searching through the list for the next viable node, + * and the subsequent viable node is returned instead. + */ + public void testFindNextSkipsMultipleDeletedNode() + { + assertTrue("Deleting consumer node should have succeeded", + getNodeForConsumer(_subList, _sub1).delete()); + assertTrue("Deleting consumer node should have succeeded", + getNodeForConsumer(_subList, _sub2).delete()); + + assertNotNull("Returned node should not be null", _node = _node.findNext()); + assertEquals("Should have returned node for 3rd consumer", _sub3, _node.getConsumer()); + } + + /** + * Test that if a node in the list is marked 'deleted' it is still present in the list + * until actually removed. counter-test to verify above testing of getNext() method. + */ + public void testDeletedNodeStillPresent() + { + assertTrue("Deleting consumer node should have succeeded", + getNodeForConsumer(_subList, _sub1).delete()); + + assertNotNull("Node marked deleted should still be present", getNodeForConsumer(_subList, _sub1)); + assertEquals("All 3 nodes are still expected to be present", 3, countNodes(_subList)); + } + + /** + * Traverses the list nodes in a non-mutating fashion, returning the first node which matches the given + * Consumer, or null if none is found. + */ + private QueueConsumerList.ConsumerNode getNodeForConsumer(final QueueConsumerList list, final Consumer sub) + { + QueueConsumerList.ConsumerNode node = list.getHead(); + while (node != null && node.getConsumer() != sub) + { + node = node.nextNode(); + } + + return node; + } + + /** + * Counts the number of (non-head) nodes in the list. + */ + private int countNodes(final QueueConsumerList list) + { + QueueConsumerList.ConsumerNode node = list.getHead(); + int count; + for(count = -1; node != null; count++) + { + node = node.nextNode(); + } + + return count; + } + + /** + * Tests that the head is returned as expected, and isn't the node for the first consumer. + */ + public void testGetHead() + { + assertNotNull("List head should be non null", _node); + assertNotSame("Head should not be node for first consumer", + _node, getNodeForConsumer(_subList, _sub1)); + } + + /** + * Tests that the size is returned correctly in the face of additions and removals. + */ + public void testGetSize() + { + QueueConsumerList subList = new QueueConsumerList(); + + assertEquals("Unexpected size result", 0, subList.size()); + + QueueConsumer sub1 = newMockConsumer(); + QueueConsumer sub2 = newMockConsumer(); + QueueConsumer sub3 = newMockConsumer(); + + subList.add(sub1); + assertEquals("Unexpected size result", 1, subList.size()); + + subList.add(sub2); + assertEquals("Unexpected size result", 2, subList.size()); + + subList.add(sub3); + assertEquals("Unexpected size result", 3, subList.size()); + + assertTrue("Removing consumer from list should have succeeded", subList.remove(sub1)); + assertEquals("Unexpected size result", 2, subList.size()); + + assertTrue("Removing consumer from list should have succeeded", subList.remove(sub2)); + assertEquals("Unexpected size result", 1, subList.size()); + + assertTrue("Removing consumer from list should have succeeded", subList.remove(sub3)); + assertEquals("Unexpected size result", 0, subList.size()); + } + + /** + * Test that if the first (non-head) node in the list is removed it is no longer + * present in the node structure of the list at all. + */ + public void testRemoveFirstNode() + { + assertNotNull("Should have been a node present for the consumer", getNodeForConsumer(_subList, _sub1)); + assertTrue("Removing consumer node should have succeeded", _subList.remove(_sub1)); + assertNull("Should not have been a node present for the removed consumer", + getNodeForConsumer(_subList, _sub1)); + assertEquals("Unexpected number of nodes", 2, countNodes(_subList)); + assertNotNull("Should have been a node present for the consumer", getNodeForConsumer(_subList, _sub2)); + assertNotNull("Should have been a node present for the consumer", getNodeForConsumer(_subList, _sub3)); + } + + /** + * Test that if a central node in the list is removed it is no longer + * present in the node structure of the list at all. + */ + public void testRemoveCentralNode() + { + assertNotNull("Should have been a node present for the consumer", getNodeForConsumer(_subList, _sub2)); + assertTrue("Removing consumer node should have succeeded", _subList.remove(_sub2)); + assertNull("Should not have been a node present for the removed consumer", + getNodeForConsumer(_subList, _sub2)); + assertEquals("Unexpected number of nodes", 2, countNodes(_subList)); + assertNotNull("Should have been a node present for the consumer", getNodeForConsumer(_subList, _sub1)); + assertNotNull("Should have been a node present for the consumer", getNodeForConsumer(_subList, _sub3)); + } + + /** + * Test that if the consumer contained in the last node of the list is removed + * it is no longer present in the node structure of the list at all. However, + * as the last node in the structure can't actually be removed a dummy will instead + * be present. + */ + public void testRemoveLastNode() + { + assertNotNull("Should have been a node present for the consumer", getNodeForConsumer(_subList, _sub3)); + assertTrue("Removing consumer node should have succeeded", _subList.remove(_sub3)); + assertNull("Should not have been a node present for the removed consumer", + getNodeForConsumer(_subList, _sub3)); + + //We actually expect 3 nodes to remain this time, because the last node cant be removed for thread safety reasons, + //however a dummy final node can be used as substitute to allow removal of the consumer node. + assertEquals("Unexpected number of nodes", 2 + 1, countNodes(_subList)); + assertNotNull("Should have been a node present for the consumer", getNodeForConsumer(_subList, _sub1)); + assertNotNull("Should have been a node present for the consumer", getNodeForConsumer(_subList, _sub2)); + } + + /** + * Test that if the consumer not contained in the list is requested to be removed + * that the removal fails + */ + public void testRemoveNonexistentNode() + { + QueueConsumer sub4 = newMockConsumer(); + assertNull("Should not have been a node present for the consumer", getNodeForConsumer(_subList, sub4)); + assertFalse("Removing consumer node should not have succeeded", _subList.remove(sub4)); + assertEquals("Unexpected number of nodes", 3, countNodes(_subList)); + } + + /** + * Test that if a consumer node which occurs later in the main list than the marked node is + * removed from the list after the marked node is also removed, then the marker node doesn't + * serve to retain the subsequent nodes in the list structure (and thus memory) despite their + * removal. + */ + public void testDeletedMarkedNodeDoesntLeakSubsequentlyDeletedNodes() + { + //get the nodes out the list for the 1st and 3rd consumers + QueueConsumerList.ConsumerNode sub1Node = getNodeForConsumer(_subList, _sub1); + assertNotNull("Should have been a node present for the consumer", sub1Node); + QueueConsumerList.ConsumerNode sub3Node = getNodeForConsumer(_subList, _sub3); + assertNotNull("Should have been a node present for the consumer", sub3Node); + + //mark the first consumer node + assertTrue("should have succeeded in updating the marked node", + _subList.updateMarkedNode(_subList.getMarkedNode(), sub1Node)); + + //remove the 1st consumer from the list + assertTrue("Removing consumer node should have succeeded", _subList.remove(_sub1)); + //verify the 1st consumer is no longer the marker node (replaced by a dummy), or in the main list structure + assertNotSame("Unexpected marker node", sub1Node, _subList.getMarkedNode()); + assertNull("Should not have been a node present in the list structure for the marked-but-removed sub1 node", + getNodeForConsumer(_subList, _sub1)); + + //remove the 2nd consumer from the list + assertTrue("Removing consumer node should have succeeded", _subList.remove(_sub2)); + + //verify the marker node isn't leaking subsequently removed nodes, by ensuring the very next node + //in its list structure is now the 3rd consumer (since the 2nd was removed too) + assertEquals("Unexpected next node", sub3Node, _subList.getMarkedNode().nextNode()); + + //remove the 3rd and final/tail consumer + assertTrue("Removing consumer node should have succeeded", _subList.remove(_sub3)); + + //verify the marker node isn't leaking subsequently removed nodes, by ensuring the very next node + //in its list structure is now the dummy tail (since the 3rd consumer was removed, and a dummy + //tail was inserted) and NOT the 3rd sub node. + assertNotSame("Unexpected next node", sub3Node, _subList.getMarkedNode().nextNode()); + assertTrue("Unexpected next node", _subList.getMarkedNode().nextNode().isDeleted()); + assertNull("Next non-deleted node from the marker should now be the list end, i.e. null", _subList.getMarkedNode().findNext()); + } + + /** + * Test that the marked node 'findNext' behaviour is as expected after a consumer is added + * to the list following the tail consumer node being removed while it is the marked node. + * That is, that the new consumers node is returned by getMarkedNode().findNext(). + */ + public void testMarkedNodeFindsNewConsumerAfterRemovingTailWhilstMarked() + { + //get the node out the list for the 3rd consumer + QueueConsumerList.ConsumerNode sub3Node = getNodeForConsumer(_subList, _sub3); + assertNotNull("Should have been a node present for the consumer", sub3Node); + + //mark the 3rd consumer node + assertTrue("should have succeeded in updating the marked node", + _subList.updateMarkedNode(_subList.getMarkedNode(), sub3Node)); + + //verify calling findNext on the marked node returns null, i.e. the end of the list has been reached + assertEquals("Unexpected node after marked node", null, _subList.getMarkedNode().findNext()); + + //remove the 3rd(marked) consumer from the list + assertTrue("Removing consumer node should have succeeded", _subList.remove(_sub3)); + + //add a new 4th consumer to the list + QueueConsumer sub4 = newMockConsumer(); + _subList.add(sub4); + + //get the node out the list for the 4th consumer + QueueConsumerList.ConsumerNode sub4Node = getNodeForConsumer(_subList, sub4); + assertNotNull("Should have been a node present for the consumer", sub4Node); + + //verify the marked node (which is now a dummy substitute for the 3rd consumer) returns + //the 4th consumers node as the next non-deleted node. + assertEquals("Unexpected next node", sub4Node, _subList.getMarkedNode().findNext()); + } + + /** + * Test that setting the marked node to null doesn't cause problems during remove operations + */ + public void testRemoveWithNullMarkedNode() + { + //set the marker to null + assertTrue("should have succeeded in updating the marked node", + _subList.updateMarkedNode(_subList.getMarkedNode(), null)); + + //remove the 1st consumer from the main list + assertTrue("Removing consumer node should have succeeded", _subList.remove(_sub1)); + + //verify the 1st consumer is no longer in the main list structure + assertNull("Should not have been a node present in the main list structure for sub1", + getNodeForConsumer(_subList, _sub1)); + assertEquals("Unexpected number of nodes", 2, countNodes(_subList)); + } + + /** + * Tests that after the first (non-head) node of the list is marked deleted but has not + * yet been removed, the iterator still skips it. + */ + public void testIteratorSkipsFirstDeletedNode() + { + //'delete' but don't remove the node for the 1st consumer + assertTrue("Deleting consumer node should have succeeded", + getNodeForConsumer(_subList, _sub1).delete()); + assertNotNull("Should still have been a node present for the deleted consumer", + getNodeForConsumer(_subList, _sub1)); + + QueueConsumerList.ConsumerNodeIterator iter = _subList.iterator(); + + //verify the iterator returns the 2nd consumers node + assertTrue("Iterator should have been able to advance", iter.advance()); + assertEquals("Iterator returned unexpected ConsumerNode", _sub2, iter.getNode().getConsumer()); + + //verify the iterator returns the 3rd consumers node and not the 2nd. + assertTrue("Iterator should have been able to advance", iter.advance()); + assertEquals("Iterator returned unexpected ConsumerNode", _sub3, iter.getNode().getConsumer()); + } + + /** + * Tests that after a central node of the list is marked deleted but has not yet been removed, + * the iterator still skips it. + */ + public void testIteratorSkipsCentralDeletedNode() + { + //'delete' but don't remove the node for the 2nd consumer + assertTrue("Deleting consumer node should have succeeded", + getNodeForConsumer(_subList, _sub2).delete()); + assertNotNull("Should still have been a node present for the deleted consumer", + getNodeForConsumer(_subList, _sub2)); + + QueueConsumerList.ConsumerNodeIterator iter = _subList.iterator(); + + //verify the iterator returns the 1st consumers node + assertTrue("Iterator should have been able to advance", iter.advance()); + assertEquals("Iterator returned unexpected ConsumerNode", _sub1, iter.getNode().getConsumer()); + + //verify the iterator returns the 3rd consumers node and not the 2nd. + assertTrue("Iterator should have been able to advance", iter.advance()); + assertEquals("Iterator returned unexpected ConsumerNode", _sub3, iter.getNode().getConsumer()); + } + + /** + * Tests that after the last node of the list is marked deleted but has not yet been removed, + * the iterator still skips it. + */ + public void testIteratorSkipsDeletedFinalNode() + { + //'delete' but don't remove the node for the 3rd consumer + assertTrue("Deleting consumer node should have succeeded", + getNodeForConsumer(_subList, _sub3).delete()); + assertNotNull("Should still have been a node present for the deleted 3rd consumer", + getNodeForConsumer(_subList, _sub3)); + + QueueConsumerList.ConsumerNodeIterator iter = _subList.iterator(); + + //verify the iterator returns the 1st consumers node + assertTrue("Iterator should have been able to advance", iter.advance()); + assertEquals("Iterator returned unexpected ConsumerNode", _sub1, iter.getNode().getConsumer()); + + //verify the iterator returns the 2nd consumers node + assertTrue("Iterator should have been able to advance", iter.advance()); + assertEquals("Iterator returned unexpected ConsumerNode", _sub2, iter.getNode().getConsumer()); + + //verify the iterator can no longer advance and does not return a consumer node + assertFalse("Iterator should not have been able to advance", iter.advance()); + assertEquals("Iterator returned unexpected ConsumerNode", null, iter.getNode()); + } +} diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java index ea2e29d40d..66c12717db 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java @@ -24,21 +24,28 @@ import org.apache.qpid.AMQException; import org.apache.qpid.server.binding.Binding; import org.apache.qpid.server.configuration.QueueConfiguration; import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.filter.FilterManager; import org.apache.qpid.server.logging.LogSubject; +import org.apache.qpid.server.message.InstanceProperties; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.security.AuthorizationHolder; -import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.consumer.Consumer; +import org.apache.qpid.server.consumer.ConsumerTarget; +import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.util.Action; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Collection; import java.util.Collections; +import java.util.EnumSet; import java.util.List; import java.util.Set; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; -public class MockAMQQueue implements AMQQueue +public class MockAMQQueue implements AMQQueue { private boolean _deleted = false; private String _name; @@ -202,32 +209,44 @@ public class MockAMQQueue implements AMQQueue return _virtualhost; } - public String getName() + @Override + public QueueConsumer addConsumer(final ConsumerTarget target, + final FilterManager filters, + final Class messageClass, + final String consumerName, + final EnumSet options) throws AMQException { - return _name; + return new QueueConsumer(filters, messageClass, options.contains(Consumer.Option.ACQUIRES), + options.contains(Consumer.Option.SEES_REQUEUES), consumerName, + options.contains(Consumer.Option.TRANSIENT), target ); } - public void registerSubscription(Subscription subscription, boolean exclusive) throws AMQException + public String getName() { - + return _name; } - public void unregisterSubscription(Subscription subscription) throws AMQException + @Override + public int send(final ServerMessage message, + final InstanceProperties instanceProperties, + final ServerTransaction txn, + final Action> postEnqueueAction) { - + return 0; } - public Collection getConsumers() + + public Collection getConsumers() { return Collections.emptyList(); } - public void addSubscriptionRegistrationListener(final SubscriptionRegistrationListener listener) + public void addConsumerRegistrationListener(final ConsumerRegistrationListener listener) { } - public void removeSubscriptionRegistrationListener(final SubscriptionRegistrationListener listener) + public void removeConsumerRegistrationListener(final ConsumerRegistrationListener listener) { } @@ -242,7 +261,7 @@ public class MockAMQQueue implements AMQQueue return 0; } - public boolean hasExclusiveSubscriber() + public boolean hasExclusiveConsumer() { return false; } @@ -293,41 +312,29 @@ public class MockAMQQueue implements AMQQueue return getMessageCount(); } - public void enqueue(ServerMessage message) throws AMQException - { - } - - public void enqueue(ServerMessage message, PostEnqueueAction action) throws AMQException + public void enqueue(ServerMessage message, Action> action) throws AMQException { } - public void enqueue(ServerMessage message, boolean sync, PostEnqueueAction action) throws AMQException - { - } - public void requeue(QueueEntry entry) { } - public void requeue(QueueEntryImpl storeContext, Subscription subscription) - { - } - - public void dequeue(QueueEntry entry, Subscription sub) + public void dequeue(QueueEntry entry, Consumer sub) { } - public boolean resend(QueueEntry entry, Subscription subscription) throws AMQException + public boolean resend(QueueEntry entry, Consumer consumer) throws AMQException { return false; } - public void addQueueDeleteTask(Task task) + public void addQueueDeleteTask(Action task) { } - public void removeQueueDeleteTask(final Task task) + public void removeQueueDeleteTask(final Action task) { } @@ -427,12 +434,12 @@ public class MockAMQQueue implements AMQQueue return null; } - public void flushSubscription(Subscription sub) throws AMQException + public void flushConsumer(Consumer sub) throws AMQException { } - public void deliverAsync(Subscription sub) + public void deliverAsync(Consumer sub) { } diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java index d3c866f747..89b366567d 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/MockQueueEntry.java @@ -24,9 +24,13 @@ import org.apache.qpid.AMQException; import org.apache.qpid.server.filter.Filterable; import org.apache.qpid.server.message.AMQMessageHeader; import org.apache.qpid.server.message.InstanceProperties; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.message.ServerMessage; -import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.store.TransactionLogResource; +import org.apache.qpid.server.consumer.Consumer; import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.util.Action; +import org.apache.qpid.server.util.StateChangeListener; public class MockQueueEntry implements QueueEntry { @@ -38,22 +42,28 @@ public class MockQueueEntry implements QueueEntry return false; } - public boolean acquire(Subscription sub) + public boolean acquire(QueueConsumer sub) { return false; } - public boolean acquiredBySubscription() + @Override + public int getMaximumDeliveryCount() + { + return 0; + } + + public boolean acquiredByConsumer() { return false; } - public boolean isAcquiredBy(Subscription subscription) + public boolean isAcquiredBy(QueueConsumer consumer) { return false; } - public void addStateChangeListener(StateChangeListener listener) + public void addStateChangeListener(StateChangeListener, State> listener) { } @@ -63,7 +73,7 @@ public class MockQueueEntry implements QueueEntry } - public int routeToAlternate(final BaseQueue.PostEnqueueAction action, final ServerTransaction txn) + public int routeToAlternate(final Action> action, final ServerTransaction txn) { return 0; } @@ -78,7 +88,7 @@ public class MockQueueEntry implements QueueEntry return false; } - public Subscription getDeliveredSubscription() + public QueueConsumer getDeliveredConsumer() { return null; } @@ -93,7 +103,7 @@ public class MockQueueEntry implements QueueEntry return _message; } - public AMQQueue getQueue() + public AMQQueue getQueue() { return null; } @@ -116,7 +126,7 @@ public class MockQueueEntry implements QueueEntry } - public boolean isRejectedBy(long subscriptionId) + public boolean isRejectedBy(QueueConsumer consumer) { return false; @@ -136,8 +146,14 @@ public class MockQueueEntry implements QueueEntry } + @Override + public boolean resend() throws AMQException + { + return false; + } + - public boolean removeStateChangeListener(StateChangeListener listener) + public boolean removeStateChangeListener(StateChangeListener, State> listener) { return false; @@ -217,4 +233,10 @@ public class MockQueueEntry implements QueueEntry { return InstanceProperties.EMPTY; } + + @Override + public TransactionLogResource getOwningResource() + { + return getQueue(); + } } diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/QueueEntryImplTestBase.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/QueueEntryImplTestBase.java index 2b1e7f5e1f..95139d8740 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/QueueEntryImplTestBase.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/QueueEntryImplTestBase.java @@ -21,11 +21,11 @@ package org.apache.qpid.server.queue; import junit.framework.TestCase; import org.apache.qpid.AMQException; +import org.apache.qpid.server.consumer.ConsumerTarget; import org.apache.qpid.server.message.MessageReference; import org.apache.qpid.server.message.ServerMessage; -import org.apache.qpid.server.queue.QueueEntry.EntryState; -import org.apache.qpid.server.subscription.MockSubscription; -import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.message.MessageInstance.EntryState; +import org.apache.qpid.server.protocol.AMQSessionModel; import java.lang.reflect.Field; @@ -113,11 +113,19 @@ public abstract class QueueEntryImplTestBase extends TestCase */ private void acquire() { - _queueEntry.acquire(new MockSubscription()); + _queueEntry.acquire(newConsumer()); assertTrue("Queue entry should be in ACQUIRED state after invoking of acquire method", _queueEntry.isAcquired()); } + private QueueConsumer newConsumer() + { + final ConsumerTarget target = mock(ConsumerTarget.class); + when(target.getSessionModel()).thenReturn(mock(AMQSessionModel.class)); + final QueueConsumer consumer = new QueueConsumer(null,null,true,true,"mock",false,target); + return consumer; + } + /** * A helper method to get entry state * @@ -140,36 +148,34 @@ public abstract class QueueEntryImplTestBase extends TestCase } /** - * Tests rejecting a queue entry records the Subscription ID - * for later verification by isRejectedBy(subscriptionId). + * Tests rejecting a queue entry records the Consumer ID + * for later verification by isRejectedBy(consumerId). */ public void testRejectAndRejectedBy() { - Subscription sub = new MockSubscription(); - long subId = sub.getSubscriptionID(); + QueueConsumer sub = newConsumer(); - assertFalse("Queue entry should not yet have been rejected by the subscription", _queueEntry.isRejectedBy(subId)); - assertFalse("Queue entry should not yet have been acquired by a subscription", _queueEntry.isAcquired()); + assertFalse("Queue entry should not yet have been rejected by the consumer", _queueEntry.isRejectedBy(sub)); + assertFalse("Queue entry should not yet have been acquired by a consumer", _queueEntry.isAcquired()); - //acquire, reject, and release the message using the subscription + //acquire, reject, and release the message using the consumer assertTrue("Queue entry should have been able to be acquired", _queueEntry.acquire(sub)); _queueEntry.reject(); _queueEntry.release(); //verify the rejection is recorded - assertTrue("Queue entry should have been rejected by the subscription", _queueEntry.isRejectedBy(subId)); + assertTrue("Queue entry should have been rejected by the consumer", _queueEntry.isRejectedBy(sub)); - //repeat rejection using a second subscription - Subscription sub2 = new MockSubscription(); - long sub2Id = sub2.getSubscriptionID(); + //repeat rejection using a second consumer + QueueConsumer sub2 = newConsumer(); - assertFalse("Queue entry should not yet have been rejected by the subscription", _queueEntry.isRejectedBy(sub2Id)); + assertFalse("Queue entry should not yet have been rejected by the consumer", _queueEntry.isRejectedBy(sub2)); assertTrue("Queue entry should have been able to be acquired", _queueEntry.acquire(sub2)); _queueEntry.reject(); - //verify it still records being rejected by both subscriptions - assertTrue("Queue entry should have been rejected by the subscription", _queueEntry.isRejectedBy(subId)); - assertTrue("Queue entry should have been rejected by the subscription", _queueEntry.isRejectedBy(sub2Id)); + //verify it still records being rejected by both consumers + assertTrue("Queue entry should have been rejected by the consumer", _queueEntry.isRejectedBy(sub)); + assertTrue("Queue entry should have been rejected by the consumer", _queueEntry.isRejectedBy(sub2)); } /** diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java index b0e5a510b8..5abc97cee9 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java @@ -29,6 +29,8 @@ import static org.mockito.Matchers.contains; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.when; +import java.util.Arrays; +import java.util.EnumSet; import java.util.Map; import org.apache.log4j.Logger; @@ -38,13 +40,15 @@ import org.apache.qpid.AMQSecurityException; import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.server.exchange.DirectExchange; import org.apache.qpid.server.message.AMQMessageHeader; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.message.MessageReference; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.model.UUIDGenerator; -import org.apache.qpid.server.queue.BaseQueue.PostEnqueueAction; import org.apache.qpid.server.queue.SimpleAMQQueue.QueueEntryFilter; -import org.apache.qpid.server.subscription.MockSubscription; -import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.consumer.ConsumerTarget; +import org.apache.qpid.server.consumer.MockConsumer; +import org.apache.qpid.server.consumer.Consumer; +import org.apache.qpid.server.util.Action; import org.apache.qpid.server.util.BrokerTestHelper; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.test.utils.QpidTestCase; @@ -65,7 +69,8 @@ public class SimpleAMQQueueTest extends QpidTestCase private String _owner = "owner"; private String _routingKey = "routing key"; private DirectExchange _exchange; - private MockSubscription _subscription = new MockSubscription(); + private MockConsumer _consumerTarget = new MockConsumer(); + private QueueConsumer _consumer; private Map _arguments = null; @Override @@ -157,20 +162,21 @@ public class SimpleAMQQueueTest extends QpidTestCase } - public void testRegisterSubscriptionThenEnqueueMessage() throws AMQException + public void testRegisterConsumerThenEnqueueMessage() throws AMQException { - // Check adding a subscription adds it to the queue - _queue.registerSubscription(_subscription, false); - assertEquals("Subscription did not get queue", _queue, - _subscription.getQueue()); + ServerMessage messageA = createMessage(new Long(24)); + + // Check adding a consumer adds it to the queue + _consumer = _queue.addConsumer(_consumerTarget, null, messageA.getClass(), "test", + EnumSet.of(Consumer.Option.ACQUIRES, + Consumer.Option.SEES_REQUEUES)); assertEquals("Queue does not have consumer", 1, _queue.getConsumerCount()); assertEquals("Queue does not have active consumer", 1, - _queue.getActiveConsumerCount()); + _queue.getActiveConsumerCount()); // Check sending a message ends up with the subscriber - ServerMessage messageA = createMessage(new Long(24)); - _queue.enqueue(messageA); + _queue.enqueue(messageA, null); try { Thread.sleep(2000L); @@ -178,45 +184,51 @@ public class SimpleAMQQueueTest extends QpidTestCase catch(InterruptedException e) { } - assertEquals(messageA, _subscription.getQueueContext().getLastSeenEntry().getMessage()); - assertNull(((QueueContext)_subscription.getQueueContext()).getReleasedEntry()); + assertEquals(messageA, _consumer.getQueueContext().getLastSeenEntry().getMessage()); + assertNull(_consumer.getQueueContext().getReleasedEntry()); - // Check removing the subscription removes it's information from the queue - _queue.unregisterSubscription(_subscription); - assertTrue("Subscription still had queue", _subscription.isClosed()); + // Check removing the consumer removes it's information from the queue + _consumer.close(); + assertTrue("Consumer still had queue", _consumerTarget.isClosed()); assertFalse("Queue still has consumer", 1 == _queue.getConsumerCount()); assertFalse("Queue still has active consumer", - 1 == _queue.getActiveConsumerCount()); + 1 == _queue.getActiveConsumerCount()); ServerMessage messageB = createMessage(new Long (25)); - _queue.enqueue(messageB); - assertNull(_subscription.getQueueContext()); + _queue.enqueue(messageB, null); + assertNull(_consumer.getQueueContext()); } - public void testEnqueueMessageThenRegisterSubscription() throws AMQException, InterruptedException + public void testEnqueueMessageThenRegisterConsumer() throws AMQException, InterruptedException { ServerMessage messageA = createMessage(new Long(24)); - _queue.enqueue(messageA); - _queue.registerSubscription(_subscription, false); + _queue.enqueue(messageA, null); + _consumer = _queue.addConsumer(_consumerTarget, null, messageA.getClass(), "test", + EnumSet.of(Consumer.Option.ACQUIRES, + Consumer.Option.SEES_REQUEUES)); Thread.sleep(150); - assertEquals(messageA, _subscription.getQueueContext().getLastSeenEntry().getMessage()); - assertNull("There should be no releasedEntry after an enqueue", ((QueueContext)_subscription.getQueueContext()).getReleasedEntry()); + assertEquals(messageA, _consumer.getQueueContext().getLastSeenEntry().getMessage()); + assertNull("There should be no releasedEntry after an enqueue", + _consumer.getQueueContext().getReleasedEntry()); } /** * Tests enqueuing two messages. */ - public void testEnqueueTwoMessagesThenRegisterSubscription() throws Exception + public void testEnqueueTwoMessagesThenRegisterConsumer() throws Exception { ServerMessage messageA = createMessage(new Long(24)); ServerMessage messageB = createMessage(new Long(25)); - _queue.enqueue(messageA); - _queue.enqueue(messageB); - _queue.registerSubscription(_subscription, false); + _queue.enqueue(messageA, null); + _queue.enqueue(messageB, null); + _consumer = _queue.addConsumer(_consumerTarget, null, messageA.getClass(), "test", + EnumSet.of(Consumer.Option.ACQUIRES, + Consumer.Option.SEES_REQUEUES)); Thread.sleep(150); - assertEquals(messageB, _subscription.getQueueContext().getLastSeenEntry().getMessage()); - assertNull("There should be no releasedEntry after enqueues", ((QueueContext)_subscription.getQueueContext()).getReleasedEntry()); + assertEquals(messageB, _consumer.getQueueContext().getLastSeenEntry().getMessage()); + assertNull("There should be no releasedEntry after enqueues", + _consumer.getQueueContext().getReleasedEntry()); } /** @@ -225,21 +237,19 @@ public class SimpleAMQQueueTest extends QpidTestCase */ public void testReleasedMessageIsResentToSubscriber() throws Exception { - _queue.registerSubscription(_subscription, false); - - final ArrayList queueEntries = new ArrayList(); - PostEnqueueAction postEnqueueAction = new PostEnqueueAction() - { - public void onEnqueue(QueueEntry entry) - { - queueEntries.add(entry); - } - }; ServerMessage messageA = createMessage(new Long(24)); ServerMessage messageB = createMessage(new Long(25)); ServerMessage messageC = createMessage(new Long(26)); + + _consumer = _queue.addConsumer(_consumerTarget, null, messageA.getClass(), "test", + EnumSet.of(Consumer.Option.ACQUIRES, + Consumer.Option.SEES_REQUEUES)); + + final ArrayList queueEntries = new ArrayList(); + EntryListAddingAction postEnqueueAction = new EntryListAddingAction(queueEntries); + /* Enqueue three messages */ _queue.enqueue(messageA, postEnqueueAction); @@ -248,7 +258,9 @@ public class SimpleAMQQueueTest extends QpidTestCase Thread.sleep(150); // Work done by SubFlushRunner/QueueRunner Threads - assertEquals("Unexpected total number of messages sent to subscription", 3, _subscription.getMessages().size()); + assertEquals("Unexpected total number of messages sent to consumer", + 3, + _consumerTarget.getMessages().size()); assertFalse("Redelivery flag should not be set", queueEntries.get(0).isRedelivered()); assertFalse("Redelivery flag should not be set", queueEntries.get(1).isRedelivered()); assertFalse("Redelivery flag should not be set", queueEntries.get(2).isRedelivered()); @@ -259,11 +271,14 @@ public class SimpleAMQQueueTest extends QpidTestCase Thread.sleep(150); // Work done by SubFlushRunner/QueueRunner Threads - assertEquals("Unexpected total number of messages sent to subscription", 4, _subscription.getMessages().size()); + assertEquals("Unexpected total number of messages sent to consumer", + 4, + _consumerTarget.getMessages().size()); assertTrue("Redelivery flag should now be set", queueEntries.get(0).isRedelivered()); assertFalse("Redelivery flag should remain be unset", queueEntries.get(1).isRedelivered()); assertFalse("Redelivery flag should remain be unset",queueEntries.get(2).isRedelivered()); - assertNull("releasedEntry should be cleared after requeue processed", ((QueueContext)_subscription.getQueueContext()).getReleasedEntry()); + assertNull("releasedEntry should be cleared after requeue processed", + _consumer.getQueueContext().getReleasedEntry()); } /** @@ -273,20 +288,17 @@ public class SimpleAMQQueueTest extends QpidTestCase */ public void testReleaseMessageThatBecomesExpiredIsNotRedelivered() throws Exception { - _queue.registerSubscription(_subscription, false); + ServerMessage messageA = createMessage(new Long(24)); + + _consumer = _queue.addConsumer(_consumerTarget, null, messageA.getClass(), "test", + EnumSet.of(Consumer.Option.SEES_REQUEUES, + Consumer.Option.ACQUIRES)); final ArrayList queueEntries = new ArrayList(); - PostEnqueueAction postEnqueueAction = new PostEnqueueAction() - { - public void onEnqueue(QueueEntry entry) - { - queueEntries.add(entry); - } - }; + EntryListAddingAction postEnqueueAction = new EntryListAddingAction(queueEntries); /* Enqueue one message with expiration set for a short time in the future */ - ServerMessage messageA = createMessage(new Long(24)); int messageExpirationOffset = 200; final long expiration = System.currentTimeMillis() + messageExpirationOffset; when(messageA.getExpiration()).thenReturn(expiration); @@ -296,7 +308,9 @@ public class SimpleAMQQueueTest extends QpidTestCase int subFlushWaitTime = 150; Thread.sleep(subFlushWaitTime); // Work done by SubFlushRunner/QueueRunner Threads - assertEquals("Unexpected total number of messages sent to subscription", 1, _subscription.getMessages().size()); + assertEquals("Unexpected total number of messages sent to consumer", + 1, + _consumerTarget.getMessages().size()); assertFalse("Redelivery flag should not be set", queueEntries.get(0).isRedelivered()); /* Wait a little more to be sure that message will have expired, then release the first message only, causing it to be requeued */ @@ -306,9 +320,12 @@ public class SimpleAMQQueueTest extends QpidTestCase Thread.sleep(subFlushWaitTime); // Work done by SubFlushRunner/QueueRunner Threads assertTrue("Expecting the queue entry to be now expired", queueEntries.get(0).expired()); - assertEquals("Total number of messages sent should not have changed", 1, _subscription.getMessages().size()); + assertEquals("Total number of messages sent should not have changed", + 1, + _consumerTarget.getMessages().size()); assertFalse("Redelivery flag should not be set", queueEntries.get(0).isRedelivered()); - assertNull("releasedEntry should be cleared after requeue processed", ((QueueContext)_subscription.getQueueContext()).getReleasedEntry()); + assertNull("releasedEntry should be cleared after requeue processed", + _consumer.getQueueContext().getReleasedEntry()); } @@ -320,21 +337,18 @@ public class SimpleAMQQueueTest extends QpidTestCase */ public void testReleasedOutOfComparableOrderAreRedelivered() throws Exception { - _queue.registerSubscription(_subscription, false); - - final ArrayList queueEntries = new ArrayList(); - PostEnqueueAction postEnqueueAction = new PostEnqueueAction() - { - public void onEnqueue(QueueEntry entry) - { - queueEntries.add(entry); - } - }; ServerMessage messageA = createMessage(new Long(24)); ServerMessage messageB = createMessage(new Long(25)); ServerMessage messageC = createMessage(new Long(26)); + _consumer = _queue.addConsumer(_consumerTarget, null, messageA.getClass(), "test", + EnumSet.of(Consumer.Option.ACQUIRES, + Consumer.Option.SEES_REQUEUES)); + + final ArrayList queueEntries = new ArrayList(); + EntryListAddingAction postEnqueueAction = new EntryListAddingAction(queueEntries); + /* Enqueue three messages */ _queue.enqueue(messageA, postEnqueueAction); @@ -343,7 +357,9 @@ public class SimpleAMQQueueTest extends QpidTestCase Thread.sleep(150); // Work done by SubFlushRunner/QueueRunner Threads - assertEquals("Unexpected total number of messages sent to subscription", 3, _subscription.getMessages().size()); + assertEquals("Unexpected total number of messages sent to consumer", + 3, + _consumerTarget.getMessages().size()); assertFalse("Redelivery flag should not be set", queueEntries.get(0).isRedelivered()); assertFalse("Redelivery flag should not be set", queueEntries.get(1).isRedelivered()); assertFalse("Redelivery flag should not be set", queueEntries.get(2).isRedelivered()); @@ -355,37 +371,41 @@ public class SimpleAMQQueueTest extends QpidTestCase Thread.sleep(150); // Work done by SubFlushRunner/QueueRunner Threads - assertEquals("Unexpected total number of messages sent to subscription", 5, _subscription.getMessages().size()); + assertEquals("Unexpected total number of messages sent to consumer", + 5, + _consumerTarget.getMessages().size()); assertTrue("Redelivery flag should now be set", queueEntries.get(0).isRedelivered()); assertFalse("Redelivery flag should remain be unset", queueEntries.get(1).isRedelivered()); assertTrue("Redelivery flag should now be set",queueEntries.get(2).isRedelivered()); - assertNull("releasedEntry should be cleared after requeue processed", ((QueueContext)_subscription.getQueueContext()).getReleasedEntry()); + assertNull("releasedEntry should be cleared after requeue processed", + _consumer.getQueueContext().getReleasedEntry()); } /** - * Tests that a release requeues an entry for a queue with multiple subscriptions. Verifies that a + * Tests that a release requeues an entry for a queue with multiple consumers. Verifies that a * requeue resends a message to a single subscriber. */ - public void testReleaseForQueueWithMultipleSubscriptions() throws Exception + public void testReleaseForQueueWithMultipleConsumers() throws Exception { - MockSubscription subscription1 = new MockSubscription(); - MockSubscription subscription2 = new MockSubscription(); + ServerMessage messageA = createMessage(new Long(24)); + ServerMessage messageB = createMessage(new Long(25)); - _queue.registerSubscription(subscription1, false); - _queue.registerSubscription(subscription2, false); + MockConsumer target1 = new MockConsumer(); + MockConsumer target2 = new MockConsumer(); - final ArrayList queueEntries = new ArrayList(); - PostEnqueueAction postEnqueueAction = new PostEnqueueAction() - { - public void onEnqueue(QueueEntry entry) - { - queueEntries.add(entry); - } - }; - ServerMessage messageA = createMessage(new Long(24)); - ServerMessage messageB = createMessage(new Long(25)); + QueueConsumer consumer1 = _queue.addConsumer(target1, null, messageA.getClass(), "test", + EnumSet.of(Consumer.Option.ACQUIRES, + Consumer.Option.SEES_REQUEUES)); + + QueueConsumer consumer2 = _queue.addConsumer(target2, null, messageA.getClass(), "test", + EnumSet.of(Consumer.Option.ACQUIRES, + Consumer.Option.SEES_REQUEUES)); + + + final ArrayList queueEntries = new ArrayList(); + EntryListAddingAction postEnqueueAction = new EntryListAddingAction(queueEntries); /* Enqueue two messages */ @@ -394,32 +414,40 @@ public class SimpleAMQQueueTest extends QpidTestCase Thread.sleep(150); // Work done by SubFlushRunner/QueueRunner Threads - assertEquals("Unexpected total number of messages sent to both after enqueue", 2, subscription1.getMessages().size() + subscription2.getMessages().size()); + assertEquals("Unexpected total number of messages sent to both after enqueue", + 2, + target1.getMessages().size() + target2.getMessages().size()); /* Now release the first message only, causing it to be requeued */ queueEntries.get(0).release(); Thread.sleep(150); // Work done by SubFlushRunner/QueueRunner Threads - assertEquals("Unexpected total number of messages sent to both subscriptions after release", 3, subscription1.getMessages().size() + subscription2.getMessages().size()); - assertNull("releasedEntry should be cleared after requeue processed", ((QueueContext)subscription1.getQueueContext()).getReleasedEntry()); - assertNull("releasedEntry should be cleared after requeue processed", ((QueueContext)subscription2.getQueueContext()).getReleasedEntry()); + assertEquals("Unexpected total number of messages sent to both consumers after release", + 3, + target1.getMessages().size() + target2.getMessages().size()); + assertNull("releasedEntry should be cleared after requeue processed", + consumer1.getQueueContext().getReleasedEntry()); + assertNull("releasedEntry should be cleared after requeue processed", + consumer2.getQueueContext().getReleasedEntry()); } public void testExclusiveConsumer() throws AMQException { - // Check adding an exclusive subscription adds it to the queue - _queue.registerSubscription(_subscription, true); - assertEquals("Subscription did not get queue", _queue, - _subscription.getQueue()); + ServerMessage messageA = createMessage(new Long(24)); + // Check adding an exclusive consumer adds it to the queue + + _consumer = _queue.addConsumer(_consumerTarget, null, messageA.getClass(), "test", + EnumSet.of(Consumer.Option.EXCLUSIVE, Consumer.Option.ACQUIRES, + Consumer.Option.SEES_REQUEUES)); + assertEquals("Queue does not have consumer", 1, - _queue.getConsumerCount()); + _queue.getConsumerCount()); assertEquals("Queue does not have active consumer", 1, - _queue.getActiveConsumerCount()); + _queue.getActiveConsumerCount()); // Check sending a message ends up with the subscriber - ServerMessage messageA = createMessage(new Long(24)); - _queue.enqueue(messageA); + _queue.enqueue(messageA, null); try { Thread.sleep(2000L); @@ -427,14 +455,18 @@ public class SimpleAMQQueueTest extends QpidTestCase catch (InterruptedException e) { } - assertEquals(messageA, _subscription.getQueueContext().getLastSeenEntry().getMessage()); + assertEquals(messageA, _consumer.getQueueContext().getLastSeenEntry().getMessage()); // Check we cannot add a second subscriber to the queue - Subscription subB = new MockSubscription(); + MockConsumer subB = new MockConsumer(); Exception ex = null; try { - _queue.registerSubscription(subB, false); + + _queue.addConsumer(subB, null, messageA.getClass(), "test", + EnumSet.of(Consumer.Option.ACQUIRES, + Consumer.Option.SEES_REQUEUES)); + } catch (AMQException e) { @@ -443,12 +475,18 @@ public class SimpleAMQQueueTest extends QpidTestCase assertNotNull(ex); // Check we cannot add an exclusive subscriber to a queue with an - // existing subscription - _queue.unregisterSubscription(_subscription); - _queue.registerSubscription(_subscription, false); + // existing consumer + _consumer.close(); + _consumer = _queue.addConsumer(_consumerTarget, null, messageA.getClass(), "test", + EnumSet.of(Consumer.Option.ACQUIRES, + Consumer.Option.SEES_REQUEUES)); + try { - _queue.registerSubscription(subB, true); + + _consumer = _queue.addConsumer(subB, null, messageA.getClass(), "test", + EnumSet.of(Consumer.Option.EXCLUSIVE)); + } catch (AMQException e) { @@ -462,23 +500,45 @@ public class SimpleAMQQueueTest extends QpidTestCase _queue.stop(); _queue = new SimpleAMQQueue(UUIDGenerator.generateRandomUUID(), _qname, false, null, true, false, _virtualHost, Collections.EMPTY_MAP); _queue.setDeleteOnNoConsumers(true); - _queue.registerSubscription(_subscription, false); - ServerMessage message = createMessage(new Long(25)); - _queue.enqueue(message); - _queue.unregisterSubscription(_subscription); - assertTrue("Queue was not deleted when subscription was removed", + + ServerMessage message = createMessage(new Long(25)); + _consumer = _queue.addConsumer(_consumerTarget, null, message.getClass(), "test", + EnumSet.of(Consumer.Option.ACQUIRES, + Consumer.Option.SEES_REQUEUES)); + + _queue.enqueue(message, null); + _consumer.close(); + assertTrue("Queue was not deleted when consumer was removed", _queue.isDeleted()); } public void testResend() throws Exception { - _queue.registerSubscription(_subscription, false); Long id = new Long(26); ServerMessage message = createMessage(id); - _queue.enqueue(message); - QueueEntry entry = _subscription.getQueueContext().getLastSeenEntry(); - entry.setRedelivered(); - _queue.resend(entry, _subscription); + + _consumer = _queue.addConsumer(_consumerTarget, null, message.getClass(), "test", + EnumSet.of(Consumer.Option.ACQUIRES, Consumer.Option.SEES_REQUEUES)); + + _queue.enqueue(message, new Action>() + { + @Override + public void performAction(final MessageInstance object) + { + QueueEntry entry = (QueueEntry) object; + entry.setRedelivered(); + try + { + _consumer.resend(entry); + } + catch (AMQException e) + { + fail("Exception thrown: " + e.getMessage()); + } + } + }); + + } @@ -489,7 +549,7 @@ public class SimpleAMQQueueTest extends QpidTestCase ServerMessage message = createMessage(messageId); // Put message on queue - _queue.enqueue(message); + _queue.enqueue(message, null); // Get message id Long testmsgid = _queue.getMessagesOnTheQueue(1).get(0); @@ -505,7 +565,7 @@ public class SimpleAMQQueueTest extends QpidTestCase Long messageId = new Long(i); ServerMessage message = createMessage(messageId); // Put message on queue - _queue.enqueue(message); + _queue.enqueue(message, null); } // Get message ids List msgids = _queue.getMessagesOnTheQueue(5); @@ -526,7 +586,7 @@ public class SimpleAMQQueueTest extends QpidTestCase Long messageId = new Long(i); ServerMessage message = createMessage(messageId); // Put message on queue - _queue.enqueue(message); + _queue.enqueue(message, null); } // Get message ids List msgids = _queue.getMessagesOnTheQueue(5, 5); @@ -547,7 +607,7 @@ public class SimpleAMQQueueTest extends QpidTestCase Long messageId = new Long(i); ServerMessage message = createMessage(messageId); // Put message on queue - _queue.enqueue(message); + _queue.enqueue(message, null); } // Get non-existent 0th QueueEntry & check returned list was empty @@ -605,19 +665,19 @@ public class SimpleAMQQueueTest extends QpidTestCase /** * processQueue() is used when asynchronously delivering messages to - * subscriptions which could not be delivered immediately during the + * consumers which could not be delivered immediately during the * enqueue() operation. * * A defect within the method would mean that delivery of these messages may * not occur should the Runner stop before all messages have been processed. * Such a defect was discovered when Selectors were used such that one and - * only one subscription can/will accept any given messages, but multiple - * subscriptions are present, and one of the earlier subscriptions receives + * only one consumer can/will accept any given messages, but multiple + * consumers are present, and one of the earlier consumers receives * more messages than the others. * * This test is to validate that the processQueue() method is able to * correctly deliver all of the messages present for asynchronous delivery - * to subscriptions in such a scenario. + * to consumers in such a scenario. */ public void testProcessQueueWithUniqueSelectors() throws Exception { @@ -626,10 +686,10 @@ public class SimpleAMQQueueTest extends QpidTestCase false, false, _virtualHost, factory, null) { @Override - public void deliverAsync(Subscription sub) + public void deliverAsync(QueueConsumer sub) { // do nothing, i.e prevent deliveries by the SubFlushRunner - // when registering the new subscriptions + // when registering the new consumers } }; @@ -645,25 +705,28 @@ public class SimpleAMQQueueTest extends QpidTestCase QueueEntry msg4 = list.add(createMessage(4L)); QueueEntry msg5 = list.add(createMessage(5L)); - // Create lists of the entries each subscription should be interested - // in.Bias over 50% of the messages to the first subscription so that - // the later subscriptions reject them and report being done before - // the first subscription as the processQueue method proceeds. - List msgListSub1 = createEntriesList(msg1, msg2, msg3); - List msgListSub2 = createEntriesList(msg4); - List msgListSub3 = createEntriesList(msg5); - - MockSubscription sub1 = new MockSubscription(msgListSub1); - MockSubscription sub2 = new MockSubscription(msgListSub2); - MockSubscription sub3 = new MockSubscription(msgListSub3); - - // register the subscriptions - testQueue.registerSubscription(sub1, false); - testQueue.registerSubscription(sub2, false); - testQueue.registerSubscription(sub3, false); + // Create lists of the entries each consumer should be interested + // in.Bias over 50% of the messages to the first consumer so that + // the later consumers reject them and report being done before + // the first consumer as the processQueue method proceeds. + List msgListSub1 = createEntriesList(msg1, msg2, msg3); + List msgListSub2 = createEntriesList(msg4); + List msgListSub3 = createEntriesList(msg5); + + MockConsumer sub1 = new MockConsumer(msgListSub1); + MockConsumer sub2 = new MockConsumer(msgListSub2); + MockConsumer sub3 = new MockConsumer(msgListSub3); + + // register the consumers + testQueue.addConsumer(sub1, sub1.getFilters(), msg1.getMessage().getClass(), "test", + EnumSet.of(Consumer.Option.ACQUIRES, Consumer.Option.SEES_REQUEUES)); + testQueue.addConsumer(sub2, sub2.getFilters(), msg1.getMessage().getClass(), "test", + EnumSet.of(Consumer.Option.ACQUIRES, Consumer.Option.SEES_REQUEUES)); + testQueue.addConsumer(sub3, sub3.getFilters(), msg1.getMessage().getClass(), "test", + EnumSet.of(Consumer.Option.ACQUIRES, Consumer.Option.SEES_REQUEUES)); //check that no messages have been delivered to the - //subscriptions during registration + //consumers during registration assertEquals("No messages should have been delivered yet", 0, sub1.getMessages().size()); assertEquals("No messages should have been delivered yet", 0, sub2.getMessages().size()); assertEquals("No messages should have been delivered yet", 0, sub3.getMessages().size()); @@ -680,9 +743,9 @@ public class SimpleAMQQueueTest extends QpidTestCase }); // check expected messages delivered to correct consumers - verifyReceivedMessages(msgListSub1, sub1.getMessages()); - verifyReceivedMessages(msgListSub2, sub2.getMessages()); - verifyReceivedMessages(msgListSub3, sub3.getMessages()); + verifyReceivedMessages(Arrays.asList((MessageInstance)msg1,msg2,msg3), sub1.getMessages()); + verifyReceivedMessages(Collections.singletonList((MessageInstance)msg4), sub2.getMessages()); + verifyReceivedMessages(Collections.singletonList((MessageInstance)msg5), sub3.getMessages()); } /** @@ -850,7 +913,7 @@ public class SimpleAMQQueueTest extends QpidTestCase false, "testOwner", false, false, _virtualHost, null) { @Override - public void deliverAsync(Subscription sub) + public void deliverAsync(QueueConsumer sub) { // do nothing } @@ -865,15 +928,15 @@ public class SimpleAMQQueueTest extends QpidTestCase // latch to wait for message receipt final CountDownLatch latch = new CountDownLatch(messageNumber -1); - // create a subscription - MockSubscription subscription = new MockSubscription() + // create a consumer + MockConsumer consumer = new MockConsumer() { /** * Send a message and decrement latch * @param entry * @param batch */ - public void send(QueueEntry entry, boolean batch) throws AMQException + public void send(MessageInstance entry, boolean batch) throws AMQException { super.send(entry, batch); latch.countDown(); @@ -883,7 +946,12 @@ public class SimpleAMQQueueTest extends QpidTestCase try { // subscribe - testQueue.registerSubscription(subscription, false); + testQueue.addConsumer(consumer, + null, + entries.get(0).getMessage().getClass(), + "test", + EnumSet.of(Consumer.Option.ACQUIRES, + Consumer.Option.SEES_REQUEUES)); // process queue testQueue.processQueue(new QueueRunner(testQueue) @@ -907,12 +975,12 @@ public class SimpleAMQQueueTest extends QpidTestCase { Thread.currentThread().interrupt(); } - List expected = createEntriesList(entries.get(0), entries.get(2), entries.get(3)); - verifyReceivedMessages(expected, subscription.getMessages()); + List expected = Arrays.asList((MessageInstance)entries.get(0), entries.get(2), entries.get(3)); + verifyReceivedMessages(expected, consumer.getMessages()); } /** - * Tests that entry in dequeued state are not enqueued and not delivered to subscription + * Tests that entry in dequeued state are not enqueued and not delivered to consumer */ public void testEnqueueDequeuedEntry() { @@ -948,7 +1016,7 @@ public class SimpleAMQQueueTest extends QpidTestCase } @Override - public boolean acquire(Subscription sub) + public boolean acquire(QueueConsumer sub) { if(message.getMessageNumber() % 2 == 0) { @@ -964,24 +1032,29 @@ public class SimpleAMQQueueTest extends QpidTestCase }; } }, null); - // create a subscription - MockSubscription subscription = new MockSubscription(); + // create a consumer + MockConsumer consumer = new MockConsumer(); - // register subscription + // register consumer try { - queue.registerSubscription(subscription, false); + queue.addConsumer(consumer, + null, + createMessage(-1l).getClass(), + "test", + EnumSet.of(Consumer.Option.ACQUIRES, + Consumer.Option.SEES_REQUEUES)); } catch (AMQException e) { - fail("Failure to register subscription:" + e.getMessage()); + fail("Failure to register consumer:" + e.getMessage()); } // put test messages into a queue putGivenNumberOfMessages(queue, 4); // assert received messages - List messages = subscription.getMessages(); + List messages = consumer.getMessages(); assertEquals("Only 2 messages should be returned", 2, messages.size()); assertEquals("ID of first message should be 1", 1l, (messages.get(0).getMessage()).getMessageNumber()); @@ -994,55 +1067,64 @@ public class SimpleAMQQueueTest extends QpidTestCase final SimpleAMQQueue queue = new SimpleAMQQueue(UUIDGenerator.generateRandomUUID(), "testActiveConsumerCount", false, "testOwner", false, false, _virtualHost, new SimpleQueueEntryList.Factory(), null); - //verify adding an active subscription increases the count - final MockSubscription subscription1 = new MockSubscription(); - subscription1.setActive(true); + //verify adding an active consumer increases the count + final MockConsumer consumer1 = new MockConsumer(); + consumer1.setActive(true); + consumer1.setState(ConsumerTarget.State.ACTIVE); assertEquals("Unexpected active consumer count", 0, queue.getActiveConsumerCount()); - queue.registerSubscription(subscription1, false); + queue.addConsumer(consumer1, + null, + createMessage(-1l).getClass(), + "test", + EnumSet.of(Consumer.Option.ACQUIRES, + Consumer.Option.SEES_REQUEUES)); assertEquals("Unexpected active consumer count", 1, queue.getActiveConsumerCount()); - //verify adding an inactive subscription doesn't increase the count - final MockSubscription subscription2 = new MockSubscription(); - subscription2.setActive(false); + //verify adding an inactive consumer doesn't increase the count + final MockConsumer consumer2 = new MockConsumer(); + consumer2.setActive(false); + consumer2.setState(ConsumerTarget.State.SUSPENDED); assertEquals("Unexpected active consumer count", 1, queue.getActiveConsumerCount()); - queue.registerSubscription(subscription2, false); + queue.addConsumer(consumer2, + null, + createMessage(-1l).getClass(), + "test", + EnumSet.of(Consumer.Option.ACQUIRES, + Consumer.Option.SEES_REQUEUES)); assertEquals("Unexpected active consumer count", 1, queue.getActiveConsumerCount()); //verify behaviour in face of expected state changes: - //verify a subscription going suspended->active increases the count - queue.stateChange(subscription2, Subscription.State.SUSPENDED, Subscription.State.ACTIVE); + //verify a consumer going suspended->active increases the count + consumer2.setState(ConsumerTarget.State.ACTIVE); assertEquals("Unexpected active consumer count", 2, queue.getActiveConsumerCount()); - //verify a subscription going active->suspended decreases the count - queue.stateChange(subscription2, Subscription.State.ACTIVE, Subscription.State.SUSPENDED); + //verify a consumer going active->suspended decreases the count + consumer2.setState(ConsumerTarget.State.SUSPENDED); assertEquals("Unexpected active consumer count", 1, queue.getActiveConsumerCount()); - //verify a subscription going suspended->closed doesn't change the count - queue.stateChange(subscription2, Subscription.State.SUSPENDED, Subscription.State.CLOSED); + //verify a consumer going suspended->closed doesn't change the count + consumer2.setState(ConsumerTarget.State.CLOSED); assertEquals("Unexpected active consumer count", 1, queue.getActiveConsumerCount()); - //verify a subscription going active->closed decreases the count - queue.stateChange(subscription2, Subscription.State.ACTIVE, Subscription.State.CLOSED); - assertEquals("Unexpected active consumer count", 0, queue.getActiveConsumerCount()); + //verify a consumer going active->active doesn't change the count + consumer1.setState(ConsumerTarget.State.ACTIVE); + assertEquals("Unexpected active consumer count", 1, queue.getActiveConsumerCount()); - //verify behaviour in face of unexpected state changes: + consumer1.setState(ConsumerTarget.State.SUSPENDED); + assertEquals("Unexpected active consumer count", 0, queue.getActiveConsumerCount()); - //verify a subscription going closed->active increases the count - queue.stateChange(subscription2, Subscription.State.CLOSED, Subscription.State.ACTIVE); - assertEquals("Unexpected active consumer count", 1, queue.getActiveConsumerCount()); + //verify a consumer going suspended->suspended doesn't change the count + consumer1.setState(ConsumerTarget.State.SUSPENDED); + assertEquals("Unexpected active consumer count", 0, queue.getActiveConsumerCount()); - //verify a subscription going active->active doesn't change the count - queue.stateChange(subscription2, Subscription.State.ACTIVE, Subscription.State.ACTIVE); + consumer1.setState(ConsumerTarget.State.ACTIVE); assertEquals("Unexpected active consumer count", 1, queue.getActiveConsumerCount()); - //verify a subscription going closed->suspended doesn't change the count - queue.stateChange(subscription2, Subscription.State.CLOSED, Subscription.State.SUSPENDED); - assertEquals("Unexpected active consumer count", 1, queue.getActiveConsumerCount()); + //verify a consumer going active->closed decreases the count + consumer1.setState(ConsumerTarget.State.CLOSED); + assertEquals("Unexpected active consumer count", 0, queue.getActiveConsumerCount()); - //verify a subscription going suspended->suspended doesn't change the count - queue.stateChange(subscription2, Subscription.State.SUSPENDED, Subscription.State.SUSPENDED); - assertEquals("Unexpected active consumer count", 1, queue.getActiveConsumerCount()); } public void testNotificationFiredOnEnqueue() throws Exception @@ -1052,10 +1134,10 @@ public class SimpleAMQQueueTest extends QpidTestCase _queue.setNotificationListener(listener); _queue.setMaximumMessageCount(2); - _queue.enqueue(createMessage(new Long(24))); + _queue.enqueue(createMessage(new Long(24)), null); verifyZeroInteractions(listener); - _queue.enqueue(createMessage(new Long(25))); + _queue.enqueue(createMessage(new Long(25)), null); verify(listener, atLeastOnce()).notifyClients(eq(NotificationCheck.MESSAGE_COUNT_ALERT), eq(_queue), contains("Maximum count on queue threshold")); } @@ -1064,9 +1146,9 @@ public class SimpleAMQQueueTest extends QpidTestCase { AMQQueue.NotificationListener listener = mock(AMQQueue.NotificationListener.class); - _queue.enqueue(createMessage(new Long(24))); - _queue.enqueue(createMessage(new Long(25))); - _queue.enqueue(createMessage(new Long(26))); + _queue.enqueue(createMessage(new Long(24)), null); + _queue.enqueue(createMessage(new Long(25)), null); + _queue.enqueue(createMessage(new Long(26)), null); _queue.setNotificationListener(listener); _queue.setMaximumMessageCount(2); @@ -1132,7 +1214,7 @@ public class SimpleAMQQueueTest extends QpidTestCase // Put message on queue try { - queue.enqueue(message); + queue.enqueue(message,null); } catch (AMQException e) { @@ -1167,23 +1249,23 @@ public class SimpleAMQQueueTest extends QpidTestCase return entry; } - private List createEntriesList(QueueEntry... entries) + private List createEntriesList(QueueEntry... entries) { - ArrayList entriesList = new ArrayList(); + ArrayList entriesList = new ArrayList(); for (QueueEntry entry : entries) { - entriesList.add(entry); + entriesList.add(entry.getMessage().getMessageHeader().getMessageId()); } return entriesList; } - private void verifyReceivedMessages(List expected, - List delivered) + private void verifyReceivedMessages(List expected, + List delivered) { assertEquals("Consumer did not receive the expected number of messages", expected.size(), delivered.size()); - for (QueueEntry msg : expected) + for (MessageInstance msg : expected) { assertTrue("Consumer did not receive msg: " + msg.getMessage().getMessageNumber(), delivered.contains(msg)); @@ -1195,9 +1277,9 @@ public class SimpleAMQQueueTest extends QpidTestCase return _queue; } - public MockSubscription getSubscription() + public MockConsumer getConsumer() { - return _subscription; + return _consumerTarget; } public Map getArguments() @@ -1213,20 +1295,36 @@ public class SimpleAMQQueueTest extends QpidTestCase protected ServerMessage createMessage(Long id) throws AMQException { + AMQMessageHeader header = mock(AMQMessageHeader.class); + when(header.getMessageId()).thenReturn(String.valueOf(id)); ServerMessage message = mock(ServerMessage.class); when(message.getMessageNumber()).thenReturn(id); + when(message.getMessageHeader()).thenReturn(header); MessageReference ref = mock(MessageReference.class); when(ref.getMessage()).thenReturn(message); - AMQMessageHeader hdr = mock(AMQMessageHeader.class); - when(message.getMessageHeader()).thenReturn(hdr); when(message.newReference()).thenReturn(ref); return message; } + private static class EntryListAddingAction implements Action> + { + private final ArrayList _queueEntries; + + public EntryListAddingAction(final ArrayList queueEntries) + { + _queueEntries = queueEntries; + } + + public void performAction(MessageInstance entry) + { + _queueEntries.add((QueueEntry) entry); + } + } + class TestSimpleQueueEntryListFactory implements QueueEntryListFactory { QueueEntryList _list; diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/AbstractDurableConfigurationStoreTestCase.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/AbstractDurableConfigurationStoreTestCase.java index 3dfe057285..d3ee938586 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/AbstractDurableConfigurationStoreTestCase.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/AbstractDurableConfigurationStoreTestCase.java @@ -464,7 +464,7 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest } @Override - public TransactionLogResource getQueue() + public TransactionLogResource getResource() { return _queue; } @@ -505,7 +505,7 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest { return false; } - if (_queue == null && other.getQueue() != null) + if (_queue == null && other.getResource() != null) { return false; } @@ -513,7 +513,7 @@ public abstract class AbstractDurableConfigurationStoreTestCase extends QpidTest { return false; } - return _queue.getId().equals(other.getQueue().getId()); + return _queue.getId().equals(other.getResource().getId()); } } diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/MessageStoreQuotaEventsTestBase.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/MessageStoreQuotaEventsTestBase.java index 121c380736..7a4f92f0ca 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/MessageStoreQuotaEventsTestBase.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/MessageStoreQuotaEventsTestBase.java @@ -154,6 +154,12 @@ public abstract class MessageStoreQuotaEventsTestBase extends QpidTestCase imple return _transactionResource; } + @Override + public boolean isDurable() + { + return true; + } + private static class TestMessage implements EnqueueableMessage { private final StoredMessage _handle; diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/TestMessageMetaData.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/TestMessageMetaData.java index a52b9f8d14..e14b41b221 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/TestMessageMetaData.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/store/TestMessageMetaData.java @@ -26,7 +26,6 @@ import java.nio.ByteBuffer; import org.apache.qpid.framing.EncodingUtils; import org.apache.qpid.server.plugin.MessageMetaDataType; -import org.apache.qpid.server.store.StorableMessageMetaData; import org.apache.qpid.server.util.ByteBufferOutputStream; public class TestMessageMetaData implements StorableMessageMetaData @@ -72,7 +71,7 @@ public class TestMessageMetaData implements StorableMessageMetaData } @Override - public int writeToBuffer(int offsetInMetaData, ByteBuffer dest) + public int writeToBuffer(ByteBuffer dest) { int oldPosition = dest.position(); try diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java deleted file mode 100644 index 8d1b27e272..0000000000 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java +++ /dev/null @@ -1,583 +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.subscription; - -import org.apache.qpid.AMQException; -import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.server.logging.LogActor; -import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.server.model.Port; -import org.apache.qpid.server.model.Transport; -import org.apache.qpid.server.protocol.AMQConnectionModel; -import org.apache.qpid.server.protocol.AMQSessionModel; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.QueueEntry; -import org.apache.qpid.server.queue.QueueEntry.SubscriptionAcquiredState; -import org.apache.qpid.server.stats.StatisticsCounter; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -public class MockSubscription implements Subscription -{ - - private boolean _closed = false; - private String tag = "mocktag"; - private AMQQueue queue = null; - private StateListener _listener = null; - private volatile AMQQueue.Context _queueContext = null; - private State _state = State.ACTIVE; - private ArrayList messages = new ArrayList(); - private final Lock _stateChangeLock = new ReentrantLock(); - private List _acceptEntries = null; - - private final QueueEntry.SubscriptionAcquiredState _owningState = new QueueEntry.SubscriptionAcquiredState(this); - - private static final AtomicLong idGenerator = new AtomicLong(0); - // Create a simple ID that increments for ever new Subscription - private final long _subscriptionID = idGenerator.getAndIncrement(); - private boolean _isActive = true; - - public MockSubscription() - { - } - - public MockSubscription(List acceptEntries) - { - _acceptEntries = acceptEntries; - } - - public void close() - { - _closed = true; - if (_listener != null) - { - _listener.stateChange(this, _state, State.CLOSED); - } - _state = State.CLOSED; - } - - public String getConsumerName() - { - return tag; - } - - public long getSubscriptionID() - { - return _subscriptionID; - } - - public AMQQueue.Context getQueueContext() - { - return _queueContext; - } - - public SubscriptionAcquiredState getOwningState() - { - return _owningState; - } - - public LogActor getLogActor() - { - return null; //To change body of implemented methods use File | Settings | File Templates. - } - - public boolean isTransient() - { - return false; - } - - public long getBytesOut() - { - return 0; // TODO - Implement - } - - public long getMessagesOut() - { - return 0; // TODO - Implement - } - - public long getUnacknowledgedBytes() - { - return 0; // TODO - Implement - } - - public long getUnacknowledgedMessages() - { - return 0; // TODO - Implement - } - - public AMQQueue getQueue() - { - return queue; - } - - public AMQSessionModel getSessionModel() - { - return new MockSessionModel(); - } - - public boolean trySendLock() - { - return _stateChangeLock.tryLock(); - } - - - public void getSendLock() - { - _stateChangeLock.lock(); - } - - public boolean hasInterest(QueueEntry entry) - { - if(_acceptEntries != null) - { - //simulate selector behaviour, only signal - //interest in the dictated queue entries - return _acceptEntries.contains(entry); - } - - return true; - } - - public boolean isActive() - { - return _isActive ; - } - - public void set(String key, Object value) - { - } - - public Object get(String key) - { - return null; - } - - public boolean isAutoClose() - { - return false; - } - - public boolean isClosed() - { - return _closed; - } - - public boolean acquires() - { - return true; - } - - public boolean seesRequeues() - { - return true; - } - - public boolean isSuspended() - { - return false; - } - - public void queueDeleted(AMQQueue queue) - { - } - - public void releaseSendLock() - { - _stateChangeLock.unlock(); - } - - public void onDequeue(QueueEntry queueEntry) - { - } - - public void restoreCredit(QueueEntry queueEntry) - { - } - - public void releaseQueueEntry(QueueEntry queueEntry) - { - } - - public void send(QueueEntry entry, boolean batch) throws AMQException - { - if (messages.contains(entry)) - { - entry.setRedelivered(); - } - messages.add(entry); - } - - public void flushBatched() - { - - } - - public void setQueueContext(AMQQueue.Context queueContext) - { - _queueContext = queueContext; - } - - public void setQueue(AMQQueue queue, boolean exclusive) - { - this.queue = queue; - } - - public void setNoLocal(boolean noLocal) - { - } - - public void setStateListener(StateListener listener) - { - this._listener = listener; - } - - public State getState() - { - return _state; - } - - public boolean wouldSuspend(QueueEntry msg) - { - return false; - } - - public ArrayList getMessages() - { - return messages; - } - - public boolean isSessionTransactional() - { - return false; - } - - public void queueEmpty() throws AMQException - { - } - - public void setActive(final boolean isActive) - { - _isActive = isActive; - } - - private static class MockSessionModel implements AMQSessionModel - { - private final UUID _id = UUID.randomUUID(); - - @Override - public UUID getId() - { - return _id; - } - - @Override - public AMQConnectionModel getConnectionModel() - { - return new MockConnectionModel(); - } - - @Override - public String getClientID() - { - return null; - } - - @Override - public void close() throws AMQException - { - } - - @Override - public LogSubject getLogSubject() - { - return null; - } - - @Override - public void checkTransactionStatus(long openWarn, long openClose, - long idleWarn, long idleClose) throws AMQException - { - } - - @Override - public void block(AMQQueue queue) - { - } - - @Override - public void unblock(AMQQueue queue) - { - } - - @Override - public void block() - { - } - - @Override - public void unblock() - { - } - - @Override - public boolean getBlocking() - { - return false; - } - - @Override - public Object getConnectionReference() - { - return this; - } - - @Override - public int getUnacknowledgedMessageCount() - { - return 0; - } - - @Override - public Long getTxnCount() - { - return null; - } - - @Override - public Long getTxnStart() - { - return null; - } - - @Override - public Long getTxnCommits() - { - return null; - } - - @Override - public Long getTxnRejects() - { - return null; - } - - @Override - public int getChannelId() - { - return 0; - } - - @Override - public int getConsumerCount() - { - return 0; - } - - @Override - public int compareTo(AMQSessionModel o) - { - return getId().compareTo(o.getId()); - } - - @Override - public void close(AMQConstant cause, String message) throws AMQException - { - } - } - - private static class MockConnectionModel implements AMQConnectionModel - { - @Override - public void initialiseStatistics() - { - } - - @Override - public void registerMessageReceived(long messageSize, long timestamp) - { - } - - @Override - public void registerMessageDelivered(long messageSize) - { - } - - @Override - public StatisticsCounter getMessageDeliveryStatistics() - { - return null; - } - - @Override - public StatisticsCounter getMessageReceiptStatistics() - { - return null; - } - - @Override - public StatisticsCounter getDataDeliveryStatistics() - { - return null; - } - - @Override - public StatisticsCounter getDataReceiptStatistics() - { - return null; - } - - @Override - public void resetStatistics() - { - - } - - @Override - public void close(AMQConstant cause, String message) - throws AMQException - { - } - - @Override - public void closeSession(AMQSessionModel session, AMQConstant cause, - String message) throws AMQException - { - } - - @Override - public long getConnectionId() - { - return 0; - } - - @Override - public List getSessionModels() - { - return null; - } - - @Override - public void block() - { - } - - @Override - public void unblock() - { - } - - @Override - public LogSubject getLogSubject() - { - return null; - } - - @Override - public String getUserName() - { - return null; - } - - @Override - public boolean isSessionNameUnique(byte[] name) - { - return false; - } - - @Override - public String getRemoteAddressString() - { - return "remoteAddress:1234"; - } - - @Override - public String getClientId() - { - return null; - } - - @Override - public String getClientVersion() - { - return null; - } - - @Override - public String getClientProduct() - { - return null; - } - - @Override - public String getPrincipalAsString() - { - return null; - } - - @Override - public long getSessionCountLimit() - { - return 0; - } - - @Override - public long getLastIoTime() - { - return 0; - } - - @Override - public Port getPort() - { - return null; - } - - @Override - public Transport getTransport() - { - return null; - } - - @Override - public void stop() - { - } - - @Override - public boolean isStopped() - { - return false; - } - - @Override - public String getVirtualHostName() - { - return null; - } - } -} diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/subscription/SubscriptionListTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/subscription/SubscriptionListTest.java deleted file mode 100644 index cd5b178464..0000000000 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/subscription/SubscriptionListTest.java +++ /dev/null @@ -1,429 +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.subscription; - -import org.apache.qpid.server.subscription.SubscriptionList.SubscriptionNode; -import org.apache.qpid.server.subscription.SubscriptionList.SubscriptionNodeIterator; -import org.apache.qpid.test.utils.QpidTestCase; - -public class SubscriptionListTest extends QpidTestCase -{ - private SubscriptionList _subList; - private MockSubscription _sub1; - private MockSubscription _sub2; - private MockSubscription _sub3; - private SubscriptionNode _node; - - protected void setUp() - { - _subList = new SubscriptionList(); - - _sub1 = new MockSubscription(); - _sub2 = new MockSubscription(); - _sub3 = new MockSubscription(); - - _subList.add(_sub1); - _subList.add(_sub2); - _subList.add(_sub3); - - _node = _subList.getHead(); - } - - /** - * Test that if the first (non-head) node in the list is deleted (but is still present), - * it is not returned when searching through the list for the next viable node, and the - * subsequent viable node is returned instead. - */ - public void testFindNextSkipsFirstDeletedNode() - { - assertTrue("Deleting subscription node should have succeeded", - getNodeForSubscription(_subList, _sub1).delete()); - - assertNotNull("Returned node should not be null", _node = _node.findNext()); - assertEquals("Should have returned node for 2nd subscription", _sub2, _node.getSubscription()); - - assertNotNull("Returned node should not be null", _node = _node.findNext()); - assertEquals("Should have returned node for 3rd subscription", _sub3, _node.getSubscription()); - } - - /** - * Test that if a central node in the list is deleted (but is still present), - * it is not returned when searching through the list for the next viable node, - * and the subsequent viable node is returned instead. - */ - public void testFindNextSkipsCentralDeletedNode() - { - assertNotNull("Returned node should not be null", _node = _node.findNext()); - - assertTrue("Deleting subscription node should have succeeded", - getNodeForSubscription(_subList, _sub2).delete()); - - assertNotNull("Returned node should not be null", _node = _node.findNext()); - assertEquals("Should have returned node for 3rd subscription", _sub3, _node.getSubscription()); - } - - /** - * Test that if the last node in the list is deleted (but is still present), - * it is not returned when searching through the list for the next viable node, - * and null is returned instead. - */ - public void testFindNextSkipsLastDeletedNode() - { - assertNotNull("Returned node should not be null", _node = _node.findNext()); - assertEquals("Should have returned node for 1st subscription", _sub1, _node.getSubscription()); - - assertNotNull("Returned node should not be null", _node = _node.findNext()); - assertEquals("Should have returned node for 2nd subscription", _sub2, _node.getSubscription()); - - assertTrue("Deleting subscription node should have succeeded", - getNodeForSubscription(_subList, _sub3).delete()); - - assertNull("Returned node should be null", _node = _node.findNext()); - } - - /** - * Test that if multiple nodes in the list are deleted (but still present), they - * are not returned when searching through the list for the next viable node, - * and the subsequent viable node is returned instead. - */ - public void testFindNextSkipsMultipleDeletedNode() - { - assertTrue("Deleting subscription node should have succeeded", - getNodeForSubscription(_subList, _sub1).delete()); - assertTrue("Deleting subscription node should have succeeded", - getNodeForSubscription(_subList, _sub2).delete()); - - assertNotNull("Returned node should not be null", _node = _node.findNext()); - assertEquals("Should have returned node for 3rd subscription", _sub3, _node.getSubscription()); - } - - /** - * Test that if a node in the list is marked 'deleted' it is still present in the list - * until actually removed. counter-test to verify above testing of getNext() method. - */ - public void testDeletedNodeStillPresent() - { - assertTrue("Deleting subscription node should have succeeded", - getNodeForSubscription(_subList, _sub1).delete()); - - assertNotNull("Node marked deleted should still be present", getNodeForSubscription(_subList, _sub1)); - assertEquals("All 3 nodes are still expected to be present", 3, countNodes(_subList)); - } - - /** - * Traverses the list nodes in a non-mutating fashion, returning the first node which matches the given - * Subscription, or null if none is found. - */ - private SubscriptionNode getNodeForSubscription(final SubscriptionList list, final Subscription sub) - { - SubscriptionNode node = list.getHead(); - while (node != null && node.getSubscription() != sub) - { - node = node.nextNode(); - } - - return node; - } - - /** - * Counts the number of (non-head) nodes in the list. - */ - private int countNodes(final SubscriptionList list) - { - SubscriptionNode node = list.getHead(); - int count; - for(count = -1; node != null; count++) - { - node = node.nextNode(); - } - - return count; - } - - /** - * Tests that the head is returned as expected, and isn't the node for the first subscription. - */ - public void testGetHead() - { - assertNotNull("List head should be non null", _node); - assertNotSame("Head should not be node for first subscription", - _node, getNodeForSubscription(_subList, _sub1)); - } - - /** - * Tests that the size is returned correctly in the face of additions and removals. - */ - public void testGetSize() - { - SubscriptionList subList = new SubscriptionList(); - - assertEquals("Unexpected size result", 0, subList.size()); - - Subscription sub1 = new MockSubscription(); - Subscription sub2 = new MockSubscription(); - Subscription sub3 = new MockSubscription(); - - subList.add(sub1); - assertEquals("Unexpected size result", 1, subList.size()); - - subList.add(sub2); - assertEquals("Unexpected size result", 2, subList.size()); - - subList.add(sub3); - assertEquals("Unexpected size result", 3, subList.size()); - - assertTrue("Removing subscription from list should have succeeded", subList.remove(sub1)); - assertEquals("Unexpected size result", 2, subList.size()); - - assertTrue("Removing subscription from list should have succeeded", subList.remove(sub2)); - assertEquals("Unexpected size result", 1, subList.size()); - - assertTrue("Removing subscription from list should have succeeded", subList.remove(sub3)); - assertEquals("Unexpected size result", 0, subList.size()); - } - - /** - * Test that if the first (non-head) node in the list is removed it is no longer - * present in the node structure of the list at all. - */ - public void testRemoveFirstNode() - { - assertNotNull("Should have been a node present for the subscription", getNodeForSubscription(_subList, _sub1)); - assertTrue("Removing subscription node should have succeeded", _subList.remove(_sub1)); - assertNull("Should not have been a node present for the removed subscription", getNodeForSubscription(_subList, _sub1)); - assertEquals("Unexpected number of nodes", 2, countNodes(_subList)); - assertNotNull("Should have been a node present for the subscription", getNodeForSubscription(_subList, _sub2)); - assertNotNull("Should have been a node present for the subscription", getNodeForSubscription(_subList, _sub3)); - } - - /** - * Test that if a central node in the list is removed it is no longer - * present in the node structure of the list at all. - */ - public void testRemoveCentralNode() - { - assertNotNull("Should have been a node present for the subscription", getNodeForSubscription(_subList, _sub2)); - assertTrue("Removing subscription node should have succeeded", _subList.remove(_sub2)); - assertNull("Should not have been a node present for the removed subscription", getNodeForSubscription(_subList, _sub2)); - assertEquals("Unexpected number of nodes", 2, countNodes(_subList)); - assertNotNull("Should have been a node present for the subscription", getNodeForSubscription(_subList, _sub1)); - assertNotNull("Should have been a node present for the subscription", getNodeForSubscription(_subList, _sub3)); - } - - /** - * Test that if the subscription contained in the last node of the list is removed - * it is no longer present in the node structure of the list at all. However, - * as the last node in the structure can't actually be removed a dummy will instead - * be present. - */ - public void testRemoveLastNode() - { - assertNotNull("Should have been a node present for the subscription", getNodeForSubscription(_subList, _sub3)); - assertTrue("Removing subscription node should have succeeded", _subList.remove(_sub3)); - assertNull("Should not have been a node present for the removed subscription", getNodeForSubscription(_subList, _sub3)); - - //We actually expect 3 nodes to remain this time, because the last node cant be removed for thread safety reasons, - //however a dummy final node can be used as substitute to allow removal of the subscription node. - assertEquals("Unexpected number of nodes", 2 + 1, countNodes(_subList)); - assertNotNull("Should have been a node present for the subscription", getNodeForSubscription(_subList, _sub1)); - assertNotNull("Should have been a node present for the subscription", getNodeForSubscription(_subList, _sub2)); - } - - /** - * Test that if the subscription not contained in the list is requested to be removed - * that the removal fails - */ - public void testRemoveNonexistentNode() - { - Subscription sub4 = new MockSubscription(); - assertNull("Should not have been a node present for the subscription", getNodeForSubscription(_subList, sub4)); - assertFalse("Removing subscription node should not have succeeded", _subList.remove(sub4)); - assertEquals("Unexpected number of nodes", 3, countNodes(_subList)); - } - - /** - * Test that if a subscription node which occurs later in the main list than the marked node is - * removed from the list after the marked node is also removed, then the marker node doesn't - * serve to retain the subsequent nodes in the list structure (and thus memory) despite their - * removal. - */ - public void testDeletedMarkedNodeDoesntLeakSubsequentlyDeletedNodes() - { - //get the nodes out the list for the 1st and 3rd subscriptions - SubscriptionNode sub1Node = getNodeForSubscription(_subList, _sub1); - assertNotNull("Should have been a node present for the subscription", sub1Node); - SubscriptionNode sub3Node = getNodeForSubscription(_subList, _sub3); - assertNotNull("Should have been a node present for the subscription", sub3Node); - - //mark the first subscription node - assertTrue("should have succeeded in updating the marked node", - _subList.updateMarkedNode(_subList.getMarkedNode(), sub1Node)); - - //remove the 1st subscription from the list - assertTrue("Removing subscription node should have succeeded", _subList.remove(_sub1)); - //verify the 1st subscription is no longer the marker node (replaced by a dummy), or in the main list structure - assertNotSame("Unexpected marker node", sub1Node, _subList.getMarkedNode()); - assertNull("Should not have been a node present in the list structure for the marked-but-removed sub1 node", - getNodeForSubscription(_subList, _sub1)); - - //remove the 2nd subscription from the list - assertTrue("Removing subscription node should have succeeded", _subList.remove(_sub2)); - - //verify the marker node isn't leaking subsequently removed nodes, by ensuring the very next node - //in its list structure is now the 3rd subscription (since the 2nd was removed too) - assertEquals("Unexpected next node", sub3Node, _subList.getMarkedNode().nextNode()); - - //remove the 3rd and final/tail subscription - assertTrue("Removing subscription node should have succeeded", _subList.remove(_sub3)); - - //verify the marker node isn't leaking subsequently removed nodes, by ensuring the very next node - //in its list structure is now the dummy tail (since the 3rd subscription was removed, and a dummy - //tail was inserted) and NOT the 3rd sub node. - assertNotSame("Unexpected next node", sub3Node, _subList.getMarkedNode().nextNode()); - assertTrue("Unexpected next node", _subList.getMarkedNode().nextNode().isDeleted()); - assertNull("Next non-deleted node from the marker should now be the list end, i.e. null", _subList.getMarkedNode().findNext()); - } - - /** - * Test that the marked node 'findNext' behaviour is as expected after a subscription is added - * to the list following the tail subscription node being removed while it is the marked node. - * That is, that the new subscriptions node is returned by getMarkedNode().findNext(). - */ - public void testMarkedNodeFindsNewSubscriptionAfterRemovingTailWhilstMarked() - { - //get the node out the list for the 3rd subscription - SubscriptionNode sub3Node = getNodeForSubscription(_subList, _sub3); - assertNotNull("Should have been a node present for the subscription", sub3Node); - - //mark the 3rd subscription node - assertTrue("should have succeeded in updating the marked node", - _subList.updateMarkedNode(_subList.getMarkedNode(), sub3Node)); - - //verify calling findNext on the marked node returns null, i.e. the end of the list has been reached - assertEquals("Unexpected node after marked node", null, _subList.getMarkedNode().findNext()); - - //remove the 3rd(marked) subscription from the list - assertTrue("Removing subscription node should have succeeded", _subList.remove(_sub3)); - - //add a new 4th subscription to the list - Subscription sub4 = new MockSubscription(); - _subList.add(sub4); - - //get the node out the list for the 4th subscription - SubscriptionNode sub4Node = getNodeForSubscription(_subList, sub4); - assertNotNull("Should have been a node present for the subscription", sub4Node); - - //verify the marked node (which is now a dummy substitute for the 3rd subscription) returns - //the 4th subscriptions node as the next non-deleted node. - assertEquals("Unexpected next node", sub4Node, _subList.getMarkedNode().findNext()); - } - - /** - * Test that setting the marked node to null doesn't cause problems during remove operations - */ - public void testRemoveWithNullMarkedNode() - { - //set the marker to null - assertTrue("should have succeeded in updating the marked node", - _subList.updateMarkedNode(_subList.getMarkedNode(), null)); - - //remove the 1st subscription from the main list - assertTrue("Removing subscription node should have succeeded", _subList.remove(_sub1)); - - //verify the 1st subscription is no longer in the main list structure - assertNull("Should not have been a node present in the main list structure for sub1", - getNodeForSubscription(_subList, _sub1)); - assertEquals("Unexpected number of nodes", 2, countNodes(_subList)); - } - - /** - * Tests that after the first (non-head) node of the list is marked deleted but has not - * yet been removed, the iterator still skips it. - */ - public void testIteratorSkipsFirstDeletedNode() - { - //'delete' but don't remove the node for the 1st subscription - assertTrue("Deleting subscription node should have succeeded", - getNodeForSubscription(_subList, _sub1).delete()); - assertNotNull("Should still have been a node present for the deleted subscription", - getNodeForSubscription(_subList, _sub1)); - - SubscriptionNodeIterator iter = _subList.iterator(); - - //verify the iterator returns the 2nd subscriptions node - assertTrue("Iterator should have been able to advance", iter.advance()); - assertEquals("Iterator returned unexpected SubscriptionNode", _sub2, iter.getNode().getSubscription()); - - //verify the iterator returns the 3rd subscriptions node and not the 2nd. - assertTrue("Iterator should have been able to advance", iter.advance()); - assertEquals("Iterator returned unexpected SubscriptionNode", _sub3, iter.getNode().getSubscription()); - } - - /** - * Tests that after a central node of the list is marked deleted but has not yet been removed, - * the iterator still skips it. - */ - public void testIteratorSkipsCentralDeletedNode() - { - //'delete' but don't remove the node for the 2nd subscription - assertTrue("Deleting subscription node should have succeeded", - getNodeForSubscription(_subList, _sub2).delete()); - assertNotNull("Should still have been a node present for the deleted subscription", - getNodeForSubscription(_subList, _sub2)); - - SubscriptionNodeIterator iter = _subList.iterator(); - - //verify the iterator returns the 1st subscriptions node - assertTrue("Iterator should have been able to advance", iter.advance()); - assertEquals("Iterator returned unexpected SubscriptionNode", _sub1, iter.getNode().getSubscription()); - - //verify the iterator returns the 3rd subscriptions node and not the 2nd. - assertTrue("Iterator should have been able to advance", iter.advance()); - assertEquals("Iterator returned unexpected SubscriptionNode", _sub3, iter.getNode().getSubscription()); - } - - /** - * Tests that after the last node of the list is marked deleted but has not yet been removed, - * the iterator still skips it. - */ - public void testIteratorSkipsDeletedFinalNode() - { - //'delete' but don't remove the node for the 3rd subscription - assertTrue("Deleting subscription node should have succeeded", - getNodeForSubscription(_subList, _sub3).delete()); - assertNotNull("Should still have been a node present for the deleted 3rd subscription", - getNodeForSubscription(_subList, _sub3)); - - SubscriptionNodeIterator iter = _subList.iterator(); - - //verify the iterator returns the 1st subscriptions node - assertTrue("Iterator should have been able to advance", iter.advance()); - assertEquals("Iterator returned unexpected SubscriptionNode", _sub1, iter.getNode().getSubscription()); - - //verify the iterator returns the 2nd subscriptions node - assertTrue("Iterator should have been able to advance", iter.advance()); - assertEquals("Iterator returned unexpected SubscriptionNode", _sub2, iter.getNode().getSubscription()); - - //verify the iterator can no longer advance and does not return a subscription node - assertFalse("Iterator should not have been able to advance", iter.advance()); - assertEquals("Iterator returned unexpected SubscriptionNode", null, iter.getNode()); - } -} diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/txn/AutoCommitTransactionTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/txn/AutoCommitTransactionTest.java index 3c66a4c94b..11b9bbe1b4 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/txn/AutoCommitTransactionTest.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/txn/AutoCommitTransactionTest.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.txn; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.MockAMQQueue; @@ -47,7 +48,7 @@ public class AutoCommitTransactionTest extends QpidTestCase private MessageStore _transactionLog; private AMQQueue _queue; private List _queues; - private Collection _queueEntries; + private Collection _queueEntries; private ServerMessage _message; private MockAction _action; private MockStoreTransaction _storeTransaction; @@ -373,9 +374,9 @@ public class AutoCommitTransactionTest extends QpidTestCase assertFalse("Rollback action must be fired", _action.isRollbackActionFired()); } - private Collection createTestQueueEntries(boolean[] queueDurableFlags, boolean[] messagePersistentFlags) + private Collection createTestQueueEntries(boolean[] queueDurableFlags, boolean[] messagePersistentFlags) { - Collection queueEntries = new ArrayList(); + Collection queueEntries = new ArrayList(); assertTrue("Boolean arrays must be the same length", queueDurableFlags.length == messagePersistentFlags.length); diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/txn/LocalTransactionTest.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/txn/LocalTransactionTest.java index f3f5e00346..80e794e0ff 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/txn/LocalTransactionTest.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/txn/LocalTransactionTest.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.txn; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.MockAMQQueue; @@ -46,7 +47,7 @@ public class LocalTransactionTest extends QpidTestCase private AMQQueue _queue; private List _queues; - private Collection _queueEntries; + private Collection _queueEntries; private ServerMessage _message; private MockAction _action1; private MockAction _action2; @@ -597,9 +598,9 @@ public class LocalTransactionTest extends QpidTestCase assertEquals("Transaction update time should be reset after rollback", 0, _transaction.getTransactionUpdateTime()); } - private Collection createTestQueueEntries(boolean[] queueDurableFlags, boolean[] messagePersistentFlags) + private Collection createTestQueueEntries(boolean[] queueDurableFlags, boolean[] messagePersistentFlags) { - Collection queueEntries = new ArrayList(); + Collection queueEntries = new ArrayList(); assertTrue("Boolean arrays must be the same length", queueDurableFlags.length == messagePersistentFlags.length); diff --git a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java index 1ca7ff1b65..832b89c81a 100644 --- a/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java +++ b/qpid/java/broker-core/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java @@ -27,6 +27,8 @@ import org.apache.qpid.AMQException; import org.apache.qpid.server.configuration.VirtualHostConfiguration; import org.apache.qpid.server.connection.IConnectionRegistry; import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.message.MessageDestination; +import org.apache.qpid.server.message.MessageSource; import org.apache.qpid.server.plugin.ExchangeType; import org.apache.qpid.server.protocol.LinkRegistry; import org.apache.qpid.server.queue.AMQQueue; @@ -126,6 +128,12 @@ public class MockVirtualHost implements VirtualHost return null; } + @Override + public MessageSource getMessageSource(final String name) + { + return null; + } + @Override public AMQQueue getQueue(UUID id) { @@ -173,6 +181,12 @@ public class MockVirtualHost implements VirtualHost { } + @Override + public MessageDestination getMessageDestination(final String name) + { + return null; + } + @Override public Exchange getExchange(String name) { diff --git a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ConsumerTarget_0_10.java b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ConsumerTarget_0_10.java new file mode 100644 index 0000000000..6ad9de22cb --- /dev/null +++ b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ConsumerTarget_0_10.java @@ -0,0 +1,580 @@ +/* + * + * 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.protocol.v0_10; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.flow.FlowCreditManager; +import org.apache.qpid.server.logging.LogActor; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.messages.ChannelMessages; +import org.apache.qpid.server.message.MessageInstance; +import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.plugin.MessageConverter; +import org.apache.qpid.server.protocol.MessageConverterRegistry; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.store.TransactionLogResource; +import org.apache.qpid.server.consumer.AbstractConsumerTarget; +import org.apache.qpid.server.consumer.Consumer; +import org.apache.qpid.server.txn.AutoCommitTransaction; +import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.util.Action; +import org.apache.qpid.transport.*; + +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; + +public class ConsumerTarget_0_10 extends AbstractConsumerTarget implements FlowCreditManager.FlowCreditManagerListener +{ + + private static final Option[] BATCHED = new Option[] { Option.BATCH }; + + private final AtomicBoolean _deleted = new AtomicBoolean(false); + private final String _name; + + + private FlowCreditManager_0_10 _creditManager; + + private final MessageAcceptMode _acceptMode; + private final MessageAcquireMode _acquireMode; + private MessageFlowMode _flowMode; + private final ServerSession _session; + private final AtomicBoolean _stopped = new AtomicBoolean(true); + + private final AtomicLong _unacknowledgedCount = new AtomicLong(0); + private final AtomicLong _unacknowledgedBytes = new AtomicLong(0); + + private final Map _arguments; + private int _deferredMessageCredit; + private long _deferredSizeCredit; + private Consumer _consumer; + + + public ConsumerTarget_0_10(ServerSession session, + String name, + MessageAcceptMode acceptMode, + MessageAcquireMode acquireMode, + MessageFlowMode flowMode, + FlowCreditManager_0_10 creditManager, + Map arguments) + { + super(State.SUSPENDED); + _session = session; + _postIdSettingAction = new AddMessageDispositionListenerAction(session); + _acceptMode = acceptMode; + _acquireMode = acquireMode; + _creditManager = creditManager; + _flowMode = flowMode; + _creditManager.addStateListener(this); + _arguments = arguments == null ? Collections. emptyMap() : + Collections. unmodifiableMap(arguments); + _name = name; + } + + public Consumer getConsumer() + { + return _consumer; + } + + public boolean isSuspended() + { + return getState()!=State.ACTIVE || _deleted.get() || _session.isClosing() || _session.getConnectionModel().isStopped(); // TODO check for Session suspension + } + + public boolean close() + { + boolean closed = false; + State state = getState(); + + getConsumer().getSendLock(); + try + { + while(!closed && state != State.CLOSED) + { + closed = updateState(state, State.CLOSED); + if(!closed) + { + state = getState(); + } + } + _creditManager.removeListener(this); + } + finally + { + getConsumer().releaseSendLock(); + } + + return closed; + + } + + public void creditStateChanged(boolean hasCredit) + { + + if(hasCredit) + { + if(!updateState(State.SUSPENDED, State.ACTIVE)) + { + // this is a hack to get round the issue of increasing bytes credit + getStateListener().stateChanged(this, State.ACTIVE, State.ACTIVE); + } + } + else + { + updateState(State.ACTIVE, State.SUSPENDED); + } + } + + public String getName() + { + return _name; + } + + + public static class AddMessageDispositionListenerAction implements Runnable + { + private MessageTransfer _xfr; + private ServerSession.MessageDispositionChangeListener _action; + private ServerSession _session; + + public AddMessageDispositionListenerAction(ServerSession session) + { + _session = session; + } + + public void setXfr(MessageTransfer xfr) + { + _xfr = xfr; + } + + public void setAction(ServerSession.MessageDispositionChangeListener action) + { + _action = action; + } + + public void run() + { + if(_action != null) + { + _session.onMessageDispositionChange(_xfr, _action); + } + } + } + + private final AddMessageDispositionListenerAction _postIdSettingAction; + + public void send(final MessageInstance entry, boolean batch) throws AMQException + { + ServerMessage serverMsg = entry.getMessage(); + + + MessageTransfer xfr; + + DeliveryProperties deliveryProps; + MessageProperties messageProps = null; + + MessageTransferMessage msg; + + if(serverMsg instanceof MessageTransferMessage) + { + + msg = (MessageTransferMessage) serverMsg; + + } + else + { + MessageConverter converter = + MessageConverterRegistry.getConverter(serverMsg.getClass(), MessageTransferMessage.class); + + + msg = (MessageTransferMessage) converter.convert(serverMsg, _session.getVirtualHost()); + } + DeliveryProperties origDeliveryProps = msg.getHeader() == null ? null : msg.getHeader().getDeliveryProperties(); + messageProps = msg.getHeader() == null ? null : msg.getHeader().getMessageProperties(); + + deliveryProps = new DeliveryProperties(); + if(origDeliveryProps != null) + { + if(origDeliveryProps.hasDeliveryMode()) + { + deliveryProps.setDeliveryMode(origDeliveryProps.getDeliveryMode()); + } + if(origDeliveryProps.hasExchange()) + { + deliveryProps.setExchange(origDeliveryProps.getExchange()); + } + if(origDeliveryProps.hasExpiration()) + { + deliveryProps.setExpiration(origDeliveryProps.getExpiration()); + } + if(origDeliveryProps.hasPriority()) + { + deliveryProps.setPriority(origDeliveryProps.getPriority()); + } + if(origDeliveryProps.hasRoutingKey()) + { + deliveryProps.setRoutingKey(origDeliveryProps.getRoutingKey()); + } + if(origDeliveryProps.hasTimestamp()) + { + deliveryProps.setTimestamp(origDeliveryProps.getTimestamp()); + } + if(origDeliveryProps.hasTtl()) + { + deliveryProps.setTtl(origDeliveryProps.getTtl()); + } + + + } + + deliveryProps.setRedelivered(entry.isRedelivered()); + + Header header = new Header(deliveryProps, messageProps, msg.getHeader() == null ? null : msg.getHeader().getNonStandardProperties()); + + + xfr = batch ? new MessageTransfer(getConsumer().getName(),_acceptMode,_acquireMode,header,msg.getBody(), BATCHED) + : new MessageTransfer(getConsumer().getName(),_acceptMode,_acquireMode,header,msg.getBody()); + + if(_acceptMode == MessageAcceptMode.NONE && _acquireMode != MessageAcquireMode.PRE_ACQUIRED) + { + xfr.setCompletionListener(new MessageAcceptCompletionListener(this, _session, entry, _flowMode == MessageFlowMode.WINDOW)); + } + else if(_flowMode == MessageFlowMode.WINDOW) + { + xfr.setCompletionListener(new Method.CompletionListener() + { + public void onComplete(Method method) + { + deferredAddCredit(1, entry.getMessage().getSize()); + } + }); + } + + + _postIdSettingAction.setXfr(xfr); + if(_acceptMode == MessageAcceptMode.EXPLICIT) + { + _postIdSettingAction.setAction(new ExplicitAcceptDispositionChangeListener(entry, this)); + } + else if(_acquireMode != MessageAcquireMode.PRE_ACQUIRED) + { + _postIdSettingAction.setAction(new ImplicitAcceptDispositionChangeListener(entry, this)); + } + else + { + _postIdSettingAction.setAction(null); + } + + + _session.sendMessage(xfr, _postIdSettingAction); + entry.incrementDeliveryCount(); + if(_acceptMode == MessageAcceptMode.NONE && _acquireMode == MessageAcquireMode.PRE_ACQUIRED) + { + forceDequeue(entry, false); + } + else if(_acquireMode == MessageAcquireMode.PRE_ACQUIRED) + { + recordUnacknowledged(entry); + } + + } + + void recordUnacknowledged(MessageInstance entry) + { + _unacknowledgedCount.incrementAndGet(); + _unacknowledgedBytes.addAndGet(entry.getMessage().getSize()); + } + + private void deferredAddCredit(final int deferredMessageCredit, final long deferredSizeCredit) + { + _deferredMessageCredit += deferredMessageCredit; + _deferredSizeCredit += deferredSizeCredit; + + } + + public void flushCreditState(boolean strict) + { + if(strict || !isSuspended() || _deferredMessageCredit >= 200 + || !(_creditManager instanceof WindowCreditManager) + || ((WindowCreditManager)_creditManager).getMessageCreditLimit() < 400 ) + { + _creditManager.restoreCredit(_deferredMessageCredit, _deferredSizeCredit); + _deferredMessageCredit = 0; + _deferredSizeCredit = 0l; + } + } + + private void forceDequeue(final MessageInstance entry, final boolean restoreCredit) + { + AutoCommitTransaction dequeueTxn = new AutoCommitTransaction(_session.getVirtualHost().getMessageStore()); + dequeueTxn.dequeue(entry.getOwningResource(), entry.getMessage(), + new ServerTransaction.Action() + { + public void postCommit() + { + if (restoreCredit) + { + restoreCredit(entry.getMessage()); + } + entry.delete(); + } + + public void onRollback() + { + + } + }); + } + + void reject(final MessageInstance entry) + { + entry.setRedelivered(); + entry.routeToAlternate(null, null); + if(entry.isAcquiredBy(getConsumer())) + { + entry.delete(); + } + } + + void release(final MessageInstance entry, final boolean setRedelivered) + { + if (setRedelivered) + { + entry.setRedelivered(); + } + + if (getSessionModel().isClosing() || !setRedelivered) + { + entry.decrementDeliveryCount(); + } + + if (isMaxDeliveryLimitReached(entry)) + { + sendToDLQOrDiscard(entry); + } + else + { + entry.release(); + } + } + + protected void sendToDLQOrDiscard(MessageInstance entry) + { + final LogActor logActor = CurrentActor.get(); + final ServerMessage msg = entry.getMessage(); + + int requeues = entry.routeToAlternate(new Action() + { + @Override + public void performAction(final MessageInstance requeueEntry) + { + logActor.message( ChannelMessages.DEADLETTERMSG(msg.getMessageNumber(), + requeueEntry.getOwningResource().getName())); + } + }, null); + + if (requeues == 0) + { + TransactionLogResource owningResource = entry.getOwningResource(); + if(owningResource instanceof AMQQueue) + { + final AMQQueue queue = (AMQQueue)owningResource; + final Exchange alternateExchange = queue.getAlternateExchange(); + + if(alternateExchange != null) + { + logActor.message( ChannelMessages.DISCARDMSG_NOROUTE(msg.getMessageNumber(), + alternateExchange.getName())); + } + else + { + logActor.message(ChannelMessages.DISCARDMSG_NOALTEXCH(msg.getMessageNumber(), + queue.getName(), + msg.getRoutingKey())); + } + } + } + } + + private boolean isMaxDeliveryLimitReached(MessageInstance entry) + { + final int maxDeliveryLimit = entry.getMaximumDeliveryCount(); + return (maxDeliveryLimit > 0 && entry.getDeliveryCount() >= maxDeliveryLimit); + } + + public void queueDeleted() + { + _deleted.set(true); + } + + public boolean allocateCredit(ServerMessage message) + { + return _creditManager.useCreditForMessage(message.getSize()); + } + + public void restoreCredit(ServerMessage message) + { + _creditManager.restoreCredit(1, message.getSize()); + } + + public FlowCreditManager_0_10 getCreditManager() + { + return _creditManager; + } + + + public void stop() + { + try + { + getConsumer().getSendLock(); + + updateState(State.ACTIVE, State.SUSPENDED); + _stopped.set(true); + FlowCreditManager_0_10 creditManager = getCreditManager(); + creditManager.clearCredit(); + } + finally + { + getConsumer().releaseSendLock(); + } + } + + public void addCredit(MessageCreditUnit unit, long value) + { + FlowCreditManager_0_10 creditManager = getCreditManager(); + + switch (unit) + { + case MESSAGE: + + creditManager.addCredit(value, 0L); + break; + case BYTE: + creditManager.addCredit(0l, value); + break; + } + + _stopped.set(false); + + if(creditManager.hasCredit()) + { + updateState(State.SUSPENDED, State.ACTIVE); + } + + } + + public void setFlowMode(MessageFlowMode flowMode) + { + + + _creditManager.removeListener(this); + + switch(flowMode) + { + case CREDIT: + _creditManager = new CreditCreditManager(0l,0l); + break; + case WINDOW: + _creditManager = new WindowCreditManager(0l,0l); + break; + default: + throw new RuntimeException("Unknown message flow mode: " + flowMode); + } + _flowMode = flowMode; + updateState(State.ACTIVE, State.SUSPENDED); + + _creditManager.addStateListener(this); + + } + + public boolean isStopped() + { + return _stopped.get(); + } + + public void acknowledge(MessageInstance entry) + { + // TODO Fix Store Context / cleanup + if(entry.isAcquiredBy(getConsumer())) + { + _unacknowledgedBytes.addAndGet(-entry.getMessage().getSize()); + _unacknowledgedCount.decrementAndGet(); + entry.delete(); + } + } + + public void flush() throws AMQException + { + flushCreditState(true); + getConsumer().flush(); + stop(); + } + + public ServerSession getSessionModel() + { + return _session; + } + + public boolean isDurable() + { + return false; + } + + public Map getArguments() + { + return _arguments; + } + + public void queueEmpty() + { + } + + public void flushBatched() + { + _session.getConnection().flush(); + } + + + @Override + public void consumerAdded(final Consumer sub) + { + _consumer = sub; + } + + @Override + public void consumerRemoved(final Consumer sub) + { + } + + public long getUnacknowledgedBytes() + { + return _unacknowledgedBytes.longValue(); + } + + public long getUnacknowledgedMessages() + { + return _unacknowledgedCount.longValue(); + } +} diff --git a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ExplicitAcceptDispositionChangeListener.java b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ExplicitAcceptDispositionChangeListener.java index 4b38b8a1a3..4420709a91 100755 --- a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ExplicitAcceptDispositionChangeListener.java +++ b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ExplicitAcceptDispositionChangeListener.java @@ -22,7 +22,7 @@ package org.apache.qpid.server.protocol.v0_10; import org.apache.log4j.Logger; -import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.message.MessageInstance; class ExplicitAcceptDispositionChangeListener implements ServerSession.MessageDispositionChangeListener @@ -30,21 +30,20 @@ class ExplicitAcceptDispositionChangeListener implements ServerSession.MessageDi private static final Logger _logger = Logger.getLogger(ExplicitAcceptDispositionChangeListener.class); - private final QueueEntry _entry; - private final Subscription_0_10 _sub; + private final MessageInstance _entry; + private final ConsumerTarget_0_10 _target; - public ExplicitAcceptDispositionChangeListener(QueueEntry entry, Subscription_0_10 subscription_0_10) + public ExplicitAcceptDispositionChangeListener(MessageInstance entry, ConsumerTarget_0_10 target) { _entry = entry; - _sub = subscription_0_10; + _target = target; } public void onAccept() { - final Subscription_0_10 subscription = getSubscription(); - if(subscription != null && _entry.isAcquiredBy(_sub)) + if(_target != null && _entry.isAcquiredBy(_target.getConsumer())) { - subscription.getSessionModel().acknowledge(subscription, _entry); + _target.getSessionModel().acknowledge(_target, _entry); } else { @@ -55,10 +54,9 @@ class ExplicitAcceptDispositionChangeListener implements ServerSession.MessageDi public void onRelease(boolean setRedelivered) { - final Subscription_0_10 subscription = getSubscription(); - if(subscription != null && _entry.isAcquiredBy(_sub)) + if(_target != null && _entry.isAcquiredBy(_target.getConsumer())) { - subscription.release(_entry, setRedelivered); + _target.release(_entry, setRedelivered); } else { @@ -68,10 +66,9 @@ class ExplicitAcceptDispositionChangeListener implements ServerSession.MessageDi public void onReject() { - final Subscription_0_10 subscription = getSubscription(); - if(subscription != null && _entry.isAcquiredBy(_sub)) + if(_target != null && _entry.isAcquiredBy(_target.getConsumer())) { - subscription.reject(_entry); + _target.reject(_entry); } else { @@ -82,12 +79,8 @@ class ExplicitAcceptDispositionChangeListener implements ServerSession.MessageDi public boolean acquire() { - return _entry.acquire(getSubscription()); + return _entry.acquire(_target.getConsumer()); } - private Subscription_0_10 getSubscription() - { - return _sub; - } } diff --git a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ImplicitAcceptDispositionChangeListener.java b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ImplicitAcceptDispositionChangeListener.java index ce0155b789..c459364dbb 100755 --- a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ImplicitAcceptDispositionChangeListener.java +++ b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ImplicitAcceptDispositionChangeListener.java @@ -22,20 +22,20 @@ package org.apache.qpid.server.protocol.v0_10; import org.apache.log4j.Logger; -import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.message.MessageInstance; class ImplicitAcceptDispositionChangeListener implements ServerSession.MessageDispositionChangeListener { private static final Logger _logger = Logger.getLogger(ImplicitAcceptDispositionChangeListener.class); - private final QueueEntry _entry; - private Subscription_0_10 _sub; + private final MessageInstance _entry; + private ConsumerTarget_0_10 _target; - public ImplicitAcceptDispositionChangeListener(QueueEntry entry, Subscription_0_10 subscription_0_10) + public ImplicitAcceptDispositionChangeListener(MessageInstance entry, ConsumerTarget_0_10 target) { _entry = entry; - _sub = subscription_0_10; + _target = target; } public void onAccept() @@ -45,9 +45,9 @@ class ImplicitAcceptDispositionChangeListener implements ServerSession.MessageDi public void onRelease(boolean setRedelivered) { - if(_entry.isAcquiredBy(_sub)) + if(_entry.isAcquiredBy(_target.getConsumer())) { - getSubscription().release(_entry, setRedelivered); + _target.release(_entry, setRedelivered); } else { @@ -57,9 +57,9 @@ class ImplicitAcceptDispositionChangeListener implements ServerSession.MessageDi public void onReject() { - if(_entry.isAcquiredBy(_sub)) + if(_entry.isAcquiredBy(_target.getConsumer())) { - getSubscription().reject(_entry); + _target.reject(_entry); } else { @@ -70,19 +70,15 @@ class ImplicitAcceptDispositionChangeListener implements ServerSession.MessageDi public boolean acquire() { - boolean acquired = _entry.acquire(getSubscription()); + boolean acquired = _entry.acquire(_target.getConsumer()); if(acquired) { - getSubscription().recordUnacknowledged(_entry); + _target.recordUnacknowledged(_entry); } return acquired; } - public Subscription_0_10 getSubscription() - { - return _sub; - } } diff --git a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/MessageAcceptCompletionListener.java b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/MessageAcceptCompletionListener.java index f5f2a8d43f..cd1146ac0b 100755 --- a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/MessageAcceptCompletionListener.java +++ b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/MessageAcceptCompletionListener.java @@ -21,17 +21,17 @@ package org.apache.qpid.server.protocol.v0_10; -import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.transport.Method; public class MessageAcceptCompletionListener implements Method.CompletionListener { - private final Subscription_0_10 _sub; - private final QueueEntry _entry; + private final ConsumerTarget_0_10 _sub; + private final MessageInstance _entry; private final ServerSession _session; private boolean _restoreCredit; - public MessageAcceptCompletionListener(Subscription_0_10 sub, ServerSession session, QueueEntry entry, boolean restoreCredit) + public MessageAcceptCompletionListener(ConsumerTarget_0_10 sub, ServerSession session, MessageInstance entry, boolean restoreCredit) { super(); _sub = sub; @@ -44,9 +44,9 @@ public class MessageAcceptCompletionListener implements Method.CompletionListene { if(_restoreCredit) { - _sub.restoreCredit(_entry); + _sub.restoreCredit(_entry.getMessage()); } - if(_entry.isAcquiredBy(_sub)) + if(_entry.isAcquiredBy(_sub.getConsumer())) { _session.acknowledge(_sub, _entry); } diff --git a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/MessageMetaData_0_10.java b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/MessageMetaData_0_10.java index 2e74621814..687331e51d 100755 --- a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/MessageMetaData_0_10.java +++ b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/MessageMetaData_0_10.java @@ -141,7 +141,7 @@ public class MessageMetaData_0_10 implements StorableMessageMetaData return buf; } - public int writeToBuffer(int offsetInMetaData, ByteBuffer dest) + public int writeToBuffer(ByteBuffer dest) { ByteBuffer buf = _encoded; @@ -153,7 +153,7 @@ public class MessageMetaData_0_10 implements StorableMessageMetaData buf = buf.duplicate(); - buf.position(offsetInMetaData); + buf.position(0); if(dest.remaining() < buf.limit()) { diff --git a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerConnectionDelegate.java b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerConnectionDelegate.java index a15fea1200..c85a415ce5 100644 --- a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerConnectionDelegate.java +++ b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerConnectionDelegate.java @@ -282,8 +282,8 @@ public class ServerConnectionDelegate extends ServerDelegate private void stopAllSubscriptions(Connection conn, SessionDetach dtc) { final ServerSession ssn = (ServerSession) conn.getSession(dtc.getChannel()); - final Collection subs = ssn.getSubscriptions(); - for (Subscription_0_10 subscription_0_10 : subs) + final Collection subs = ssn.getSubscriptions(); + for (ConsumerTarget_0_10 subscription_0_10 : subs) { subscription_0_10.stop(); } diff --git a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSession.java b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSession.java index 93d886687c..53022c333e 100644 --- a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSession.java +++ b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSession.java @@ -46,7 +46,7 @@ import org.apache.qpid.AMQStoreException; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.TransactionTimeoutHelper; import org.apache.qpid.server.TransactionTimeoutHelper.CloseAction; -import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.consumer.Consumer; import org.apache.qpid.server.logging.LogActor; import org.apache.qpid.server.logging.LogMessage; import org.apache.qpid.server.logging.LogSubject; @@ -55,15 +55,16 @@ import org.apache.qpid.server.logging.actors.GenericActor; import org.apache.qpid.server.logging.messages.ChannelMessages; import org.apache.qpid.server.logging.subjects.ChannelLogSubject; import org.apache.qpid.server.message.InstanceProperties; -import org.apache.qpid.server.message.MessageReference; +import org.apache.qpid.server.message.MessageDestination; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.protocol.AMQSessionModel; +import org.apache.qpid.server.protocol.CapacityChecker; import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.BaseQueue; -import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.security.AuthorizationHolder; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.StoreFuture; +import org.apache.qpid.server.store.TransactionLogResource; import org.apache.qpid.server.txn.AlreadyKnownDtxException; import org.apache.qpid.server.txn.AsyncAutoCommitTransaction; import org.apache.qpid.server.txn.DistributedTransaction; @@ -77,6 +78,7 @@ import org.apache.qpid.server.txn.ServerTransaction; import org.apache.qpid.server.txn.SuspendAndFailDtxException; import org.apache.qpid.server.txn.TimeoutDtxException; import org.apache.qpid.server.txn.UnknownDtxBranchException; +import org.apache.qpid.server.util.Action; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.transport.*; import org.slf4j.Logger; @@ -104,14 +106,7 @@ public class ServerSession extends Session private final AtomicBoolean _blocking = new AtomicBoolean(false); private ChannelLogSubject _logSubject; private final AtomicInteger _outstandingCredit = new AtomicInteger(UNLIMITED_CREDIT); - private final BaseQueue.PostEnqueueAction _checkCapacityAction = new BaseQueue.PostEnqueueAction() - { - @Override - public void onEnqueue(final QueueEntry entry) - { - entry.getQueue().checkCapacity(ServerSession.this); - } - }; + private final CheckCapacityAction _checkCapacityAction = new CheckCapacityAction(); public static interface MessageDispositionChangeListener { @@ -126,12 +121,6 @@ public class ServerSession extends Session } - public static interface Task - { - public void doTask(ServerSession session); - } - - private final SortedMap _messageDispositionListenerMap = new ConcurrentSkipListMap(); @@ -142,9 +131,9 @@ public class ServerSession extends Session private final AtomicLong _txnRejects = new AtomicLong(0); private final AtomicLong _txnCount = new AtomicLong(0); - private Map _subscriptions = new ConcurrentHashMap(); + private Map _subscriptions = new ConcurrentHashMap(); - private final List _taskList = new CopyOnWriteArrayList(); + private final List> _taskList = new CopyOnWriteArrayList>(); private final TransactionTimeoutHelper _transactionTimeoutHelper; @@ -194,7 +183,7 @@ public class ServerSession extends Session public int enqueue(final MessageTransferMessage message, final InstanceProperties instanceProperties, - final Exchange exchange) + final MessageDestination exchange) { if(_outstandingCredit.get() != UNLIMITED_CREDIT && _outstandingCredit.decrementAndGet() == (Integer.MAX_VALUE - PRODUCER_CREDIT_TOPUP_THRESHOLD)) @@ -386,9 +375,9 @@ public class ServerSession extends Session } _messageDispositionListenerMap.clear(); - for (Task task : _taskList) + for (Action task : _taskList) { - task.doTask(this); + task.performAction(this); } LogMessage operationalLoggingMessage = _forcedCloseLogMessage.get(); @@ -405,9 +394,9 @@ public class ServerSession extends Session // Broker shouldn't block awaiting close - thus do override this method to do nothing } - public void acknowledge(final Subscription_0_10 sub, final QueueEntry entry) + public void acknowledge(final ConsumerTarget_0_10 sub, final MessageInstance entry) { - _transaction.dequeue(entry.getQueue(), entry.getMessage(), + _transaction.dequeue(entry.getOwningResource(), entry.getMessage(), new ServerTransaction.Action() { @@ -426,42 +415,26 @@ public class ServerSession extends Session }); } - public Collection getSubscriptions() + public Collection getSubscriptions() { return _subscriptions.values(); } - public void register(String destination, Subscription_0_10 sub) + public void register(String destination, ConsumerTarget_0_10 sub) { _subscriptions.put(destination == null ? NULL_DESTINATION : destination, sub); } - public Subscription_0_10 getSubscription(String destination) + public ConsumerTarget_0_10 getSubscription(String destination) { return _subscriptions.get(destination == null ? NULL_DESTINATION : destination); } - public void unregister(Subscription_0_10 sub) + public void unregister(ConsumerTarget_0_10 sub) { _subscriptions.remove(sub.getName()); - try - { - sub.getSendLock(); - AMQQueue queue = sub.getQueue(); - if(queue != null) - { - queue.unregisterSubscription(sub); - } - } - catch (AMQException e) - { - // TODO - _logger.error("Failed to unregister subscription :" + e.getMessage(), e); - } - finally - { - sub.releaseSendLock(); - } + sub.close(); + } public boolean isTransactional() @@ -638,12 +611,12 @@ public class ServerSession extends Session return getConnection().getAuthorizedSubject(); } - public void addSessionCloseTask(Task task) + public void addSessionCloseTask(Action task) { _taskList.add(task); } - public void removeSessionCloseTask(Task task) + public void removeSessionCloseTask(Action task) { _taskList.remove(task); } @@ -829,8 +802,8 @@ public class ServerSession extends Session void unregisterSubscriptions() { - final Collection subscriptions = getSubscriptions(); - for (Subscription_0_10 subscription_0_10 : subscriptions) + final Collection subscriptions = getSubscriptions(); + for (ConsumerTarget_0_10 subscription_0_10 : subscriptions) { unregister(subscription_0_10); } @@ -838,8 +811,8 @@ public class ServerSession extends Session void stopSubscriptions() { - final Collection subscriptions = getSubscriptions(); - for (Subscription_0_10 subscription_0_10 : subscriptions) + final Collection subscriptions = getSubscriptions(); + for (ConsumerTarget_0_10 subscription_0_10 : subscriptions) { subscription_0_10.stop(); } @@ -848,8 +821,8 @@ public class ServerSession extends Session public void receivedComplete() { - final Collection subscriptions = getSubscriptions(); - for (Subscription_0_10 subscription_0_10 : subscriptions) + final Collection subscriptions = getSubscriptions(); + for (ConsumerTarget_0_10 subscription_0_10 : subscriptions) { subscription_0_10.flushCreditState(false); } @@ -955,4 +928,16 @@ public class ServerSession extends Session return getId().compareTo(o.getId()); } + private class CheckCapacityAction implements Action> + { + @Override + public void performAction(final MessageInstance entry) + { + TransactionLogResource queue = entry.getOwningResource(); + if(queue instanceof CapacityChecker) + { + ((CapacityChecker)queue).checkCapacity(ServerSession.this); + } + } + } } diff --git a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSessionDelegate.java b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSessionDelegate.java index dcca696529..9a90b74656 100644 --- a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSessionDelegate.java +++ b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/ServerSessionDelegate.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.protocol.v0_10; +import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.UUID; import org.apache.log4j.Logger; @@ -34,7 +35,9 @@ import org.apache.qpid.server.filter.FilterManager; import org.apache.qpid.server.filter.FilterManagerFactory; import org.apache.qpid.server.logging.messages.ExchangeMessages; import org.apache.qpid.server.message.InstanceProperties; +import org.apache.qpid.server.message.MessageDestination; import org.apache.qpid.server.message.MessageReference; +import org.apache.qpid.server.message.MessageSource; import org.apache.qpid.server.model.Queue; import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.plugin.ExchangeType; @@ -45,6 +48,7 @@ import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.StoreFuture; import org.apache.qpid.server.store.StoredMessage; +import org.apache.qpid.server.consumer.Consumer; import org.apache.qpid.server.txn.AlreadyKnownDtxException; import org.apache.qpid.server.txn.DtxNotSelectedException; import org.apache.qpid.server.txn.IncorrectDtxStateException; @@ -55,6 +59,7 @@ import org.apache.qpid.server.txn.ServerTransaction; import org.apache.qpid.server.txn.SuspendAndFailDtxException; import org.apache.qpid.server.txn.TimeoutDtxException; import org.apache.qpid.server.txn.UnknownDtxBranchException; +import org.apache.qpid.server.util.Action; import org.apache.qpid.server.virtualhost.ExchangeExistsException; import org.apache.qpid.server.virtualhost.ExchangeIsAlternateException; import org.apache.qpid.server.virtualhost.RequiredExchangeException; @@ -193,7 +198,7 @@ public class ServerSessionDelegate extends SessionDelegate String queueName = method.getQueue(); VirtualHost vhost = getVirtualHost(session); - final AMQQueue queue = vhost.getQueue(queueName); + final MessageSource queue = vhost.getMessageSource(queueName); if(queue == null) { @@ -214,9 +219,9 @@ public class ServerSessionDelegate extends SessionDelegate ServerSession s = (ServerSession) session; queue.setExclusiveOwningSession(s); - ((ServerSession) session).addSessionCloseTask(new ServerSession.Task() + ((ServerSession) session).addSessionCloseTask(new Action() { - public void doTask(ServerSession session) + public void performAction(ServerSession session) { if(queue.getExclusiveOwningSession() == session) { @@ -228,9 +233,9 @@ public class ServerSessionDelegate extends SessionDelegate if(queue.getAuthorizationHolder() == null) { queue.setAuthorizationHolder(s); - ((ServerSession) session).addSessionCloseTask(new ServerSession.Task() + ((ServerSession) session).addSessionCloseTask(new Action() { - public void doTask(ServerSession session) + public void performAction(ServerSession session) { if(queue.getAuthorizationHolder() == session) { @@ -254,25 +259,42 @@ public class ServerSessionDelegate extends SessionDelegate return; } - Subscription_0_10 sub = new Subscription_0_10((ServerSession)session, - destination, - method.getAcceptMode(), - method.getAcquireMode(), - MessageFlowMode.WINDOW, - creditManager, - filterManager, - method.getArguments()); + ConsumerTarget_0_10 target = new ConsumerTarget_0_10((ServerSession)session, destination, + method.getAcceptMode(), + method.getAcquireMode(), + MessageFlowMode.WINDOW, + creditManager, + method.getArguments() + ); - ((ServerSession)session).register(destination, sub); + ((ServerSession)session).register(destination, target); try { - queue.registerSubscription(sub, method.getExclusive()); + EnumSet options = EnumSet.noneOf(Consumer.Option.class); + if(method.getAcquireMode() == MessageAcquireMode.PRE_ACQUIRED) + { + options.add(Consumer.Option.ACQUIRES); + } + if(method.getAcquireMode() != MessageAcquireMode.NOT_ACQUIRED || method.getAcceptMode() == MessageAcceptMode.EXPLICIT) + { + options.add(Consumer.Option.SEES_REQUEUES); + } + if(method.getExclusive()) + { + options.add(Consumer.Option.EXCLUSIVE); + } + Consumer sub = + queue.addConsumer(target, + filterManager, + MessageTransferMessage.class, + destination, + options); } - catch (AMQQueue.ExistingExclusiveSubscription existing) + catch (AMQQueue.ExistingExclusiveConsumer existing) { exception(session, method, ExecutionErrorCode.RESOURCE_LOCKED, "Queue has an exclusive consumer"); } - catch (AMQQueue.ExistingSubscriptionPreventsExclusive exclusive) + catch (AMQQueue.ExistingConsumerPreventsExclusive exclusive) { exception(session, method, ExecutionErrorCode.RESOURCE_LOCKED, "Queue has an existing consumer - can't subscribe exclusively"); } @@ -288,7 +310,7 @@ public class ServerSessionDelegate extends SessionDelegate @Override public void messageTransfer(Session ssn, final MessageTransfer xfr) { - final Exchange exchange = getExchangeForMessage(ssn, xfr); + final MessageDestination exchange = getDestinationForMessage(ssn, xfr); final DeliveryProperties delvProps = xfr.getHeader() == null ? null : xfr.getHeader().getDeliveryProperties(); if(delvProps != null && delvProps.hasTtl() && !delvProps.hasExpiration()) @@ -307,7 +329,6 @@ public class ServerSessionDelegate extends SessionDelegate return; } - final Exchange exchangeInUse; final MessageStore store = getVirtualHost(ssn).getMessageStore(); final StoredMessage storeMessage = createStoreMessage(xfr, messageMetaData, store); final ServerSession serverSession = (ServerSession) ssn; @@ -385,7 +406,7 @@ public class ServerSessionDelegate extends SessionDelegate { String destination = method.getDestination(); - Subscription_0_10 sub = ((ServerSession)session).getSubscription(destination); + ConsumerTarget_0_10 sub = ((ServerSession)session).getSubscription(destination); if(sub == null) { @@ -393,12 +414,7 @@ public class ServerSessionDelegate extends SessionDelegate } else { - AMQQueue queue = sub.getQueue(); ((ServerSession)session).unregister(sub); - if(!queue.isDeleted() && queue.isExclusive() && queue.getConsumerCount() == 0) - { - queue.setAuthorizationHolder(null); - } } } @@ -407,7 +423,7 @@ public class ServerSessionDelegate extends SessionDelegate { String destination = method.getDestination(); - Subscription_0_10 sub = ((ServerSession)session).getSubscription(destination); + ConsumerTarget_0_10 sub = ((ServerSession)session).getSubscription(destination); if(sub == null) { @@ -814,24 +830,24 @@ public class ServerSessionDelegate extends SessionDelegate return getVirtualHost(session).getExchange(exchangeName); } - private Exchange getExchangeForMessage(Session ssn, MessageTransfer xfr) + private MessageDestination getDestinationForMessage(Session ssn, MessageTransfer xfr) { VirtualHost virtualHost = getVirtualHost(ssn); - Exchange exchange; + MessageDestination destination; if(xfr.hasDestination()) { - exchange = virtualHost.getExchange(xfr.getDestination()); - if(exchange == null) + destination = virtualHost.getMessageDestination(xfr.getDestination()); + if(destination == null) { - exchange = virtualHost.getDefaultExchange(); + destination = virtualHost.getDefaultExchange(); } } else { - exchange = virtualHost.getDefaultExchange(); + destination = virtualHost.getDefaultExchange(); } - return exchange; + return destination; } private VirtualHost getVirtualHost(Session session) @@ -1249,9 +1265,9 @@ public class ServerSessionDelegate extends SessionDelegate if (autoDelete && exclusive) { final AMQQueue q = queue; - final ServerSession.Task deleteQueueTask = new ServerSession.Task() + final Action deleteQueueTask = new Action() { - public void doTask(ServerSession session) + public void performAction(ServerSession session) { try { @@ -1265,9 +1281,9 @@ public class ServerSessionDelegate extends SessionDelegate }; final ServerSession s = (ServerSession) session; s.addSessionCloseTask(deleteQueueTask); - queue.addQueueDeleteTask(new AMQQueue.Task() + queue.addQueueDeleteTask(new Action() { - public void doTask(AMQQueue queue) throws AMQException + public void performAction(AMQQueue queue) { s.removeSessionCloseTask(deleteQueueTask); } @@ -1276,9 +1292,9 @@ public class ServerSessionDelegate extends SessionDelegate if (exclusive) { final AMQQueue q = queue; - final ServerSession.Task removeExclusive = new ServerSession.Task() + final Action removeExclusive = new Action() { - public void doTask(ServerSession session) + public void performAction(ServerSession session) { q.setAuthorizationHolder(null); q.setExclusiveOwningSession(null); @@ -1287,9 +1303,9 @@ public class ServerSessionDelegate extends SessionDelegate final ServerSession s = (ServerSession) session; q.setExclusiveOwningSession(s); s.addSessionCloseTask(removeExclusive); - queue.addQueueDeleteTask(new AMQQueue.Task() + queue.addQueueDeleteTask(new Action() { - public void doTask(AMQQueue queue) throws AMQException + public void performAction(AMQQueue queue) { s.removeSessionCloseTask(removeExclusive); } @@ -1461,7 +1477,7 @@ public class ServerSessionDelegate extends SessionDelegate { String destination = sfm.getDestination(); - Subscription_0_10 sub = ((ServerSession)session).getSubscription(destination); + ConsumerTarget_0_10 sub = ((ServerSession)session).getSubscription(destination); if(sub == null) { @@ -1478,7 +1494,7 @@ public class ServerSessionDelegate extends SessionDelegate { String destination = stop.getDestination(); - Subscription_0_10 sub = ((ServerSession)session).getSubscription(destination); + ConsumerTarget_0_10 sub = ((ServerSession)session).getSubscription(destination); if(sub == null) { @@ -1496,7 +1512,7 @@ public class ServerSessionDelegate extends SessionDelegate { String destination = flow.getDestination(); - Subscription_0_10 sub = ((ServerSession)session).getSubscription(destination); + ConsumerTarget_0_10 sub = ((ServerSession)session).getSubscription(destination); if(sub == null) { diff --git a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/Subscription_0_10.java b/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/Subscription_0_10.java deleted file mode 100644 index 357b565365..0000000000 --- a/qpid/java/broker-plugins/amqp-0-10-protocol/src/main/java/org/apache/qpid/server/protocol/v0_10/Subscription_0_10.java +++ /dev/null @@ -1,944 +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.protocol.v0_10; - -import org.apache.qpid.AMQException; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.filter.FilterManager; -import org.apache.qpid.server.flow.FlowCreditManager; -import org.apache.qpid.server.logging.LogActor; -import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.actors.GenericActor; -import org.apache.qpid.server.logging.messages.ChannelMessages; -import org.apache.qpid.server.logging.messages.SubscriptionMessages; -import org.apache.qpid.server.model.Queue; -import org.apache.qpid.server.plugin.MessageConverter; -import org.apache.qpid.server.protocol.MessageConverterRegistry; -import org.apache.qpid.server.message.ServerMessage; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.BaseQueue; -import org.apache.qpid.server.queue.QueueEntry; -import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.txn.AutoCommitTransaction; -import org.apache.qpid.server.txn.ServerTransaction; -import org.apache.qpid.transport.DeliveryProperties; -import org.apache.qpid.transport.Header; -import org.apache.qpid.transport.MessageAcceptMode; -import org.apache.qpid.transport.MessageAcquireMode; -import org.apache.qpid.transport.MessageCreditUnit; -import org.apache.qpid.transport.MessageFlowMode; -import org.apache.qpid.transport.MessageProperties; -import org.apache.qpid.transport.MessageTransfer; -import org.apache.qpid.transport.Method; -import org.apache.qpid.transport.Option; -import org.apache.qpid.transport.Struct; - -import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.QUEUE_FORMAT; -import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.SUBSCRIPTION_FORMAT; - -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCreditManagerListener, LogSubject -{ - private final long _subscriptionID; - - private final QueueEntry.SubscriptionAcquiredState _owningState = new QueueEntry.SubscriptionAcquiredState(this); - - private static final Option[] BATCHED = new Option[] { Option.BATCH }; - - private final Lock _stateChangeLock = new ReentrantLock(); - - private final AtomicReference _state = new AtomicReference(State.ACTIVE); - private volatile AMQQueue.Context _queueContext; - private final AtomicBoolean _deleted = new AtomicBoolean(false); - - - private FlowCreditManager_0_10 _creditManager; - - private StateListener _stateListener = new StateListener() - { - - public void stateChange(Subscription sub, State oldState, State newState) - { - CurrentActor.get().message(SubscriptionMessages.STATE(newState.toString())); - } - }; - private AMQQueue _queue; - private final String _destination; - private boolean _noLocal; - private final FilterManager _filters; - private final MessageAcceptMode _acceptMode; - private final MessageAcquireMode _acquireMode; - private MessageFlowMode _flowMode; - private final ServerSession _session; - private final AtomicBoolean _stopped = new AtomicBoolean(true); - private static final Struct[] EMPTY_STRUCT_ARRAY = new Struct[0]; - - private LogActor _logActor; - private final Map _properties = new ConcurrentHashMap(); - private String _traceExclude; - private String _trace; - private final long _createTime = System.currentTimeMillis(); - private final AtomicLong _deliveredCount = new AtomicLong(0); - private final AtomicLong _deliveredBytes = new AtomicLong(0); - private final AtomicLong _unacknowledgedCount = new AtomicLong(0); - private final AtomicLong _unacknowledgedBytes = new AtomicLong(0); - - private final Map _arguments; - private int _deferredMessageCredit; - private long _deferredSizeCredit; - - - public Subscription_0_10(ServerSession session, String destination, MessageAcceptMode acceptMode, - MessageAcquireMode acquireMode, - MessageFlowMode flowMode, - FlowCreditManager_0_10 creditManager, - FilterManager filters,Map arguments) - { - _subscriptionID = SUB_ID_GENERATOR.getAndIncrement(); - _session = session; - _postIdSettingAction = new AddMessageDispositionListenerAction(session); - _destination = destination; - _acceptMode = acceptMode; - _acquireMode = acquireMode; - _creditManager = creditManager; - _flowMode = flowMode; - _filters = filters; - _creditManager.addStateListener(this); - _arguments = arguments == null ? Collections. emptyMap() : - Collections. unmodifiableMap(arguments); - _state.set(_creditManager.hasCredit() ? State.ACTIVE : State.SUSPENDED); - - } - - public void setNoLocal(boolean noLocal) - { - _noLocal = noLocal; - } - - public AMQQueue getQueue() - { - return _queue; - } - - public QueueEntry.SubscriptionAcquiredState getOwningState() - { - return _owningState; - } - - public void setQueue(AMQQueue queue, boolean exclusive) - { - if(getQueue() != null) - { - throw new IllegalStateException("Attempt to set queue for subscription " + this + " to " + queue + "when already set to " + getQueue()); - } - _queue = queue; - - _traceExclude = (String) queue.getAttribute(Queue.FEDERATION_EXCLUDES); - _trace = (String) queue.getAttribute(Queue.FEDERATION_ID); - String filterLogString = null; - - _logActor = GenericActor.getInstance(this); - if (CurrentActor.get().getRootMessageLogger().isMessageEnabled(_logActor, this, SubscriptionMessages.CREATE_LOG_HIERARCHY)) - { - filterLogString = getFilterLogString(); - CurrentActor.get().message(this, SubscriptionMessages.CREATE(filterLogString, queue.isDurable() && exclusive, - filterLogString.length() > 0)); - } - } - - public String getConsumerName() - { - return _destination; - } - - public boolean isSuspended() - { - return !isActive() || _deleted.get() || _session.isClosing() || _session.getConnectionModel().isStopped(); // TODO check for Session suspension - } - - public boolean hasInterest(QueueEntry entry) - { - - - - //check that the message hasn't been rejected - if (entry.isRejectedBy(getSubscriptionID())) - { - - return false; - } - - if (entry.getMessage() instanceof MessageTransferMessage) - { - if(_noLocal) - { - Object connectionRef = ((MessageTransferMessage)entry.getMessage()).getConnectionReference(); - if (connectionRef != null && connectionRef == _session.getReference()) - { - return false; - } - } - } - else - { - // no interest in messages we can't convert - if(MessageConverterRegistry.getConverter(entry.getMessage().getClass(), MessageTransferMessage.class)==null) - { - return false; - } - } - - - return checkFilters(entry); - - - } - - private boolean checkFilters(QueueEntry entry) - { - return (_filters == null) || _filters.allAllow(entry.asFilterable()); - } - - public boolean isClosed() - { - return getState() == State.CLOSED; - } - - public boolean isBrowser() - { - return _acquireMode == MessageAcquireMode.NOT_ACQUIRED; - } - - public boolean seesRequeues() - { - return _acquireMode != MessageAcquireMode.NOT_ACQUIRED || _acceptMode == MessageAcceptMode.EXPLICIT; - } - - public void close() - { - boolean closed = false; - State state = getState(); - - _stateChangeLock.lock(); - try - { - 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); - CurrentActor.get().message(getLogSubject(), SubscriptionMessages.CLOSE()); - } - finally - { - _stateChangeLock.unlock(); - } - - - - } - - public Long getDelivered() - { - return _deliveredCount.get(); - } - - public void creditStateChanged(boolean hasCredit) - { - - if(hasCredit) - { - if(_state.compareAndSet(State.SUSPENDED, State.ACTIVE)) - { - _stateListener.stateChange(this, State.SUSPENDED, State.ACTIVE); - } - else - { - // this is a hack to get round the issue of increasing bytes credit - _stateListener.stateChange(this, State.ACTIVE, State.ACTIVE); - } - } - else - { - if(_state.compareAndSet(State.ACTIVE, State.SUSPENDED)) - { - _stateListener.stateChange(this, State.ACTIVE, State.SUSPENDED); - } - } - } - - - public static class AddMessageDispositionListenerAction implements Runnable - { - private MessageTransfer _xfr; - private ServerSession.MessageDispositionChangeListener _action; - private ServerSession _session; - - public AddMessageDispositionListenerAction(ServerSession session) - { - _session = session; - } - - public void setXfr(MessageTransfer xfr) - { - _xfr = xfr; - } - - public void setAction(ServerSession.MessageDispositionChangeListener action) - { - _action = action; - } - - public void run() - { - if(_action != null) - { - _session.onMessageDispositionChange(_xfr, _action); - } - } - } - - private final AddMessageDispositionListenerAction _postIdSettingAction; - - public void send(final QueueEntry entry, boolean batch) throws AMQException - { - ServerMessage serverMsg = entry.getMessage(); - - - MessageTransfer xfr; - - DeliveryProperties deliveryProps; - MessageProperties messageProps = null; - - MessageTransferMessage msg; - - if(serverMsg instanceof MessageTransferMessage) - { - - msg = (MessageTransferMessage) serverMsg; - - } - else - { - MessageConverter converter = - MessageConverterRegistry.getConverter(serverMsg.getClass(), MessageTransferMessage.class); - - - msg = (MessageTransferMessage) converter.convert(serverMsg, getQueue().getVirtualHost()); - } - DeliveryProperties origDeliveryProps = msg.getHeader() == null ? null : msg.getHeader().getDeliveryProperties(); - messageProps = msg.getHeader() == null ? null : msg.getHeader().getMessageProperties(); - - deliveryProps = new DeliveryProperties(); - if(origDeliveryProps != null) - { - if(origDeliveryProps.hasDeliveryMode()) - { - deliveryProps.setDeliveryMode(origDeliveryProps.getDeliveryMode()); - } - if(origDeliveryProps.hasExchange()) - { - deliveryProps.setExchange(origDeliveryProps.getExchange()); - } - if(origDeliveryProps.hasExpiration()) - { - deliveryProps.setExpiration(origDeliveryProps.getExpiration()); - } - if(origDeliveryProps.hasPriority()) - { - deliveryProps.setPriority(origDeliveryProps.getPriority()); - } - if(origDeliveryProps.hasRoutingKey()) - { - deliveryProps.setRoutingKey(origDeliveryProps.getRoutingKey()); - } - if(origDeliveryProps.hasTimestamp()) - { - deliveryProps.setTimestamp(origDeliveryProps.getTimestamp()); - } - if(origDeliveryProps.hasTtl()) - { - deliveryProps.setTtl(origDeliveryProps.getTtl()); - } - - - } - - deliveryProps.setRedelivered(entry.isRedelivered()); - - if(_trace != null && messageProps == null) - { - messageProps = new MessageProperties(); - } - - Header header = new Header(deliveryProps, messageProps, msg.getHeader() == null ? null : msg.getHeader().getNonStandardProperties()); - - - xfr = batch ? new MessageTransfer(_destination,_acceptMode,_acquireMode,header,msg.getBody(), BATCHED) - : new MessageTransfer(_destination,_acceptMode,_acquireMode,header,msg.getBody()); - - boolean excludeDueToFederation = false; - - if(_trace != null) - { - if(!messageProps.hasApplicationHeaders()) - { - messageProps.setApplicationHeaders(new HashMap()); - } - Map appHeaders = messageProps.getApplicationHeaders(); - String trace = (String) appHeaders.get("x-qpid.trace"); - if(trace == null) - { - trace = _trace; - } - else - { - if(_traceExclude != null) - { - excludeDueToFederation = Arrays.asList(trace.split(",")).contains(_traceExclude); - } - trace+=","+_trace; - } - appHeaders.put("x-qpid.trace",trace); - } - - if(!excludeDueToFederation) - { - if(_acceptMode == MessageAcceptMode.NONE && _acquireMode != MessageAcquireMode.PRE_ACQUIRED) - { - xfr.setCompletionListener(new MessageAcceptCompletionListener(this, _session, entry, _flowMode == MessageFlowMode.WINDOW)); - } - else if(_flowMode == MessageFlowMode.WINDOW) - { - xfr.setCompletionListener(new Method.CompletionListener() - { - public void onComplete(Method method) - { - deferredAddCredit(1, entry.getSize()); - } - }); - } - - - _postIdSettingAction.setXfr(xfr); - if(_acceptMode == MessageAcceptMode.EXPLICIT) - { - _postIdSettingAction.setAction(new ExplicitAcceptDispositionChangeListener(entry, this)); - } - else if(_acquireMode != MessageAcquireMode.PRE_ACQUIRED) - { - _postIdSettingAction.setAction(new ImplicitAcceptDispositionChangeListener(entry, this)); - } - else - { - _postIdSettingAction.setAction(null); - } - - - _session.sendMessage(xfr, _postIdSettingAction); - entry.incrementDeliveryCount(); - _deliveredCount.incrementAndGet(); - _deliveredBytes.addAndGet(entry.getSize()); - if(_acceptMode == MessageAcceptMode.NONE && _acquireMode == MessageAcquireMode.PRE_ACQUIRED) - { - forceDequeue(entry, false); - } - else if(_acquireMode == MessageAcquireMode.PRE_ACQUIRED) - { - recordUnacknowledged(entry); - } - } - else - { - forceDequeue(entry, _flowMode == MessageFlowMode.WINDOW); - - } - } - - void recordUnacknowledged(QueueEntry entry) - { - _unacknowledgedCount.incrementAndGet(); - _unacknowledgedBytes.addAndGet(entry.getSize()); - } - - private void deferredAddCredit(final int deferredMessageCredit, final long deferredSizeCredit) - { - _deferredMessageCredit += deferredMessageCredit; - _deferredSizeCredit += deferredSizeCredit; - - } - - public void flushCreditState(boolean strict) - { - if(strict || !isSuspended() || _deferredMessageCredit >= 200 - || !(_creditManager instanceof WindowCreditManager) - || ((WindowCreditManager)_creditManager).getMessageCreditLimit() < 400 ) - { - _creditManager.restoreCredit(_deferredMessageCredit, _deferredSizeCredit); - _deferredMessageCredit = 0; - _deferredSizeCredit = 0l; - } - } - - private void forceDequeue(final QueueEntry entry, final boolean restoreCredit) - { - AutoCommitTransaction dequeueTxn = new AutoCommitTransaction(getQueue().getVirtualHost().getMessageStore()); - dequeueTxn.dequeue(entry.getQueue(), entry.getMessage(), - new ServerTransaction.Action() - { - public void postCommit() - { - if (restoreCredit) - { - restoreCredit(entry); - } - entry.delete(); - } - - public void onRollback() - { - - } - }); - } - - void reject(final QueueEntry entry) - { - entry.setRedelivered(); - entry.routeToAlternate(null, null); - if(entry.isAcquiredBy(this)) - { - entry.delete(); - } - } - - void release(final QueueEntry entry, final boolean setRedelivered) - { - if (setRedelivered) - { - entry.setRedelivered(); - } - - if (getSessionModel().isClosing() || !setRedelivered) - { - entry.decrementDeliveryCount(); - } - - if (isMaxDeliveryLimitReached(entry)) - { - sendToDLQOrDiscard(entry); - } - else - { - entry.release(); - } - } - - protected void sendToDLQOrDiscard(QueueEntry entry) - { - final LogActor logActor = CurrentActor.get(); - final ServerMessage msg = entry.getMessage(); - - int requeues = entry.routeToAlternate(new BaseQueue.PostEnqueueAction() - { - @Override - public void onEnqueue(final QueueEntry requeueEntry) - { - logActor.message( ChannelMessages.DEADLETTERMSG(msg.getMessageNumber(), - requeueEntry.getQueue().getName())); - } - }, null); - - if (requeues == 0) - { - final AMQQueue queue = entry.getQueue(); - final Exchange alternateExchange = queue.getAlternateExchange(); - - if(alternateExchange != null) - { - logActor.message( ChannelMessages.DISCARDMSG_NOROUTE(msg.getMessageNumber(), - alternateExchange.getName())); - } - else - { - logActor.message(ChannelMessages.DISCARDMSG_NOALTEXCH(msg.getMessageNumber(), - queue.getName(), - msg.getRoutingKey())); - } - } - } - - private boolean isMaxDeliveryLimitReached(QueueEntry entry) - { - final int maxDeliveryLimit = entry.getQueue().getMaximumDeliveryCount(); - return (maxDeliveryLimit > 0 && entry.getDeliveryCount() >= maxDeliveryLimit); - } - - public void queueDeleted(AMQQueue queue) - { - _deleted.set(true); - } - - public boolean wouldSuspend(QueueEntry entry) - { - return !_creditManager.useCreditForMessage(entry.getMessage().getSize()); - } - - public boolean trySendLock() - { - return _stateChangeLock.tryLock(); - } - - - public void getSendLock() - { - _stateChangeLock.lock(); - } - - public void releaseSendLock() - { - _stateChangeLock.unlock(); - } - - public void restoreCredit(QueueEntry queueEntry) - { - _creditManager.restoreCredit(1, queueEntry.getSize()); - } - - public void onDequeue(QueueEntry queueEntry) - { - // no-op for 0-10, credit restored by completing command. - } - - public void releaseQueueEntry(QueueEntry queueEntry) - { - // no-op for 0-10, credit restored by completing command. - } - - public void setStateListener(StateListener listener) - { - _stateListener = listener; - } - - public State getState() - { - return _state.get(); - } - - public AMQQueue.Context getQueueContext() - { - return _queueContext; - } - - public void setQueueContext(AMQQueue.Context queueContext) - { - _queueContext = queueContext; - } - - public boolean isActive() - { - return getState() == State.ACTIVE; - } - - public void set(String key, Object value) - { - _properties.put(key, value); - } - - public Object get(String key) - { - return _properties.get(key); - } - - - public FlowCreditManager_0_10 getCreditManager() - { - return _creditManager; - } - - - public void stop() - { - try - { - getSendLock(); - - if(_state.compareAndSet(State.ACTIVE, State.SUSPENDED)) - { - _stateListener.stateChange(this, State.ACTIVE, State.SUSPENDED); - } - _stopped.set(true); - FlowCreditManager_0_10 creditManager = getCreditManager(); - creditManager.clearCredit(); - } - finally - { - releaseSendLock(); - } - } - - public void addCredit(MessageCreditUnit unit, long value) - { - FlowCreditManager_0_10 creditManager = getCreditManager(); - - switch (unit) - { - case MESSAGE: - - creditManager.addCredit(value, 0L); - break; - case BYTE: - creditManager.addCredit(0l, value); - break; - } - - _stopped.set(false); - - if(creditManager.hasCredit()) - { - if(_state.compareAndSet(State.SUSPENDED, State.ACTIVE)) - { - _stateListener.stateChange(this, State.SUSPENDED, State.ACTIVE); - } - } - - } - - public void setFlowMode(MessageFlowMode flowMode) - { - - - _creditManager.removeListener(this); - - switch(flowMode) - { - case CREDIT: - _creditManager = new CreditCreditManager(0l,0l); - break; - case WINDOW: - _creditManager = new WindowCreditManager(0l,0l); - break; - default: - throw new RuntimeException("Unknown message flow mode: " + flowMode); - } - _flowMode = flowMode; - if(_state.compareAndSet(State.ACTIVE, State.SUSPENDED)) - { - _stateListener.stateChange(this, State.ACTIVE, State.SUSPENDED); - } - - _creditManager.addStateListener(this); - - } - - public boolean isStopped() - { - return _stopped.get(); - } - - public boolean acquires() - { - return _acquireMode == MessageAcquireMode.PRE_ACQUIRED; - } - - public void acknowledge(QueueEntry entry) - { - // TODO Fix Store Context / cleanup - if(entry.isAcquiredBy(this)) - { - _unacknowledgedBytes.addAndGet(-entry.getSize()); - _unacknowledgedCount.decrementAndGet(); - entry.delete(); - } - } - - public void flush() throws AMQException - { - flushCreditState(true); - _queue.flushSubscription(this); - stop(); - } - - public long getSubscriptionID() - { - return _subscriptionID; - } - - public LogActor getLogActor() - { - return _logActor; - } - - public boolean isTransient() - { - return false; - } - - public ServerSession getSessionModel() - { - return _session; - } - - public boolean isBrowsing() - { - return _acquireMode == MessageAcquireMode.NOT_ACQUIRED; - } - - public boolean isExclusive() - { - return getQueue().hasExclusiveSubscriber(); - } - - public boolean isDurable() - { - return false; - } - - - public boolean isExplicitAcknowledge() - { - return _acceptMode == MessageAcceptMode.EXPLICIT; - } - - public String getCreditMode() - { - return _flowMode.toString(); - } - - public String getName() - { - return _destination; - } - - public Map getArguments() - { - return _arguments; - } - - public boolean isSessionTransactional() - { - return _session.isTransactional(); - } - - public void queueEmpty() - { - } - - public long getCreateTime() - { - return _createTime; - } - - public String toLogString() - { - String queueInfo = MessageFormat.format(QUEUE_FORMAT, _queue.getVirtualHost().getName(), - _queue.getName()); - String result = "[" + MessageFormat.format(SUBSCRIPTION_FORMAT, getSubscriptionID()) + "(" - // queueString is "vh(/{0})/qu({1}) " so need to trim - + queueInfo.substring(0, queueInfo.length() - 1) + ")" + "] "; - return result; - } - - private String getFilterLogString() - { - StringBuilder filterLogString = new StringBuilder(); - String delimiter = ", "; - boolean hasEntries = false; - if (_filters != null && _filters.hasFilters()) - { - filterLogString.append(_filters.toString()); - hasEntries = true; - } - - if (isBrowser()) - { - if (hasEntries) - { - filterLogString.append(delimiter); - } - filterLogString.append("Browser"); - hasEntries = true; - } - - if (isDurable()) - { - if (hasEntries) - { - filterLogString.append(delimiter); - } - filterLogString.append("Durable"); - hasEntries = true; - } - - return filterLogString.toString(); - } - - public LogSubject getLogSubject() - { - return (LogSubject) this; - } - - - public void flushBatched() - { - _session.getConnection().flush(); - } - - public long getBytesOut() - { - return _deliveredBytes.longValue(); - } - - public long getMessagesOut() - { - return _deliveredCount.longValue(); - } - - public long getUnacknowledgedBytes() - { - return _unacknowledgedBytes.longValue(); - } - - public long getUnacknowledgedMessages() - { - return _unacknowledgedCount.longValue(); - } -} diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java index e139887284..aa465d373f 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQChannel.java @@ -21,19 +21,7 @@ package org.apache.qpid.server.protocol.v0_8; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; -import java.util.UUID; +import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; @@ -42,6 +30,7 @@ import org.apache.log4j.Logger; import org.apache.qpid.AMQConnectionException; import org.apache.qpid.AMQException; import org.apache.qpid.AMQSecurityException; +import org.apache.qpid.common.AMQPFilterTypes; import org.apache.qpid.framing.AMQMethodBody; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; @@ -55,6 +44,7 @@ import org.apache.qpid.server.TransactionTimeoutHelper; import org.apache.qpid.server.TransactionTimeoutHelper.CloseAction; import org.apache.qpid.server.configuration.BrokerProperties; import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.filter.FilterManagerFactory; import org.apache.qpid.server.flow.FlowCreditManager; import org.apache.qpid.server.flow.Pre0_10CreditManager; import org.apache.qpid.server.logging.LogActor; @@ -66,25 +56,28 @@ import org.apache.qpid.server.logging.messages.ChannelMessages; import org.apache.qpid.server.logging.messages.ExchangeMessages; import org.apache.qpid.server.logging.subjects.ChannelLogSubject; import org.apache.qpid.server.message.InstanceProperties; +import org.apache.qpid.server.message.MessageDestination; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.message.MessageReference; +import org.apache.qpid.server.message.MessageSource; import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.protocol.CapacityChecker; import org.apache.qpid.server.protocol.v0_8.output.ProtocolOutputConverter; import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.StoreFuture; import org.apache.qpid.server.store.StoredMessage; -import org.apache.qpid.server.subscription.ClientDeliveryMethod; -import org.apache.qpid.server.subscription.RecordDeliveryMethod; -import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.store.TransactionLogResource; +import org.apache.qpid.server.consumer.Consumer; import org.apache.qpid.server.txn.AsyncAutoCommitTransaction; import org.apache.qpid.server.txn.LocalTransaction; import org.apache.qpid.server.txn.LocalTransaction.ActivityTimeAccessor; import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.util.Action; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.transport.TransportException; @@ -122,7 +115,7 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F private IncomingMessage _currentMessage; /** Maps from consumer tag to subscription instance. Allows us to unsubscribe from a queue. */ - private final Map _tag2SubscriptionMap = new HashMap(); + private final Map _tag2SubscriptionTargetMap = new HashMap(); private final MessageStore _messageStore; @@ -155,7 +148,7 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F private volatile boolean _rollingBack; private static final Runnable NULL_TASK = new Runnable() { public void run() {} }; - private List _resendList = new ArrayList(); + private List _resendList = new ArrayList(); private static final AMQShortString IMMEDIATE_DELIVERY_REPLY_TEXT = new AMQShortString("Immediate delivery is not possible."); private long _createTime = System.currentTimeMillis(); @@ -266,7 +259,7 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F return _channelId; } - public void setPublishFrame(MessagePublishInfo info, final Exchange e) throws AMQSecurityException + public void setPublishFrame(MessagePublishInfo info, final MessageDestination e) throws AMQSecurityException { String routingKey = info.getRoutingKey() == null ? null : info.getRoutingKey().asString(); SecurityManager securityManager = getVirtualHost().getSecurityManager(); @@ -275,7 +268,7 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F throw new AMQSecurityException("Permission denied: " + e.getName()); } _currentMessage = new IncomingMessage(info); - _currentMessage.setExchange(e); + _currentMessage.setMessageDestination(e); } public void publishContentHeader(ContentHeaderBody contentHeaderBody) @@ -360,7 +353,7 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F } }; - int enqueues = _currentMessage.getExchange().send(amqMessage, instanceProperties, _transaction, + int enqueues = _currentMessage.getDestination().send(amqMessage, instanceProperties, _transaction, immediate ? _immediateAction : _capacityCheckAction); if(enqueues == 0) { @@ -497,62 +490,89 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F } - public Subscription getSubscription(AMQShortString subscription) + public Consumer getSubscription(AMQShortString tag) { - return _tag2SubscriptionMap.get(subscription); + final ConsumerTarget_0_8 target = _tag2SubscriptionTargetMap.get(tag); + return target == null ? null : target.getConsumer(); } /** * Subscribe to a queue. We register all subscriptions in the channel so that if the channel is closed we can clean * up all subscriptions, even if the client does not explicitly unsubscribe from all queues. * + * * @param tag the tag chosen by the client (if null, server will generate one) - * @param queue the queue to subscribe to + * @param source the queue to subscribe to * @param acks Are acks enabled for this subscriber * @param filters Filters to apply to this subscriber * - * @param noLocal Flag stopping own messages being received. * @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 AMQException if something goes wrong */ - public AMQShortString subscribeToQueue(AMQShortString tag, AMQQueue queue, boolean acks, - FieldTable filters, boolean noLocal, boolean exclusive) throws AMQException + public AMQShortString consumeFromSource(AMQShortString tag, MessageSource source, boolean acks, + FieldTable filters, boolean exclusive) throws AMQException { if (tag == null) { tag = new AMQShortString("sgen_" + getNextConsumerTag()); } - if (_tag2SubscriptionMap.containsKey(tag)) + if (_tag2SubscriptionTargetMap.containsKey(tag)) { throw new AMQException("Consumer already exists with same tag: " + tag); } - Subscription subscription = - SubscriptionFactoryImpl.INSTANCE.createSubscription(_channelId, _session, tag, acks, filters, noLocal, _creditManager); + ConsumerTarget_0_8 target; + EnumSet options = EnumSet.noneOf(Consumer.Option.class); + + if(filters != null && Boolean.TRUE.equals(filters.get(AMQPFilterTypes.NO_CONSUME.getValue()))) + { + target = ConsumerTarget_0_8.createBrowserTarget(this, tag, filters, _creditManager); + } + else if(acks) + { + target = ConsumerTarget_0_8.createAckTarget(this, tag, filters, _creditManager); + options.add(Consumer.Option.ACQUIRES); + options.add(Consumer.Option.SEES_REQUEUES); + } + else + { + target = ConsumerTarget_0_8.createNoAckTarget(this, tag, filters, _creditManager); + options.add(Consumer.Option.ACQUIRES); + options.add(Consumer.Option.SEES_REQUEUES); + } + if(exclusive) + { + options.add(Consumer.Option.EXCLUSIVE); + } // 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. - _tag2SubscriptionMap.put(tag, subscription); + _tag2SubscriptionTargetMap.put(tag, target); try { - queue.registerSubscription(subscription, exclusive); + Consumer sub = + source.addConsumer(target, + FilterManagerFactory.createManager(FieldTable.convertToMap(filters)), + AMQMessage.class, + AMQShortString.toString(tag), + options); } catch (AMQException e) { - _tag2SubscriptionMap.remove(tag); + _tag2SubscriptionTargetMap.remove(tag); throw e; } catch (RuntimeException e) { - _tag2SubscriptionMap.remove(tag); + _tag2SubscriptionTargetMap.remove(tag); throw e; } return tag; @@ -567,18 +587,11 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F public boolean unsubscribeConsumer(AMQShortString consumerTag) throws AMQException { - Subscription sub = _tag2SubscriptionMap.remove(consumerTag); + ConsumerTarget_0_8 target = _tag2SubscriptionTargetMap.remove(consumerTag); + Consumer sub = target == null ? null : target.getConsumer(); if (sub != null) { - try - { - sub.getSendLock(); - sub.getQueue().unregisterSubscription(sub); - } - finally - { - sub.releaseSendLock(); - } + sub.close(); return true; } else @@ -633,7 +646,7 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F { if (_logger.isInfoEnabled()) { - if (!_tag2SubscriptionMap.isEmpty()) + if (!_tag2SubscriptionTargetMap.isEmpty()) { _logger.info("Unsubscribing all consumers on channel " + toString()); } @@ -643,28 +656,21 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F } } - for (Map.Entry me : _tag2SubscriptionMap.entrySet()) + for (Map.Entry me : _tag2SubscriptionTargetMap.entrySet()) { if (_logger.isInfoEnabled()) { _logger.info("Unsubscribing consumer '" + me.getKey() + "' on channel " + toString()); } - Subscription sub = me.getValue(); + Consumer sub = me.getValue().getConsumer(); - try - { - sub.getSendLock(); - sub.getQueue().unregisterSubscription(sub); - } - finally - { - sub.releaseSendLock(); - } + + sub.close(); } - _tag2SubscriptionMap.clear(); + _tag2SubscriptionTargetMap.clear(); } /** @@ -673,24 +679,15 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F * @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 subscription The consumer that is to acknowledge this message. + * @param consumer The consumer that is to acknowledge this message. */ - public void addUnacknowledgedMessage(QueueEntry entry, long deliveryTag, Subscription subscription) + public void addUnacknowledgedMessage(MessageInstance entry, long deliveryTag, Consumer consumer) { if (_logger.isDebugEnabled()) { - if (entry.getQueue() == null) - { - _logger.debug("Adding unacked message with a null queue:" + entry); - } - else - { - if (_logger.isDebugEnabled()) - { _logger.debug(debugIdentity() + " Adding unacked message(" + entry.getMessage().toString() + " DT:" + deliveryTag - + ") with a queue(" + entry.getQueue() + ") for " + subscription); - } - } + + ") for " + consumer + " on " + entry.getOwningResource().getName()); + } _unacknowledgedMessageMap.add(deliveryTag, entry); @@ -713,7 +710,7 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F 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 messagesToBeDelivered = _unacknowledgedMessageMap.cancelAllMessages(); + Collection messagesToBeDelivered = _unacknowledgedMessageMap.cancelAllMessages(); if (!messagesToBeDelivered.isEmpty()) { @@ -724,21 +721,13 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F } - for (QueueEntry unacked : messagesToBeDelivered) + for (MessageInstance unacked : messagesToBeDelivered) { - if (!unacked.isQueueDeleted()) - { - // Mark message redelivered - unacked.setRedelivered(); - - // Ensure message is released for redelivery - unacked.release(); + // Mark message redelivered + unacked.setRedelivered(); - } - else - { - unacked.delete(); - } + // Ensure message is released for redelivery + unacked.release(); } } @@ -752,7 +741,7 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F */ public void requeue(long deliveryTag) throws AMQException { - QueueEntry unacked = _unacknowledgedMessageMap.remove(deliveryTag); + MessageInstance unacked = _unacknowledgedMessageMap.remove(deliveryTag); if (unacked != null) { @@ -760,20 +749,8 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F unacked.setRedelivered(); // Ensure message is released for redelivery - if (!unacked.isQueueDeleted()) - { - - // Ensure message is released for redelivery - unacked.release(); - - } - else - { - _logger.warn(System.identityHashCode(this) + " Requested requeue of message(" + unacked - + "):" + deliveryTag + " but no queue defined and no DeadLetter queue so DROPPING message."); + unacked.release(); - unacked.delete(); - } } else { @@ -786,10 +763,10 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F public boolean isMaxDeliveryCountEnabled(final long deliveryTag) { - final QueueEntry queueEntry = _unacknowledgedMessageMap.get(deliveryTag); + final MessageInstance queueEntry = _unacknowledgedMessageMap.get(deliveryTag); if (queueEntry != null) { - final int maximumDeliveryCount = queueEntry.getQueue().getMaximumDeliveryCount(); + final int maximumDeliveryCount = queueEntry.getMaximumDeliveryCount(); return maximumDeliveryCount > 0; } @@ -798,10 +775,10 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F public boolean isDeliveredTooManyTimes(final long deliveryTag) { - final QueueEntry queueEntry = _unacknowledgedMessageMap.get(deliveryTag); + final MessageInstance queueEntry = _unacknowledgedMessageMap.get(deliveryTag); if (queueEntry != null) { - final int maximumDeliveryCount = queueEntry.getQueue().getMaximumDeliveryCount(); + final int maximumDeliveryCount = queueEntry.getMaximumDeliveryCount(); final int numDeliveries = queueEntry.getDeliveryCount(); return maximumDeliveryCount != 0 && numDeliveries >= maximumDeliveryCount; } @@ -812,16 +789,14 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F /** * Called to resend all outstanding unacknowledged messages to this same channel. * - * @param requeue Are the messages to be requeued or dropped. - * * @throws AMQException When something goes wrong. */ - public void resend(final boolean requeue) throws AMQException + public void resend() throws AMQException { - final Map msgToRequeue = new LinkedHashMap(); - final Map msgToResend = new LinkedHashMap(); + final Map msgToRequeue = new LinkedHashMap(); + final Map msgToResend = new LinkedHashMap(); if (_logger.isDebugEnabled()) { @@ -833,9 +808,8 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F // and those that don't to be requeued. _unacknowledgedMessageMap.visit(new ExtractResendAndRequeue(_unacknowledgedMessageMap, msgToRequeue, - msgToResend, - requeue, - _messageStore)); + msgToResend + )); // Process Messages to Resend @@ -851,39 +825,20 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F } } - for (Map.Entry entry : msgToResend.entrySet()) + for (Map.Entry entry : msgToResend.entrySet()) { - QueueEntry message = entry.getValue(); + MessageInstance message = entry.getValue(); long deliveryTag = entry.getKey(); //Amend the delivery counter as the client hasn't seen these messages yet. message.decrementDeliveryCount(); - AMQQueue queue = message.getQueue(); - // Without any details from the client about what has been processed we have to mark // all messages in the unacked map as redelivered. message.setRedelivered(); - Subscription sub = message.getDeliveredSubscription(); - - if (sub != null) - { - - if(!queue.resend(message,sub)) - { - msgToRequeue.put(deliveryTag, message); - } - } - else + if (!message.resend()) { - - if (_logger.isInfoEnabled()) - { - _logger.info("DeliveredSubscription not recorded so just requeueing(" + message.toString() - + ")to prevent loss"); - } - // move this message to requeue msgToRequeue.put(deliveryTag, message); } } // for all messages @@ -898,9 +853,9 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F } // Process Messages to Requeue at the front of the queue - for (Map.Entry entry : msgToRequeue.entrySet()) + for (Map.Entry entry : msgToRequeue.entrySet()) { - QueueEntry message = entry.getValue(); + MessageInstance message = entry.getValue(); long deliveryTag = entry.getKey(); //Amend the delivery counter as the client hasn't seen these messages yet. @@ -926,11 +881,11 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F */ public void acknowledgeMessage(long deliveryTag, boolean multiple) throws AMQException { - Collection ackedMessages = getAckedMessages(deliveryTag, multiple); + Collection ackedMessages = getAckedMessages(deliveryTag, multiple); _transaction.dequeue(ackedMessages, new MessageAcknowledgeAction(ackedMessages)); } - private Collection getAckedMessages(long deliveryTag, boolean multiple) + private Collection getAckedMessages(long deliveryTag, boolean multiple) { return _unacknowledgedMessageMap.acknowledge(deliveryTag, multiple); @@ -976,9 +931,9 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F if (wasSuspended) { // may need to deliver queued messages - for (Subscription s : _tag2SubscriptionMap.values()) + for (ConsumerTarget_0_8 s : _tag2SubscriptionTargetMap.values()) { - s.getQueue().deliverAsync(s); + s.getConsumer().externalStateChange(); } } @@ -992,15 +947,15 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F if (!wasSuspended) { // may need to deliver queued messages - for (Subscription s : _tag2SubscriptionMap.values()) + for (ConsumerTarget_0_8 s : _tag2SubscriptionTargetMap.values()) { try { - s.getSendLock(); + s.getConsumer().getSendLock(); } finally { - s.releaseSendLock(); + s.getConsumer().releaseSendLock(); } } } @@ -1077,10 +1032,10 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F boolean requiresSuspend = _suspended.compareAndSet(false,true); // ensure all subscriptions have seen the change to the channel state - for(Subscription sub : _tag2SubscriptionMap.values()) + for(ConsumerTarget_0_8 sub : _tag2SubscriptionTargetMap.values()) { - sub.getSendLock(); - sub.releaseSendLock(); + sub.getConsumer().getSendLock(); + sub.getConsumer().releaseSendLock(); } try @@ -1098,16 +1053,16 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F postRollbackTask.run(); - for(QueueEntry entry : _resendList) + for(MessageInstance entry : _resendList) { - Subscription sub = entry.getDeliveredSubscription(); + Consumer sub = entry.getDeliveredConsumer(); if(sub == null || sub.isClosed()) { entry.release(); } else { - sub.getQueue().resend(entry, sub); + entry.resend(); } } _resendList.clear(); @@ -1115,9 +1070,9 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F if(requiresSuspend) { _suspended.set(false); - for(Subscription sub : _tag2SubscriptionMap.values()) + for(ConsumerTarget_0_8 sub : _tag2SubscriptionTargetMap.values()) { - sub.getQueue().deliverAsync(sub); + sub.getConsumer().externalStateChange(); } } @@ -1173,7 +1128,7 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F private final RecordDeliveryMethod _recordDeliveryMethod = new RecordDeliveryMethod() { - public void recordMessageDelivery(final Subscription sub, final QueueEntry entry, final long deliveryTag) + public void recordMessageDelivery(final Consumer sub, final MessageInstance entry, final long deliveryTag) { addUnacknowledgedMessage(entry, deliveryTag, sub); } @@ -1234,78 +1189,96 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F } - private class ImmediateAction implements BaseQueue.PostEnqueueAction + private class ImmediateAction implements Action> { public ImmediateAction() { } - public void onEnqueue(QueueEntry entry) + public void performAction(MessageInstance entry) { - AMQQueue queue = entry.getQueue(); + TransactionLogResource queue = entry.getOwningResource(); if (!entry.getDeliveredToConsumer() && entry.acquire()) { ServerTransaction txn = new LocalTransaction(_messageStore); - Collection entries = new ArrayList(1); - entries.add(entry); final AMQMessage message = (AMQMessage) entry.getMessage(); - txn.dequeue(queue, entry.getMessage(), - new MessageAcknowledgeAction(entries) - { - @Override - public void postCommit() + MessageReference ref = message.newReference(); + try + { + entry.delete(); + txn.dequeue(queue, message, + new ServerTransaction.Action() { - try + @Override + public void postCommit() { - final - ProtocolOutputConverter outputConverter = - _session.getProtocolOutputConverter(); - - outputConverter.writeReturn(message.getMessagePublishInfo(), - message.getContentHeaderBody(), - message, - _channelId, - AMQConstant.NO_CONSUMERS.getCode(), - IMMEDIATE_DELIVERY_REPLY_TEXT); + try + { + final + ProtocolOutputConverter outputConverter = + _session.getProtocolOutputConverter(); + + outputConverter.writeReturn(message.getMessagePublishInfo(), + message.getContentHeaderBody(), + message, + _channelId, + AMQConstant.NO_CONSUMERS.getCode(), + IMMEDIATE_DELIVERY_REPLY_TEXT); + } + catch (AMQException e) + { + throw new RuntimeException(e); + } } - catch (AMQException e) + + @Override + public void onRollback() { - throw new RuntimeException(e); + } - super.postCommit(); } - } - ); - txn.commit(); + ); + txn.commit(); + } + finally + { + ref.release(); + } } else { - queue.checkCapacity(AMQChannel.this); + if(queue instanceof CapacityChecker) + { + ((CapacityChecker)queue).checkCapacity(AMQChannel.this); + } } } } - private final class CapacityCheckAction implements BaseQueue.PostEnqueueAction + private final class CapacityCheckAction implements Action> { @Override - public void onEnqueue(final QueueEntry entry) + public void performAction(final MessageInstance entry) { - AMQQueue queue = entry.getQueue(); - queue.checkCapacity(AMQChannel.this); + TransactionLogResource queue = entry.getOwningResource(); + if(queue instanceof CapacityChecker) + { + ((CapacityChecker)queue).checkCapacity(AMQChannel.this); + } } } private class MessageAcknowledgeAction implements ServerTransaction.Action { - private final Collection _ackedMessages; + private final Collection _ackedMessages; - public MessageAcknowledgeAction(Collection ackedMessages) + public MessageAcknowledgeAction(Collection ackedMessages) { _ackedMessages = ackedMessages; } @@ -1314,7 +1287,7 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F { try { - for(QueueEntry entry : _ackedMessages) + for(MessageInstance entry : _ackedMessages) { entry.delete(); } @@ -1337,10 +1310,10 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F { try { - for(QueueEntry entry : _ackedMessages) - { - entry.release(); - } + for(MessageInstance entry : _ackedMessages) + { + entry.release(); + } } finally { @@ -1505,7 +1478,7 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F public void deadLetter(long deliveryTag) throws AMQException { final UnacknowledgedMessageMap unackedMap = getUnacknowledgedMessageMap(); - final QueueEntry rejectedQueueEntry = unackedMap.remove(deliveryTag); + final MessageInstance rejectedQueueEntry = unackedMap.remove(deliveryTag); if (rejectedQueueEntry == null) { @@ -1514,36 +1487,42 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F else { final ServerMessage msg = rejectedQueueEntry.getMessage(); + final Consumer sub = rejectedQueueEntry.getDeliveredConsumer(); - int requeues = rejectedQueueEntry.routeToAlternate(new BaseQueue.PostEnqueueAction() + int requeues = rejectedQueueEntry.routeToAlternate(new Action() { @Override - public void onEnqueue(final QueueEntry requeueEntry) + public void performAction(final MessageInstance requeueEntry) { _actor.message( _logSubject, ChannelMessages.DEADLETTERMSG(msg.getMessageNumber(), - requeueEntry.getQueue().getName())); + requeueEntry.getOwningResource().getName())); } }, null); if(requeues == 0) { - final AMQQueue queue = rejectedQueueEntry.getQueue(); - - final Exchange altExchange = queue.getAlternateExchange(); - if (altExchange == null) + final TransactionLogResource owningResource = rejectedQueueEntry.getOwningResource(); + if(owningResource instanceof AMQQueue) { - _logger.debug("No alternate exchange configured for queue, must discard the message as unable to DLQ: delivery tag: " + deliveryTag); - _actor.message(_logSubject, ChannelMessages.DISCARDMSG_NOALTEXCH(msg.getMessageNumber(), queue.getName(), msg.getRoutingKey())); + final AMQQueue queue = (AMQQueue) owningResource; - } - else - { - _logger.debug( - "Routing process provided no queues to enqueue the message on, must discard message as unable to DLQ: delivery tag: " - + deliveryTag); - _actor.message(_logSubject, - ChannelMessages.DISCARDMSG_NOROUTE(msg.getMessageNumber(), altExchange.getName())); + final Exchange altExchange = queue.getAlternateExchange(); + + if (altExchange == null) + { + _logger.debug("No alternate exchange configured for queue, must discard the message as unable to DLQ: delivery tag: " + deliveryTag); + _actor.message(_logSubject, ChannelMessages.DISCARDMSG_NOALTEXCH(msg.getMessageNumber(), queue.getName(), msg.getRoutingKey())); + + } + else + { + _logger.debug( + "Routing process provided no queues to enqueue the message on, must discard message as unable to DLQ: delivery tag: " + + deliveryTag); + _actor.message(_logSubject, + ChannelMessages.DISCARDMSG_NOROUTE(msg.getMessageNumber(), altExchange.getName())); + } } } @@ -1604,6 +1583,6 @@ public class AMQChannel implements AMQSessionModel, AsyncAutoCommitTransaction.F @Override public int getConsumerCount() { - return _tag2SubscriptionMap.size(); + return _tag2SubscriptionTargetMap.size(); } } diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQProtocolEngine.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQProtocolEngine.java index c7a84fa3b6..e83e86981b 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQProtocolEngine.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQProtocolEngine.java @@ -94,8 +94,7 @@ import org.apache.qpid.server.security.auth.AuthenticatedPrincipal; import org.apache.qpid.server.protocol.v0_8.state.AMQState; import org.apache.qpid.server.protocol.v0_8.state.AMQStateManager; import org.apache.qpid.server.stats.StatisticsCounter; -import org.apache.qpid.server.subscription.ClientDeliveryMethod; -import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.consumer.Consumer; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.transport.Sender; import org.apache.qpid.transport.TransportException; @@ -1669,7 +1668,7 @@ public class AMQProtocolEngine implements ServerProtocolEngine, AMQProtocolSessi } @Override - public void deliverToClient(final Subscription sub, final ServerMessage message, + public void deliverToClient(final Consumer sub, final ServerMessage message, final InstanceProperties props, final long deliveryTag) throws AMQException { @@ -1678,7 +1677,7 @@ public class AMQProtocolEngine implements ServerProtocolEngine, AMQProtocolSessi props, _channelId, deliveryTag, - ((SubscriptionImpl)sub).getConsumerTag()); + new AMQShortString(sub.getName())); } } diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQProtocolSession.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQProtocolSession.java index 85d995518a..6bcd4b9d49 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQProtocolSession.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/AMQProtocolSession.java @@ -39,7 +39,6 @@ import org.apache.qpid.server.logging.LogActor; import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.protocol.v0_8.output.ProtocolOutputConverter; import org.apache.qpid.server.security.AuthorizationHolder; -import org.apache.qpid.server.subscription.ClientDeliveryMethod; import org.apache.qpid.server.virtualhost.VirtualHost; diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/ClientDeliveryMethod.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/ClientDeliveryMethod.java new file mode 100644 index 0000000000..2e362c11f8 --- /dev/null +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/ClientDeliveryMethod.java @@ -0,0 +1,32 @@ +/* +* +* 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.protocol.v0_8; + +import org.apache.qpid.AMQException; +import org.apache.qpid.server.message.InstanceProperties; +import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.consumer.Consumer; + +public interface ClientDeliveryMethod +{ + void deliverToClient(final Consumer sub, final ServerMessage message, final InstanceProperties props, + final long deliveryTag) throws AMQException; +} diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/ConsumerTarget_0_8.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/ConsumerTarget_0_8.java new file mode 100644 index 0000000000..47700f812f --- /dev/null +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/ConsumerTarget_0_8.java @@ -0,0 +1,552 @@ +/* + * + * 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.protocol.v0_8; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.common.AMQPFilterTypes; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.flow.FlowCreditManager; +import org.apache.qpid.server.message.InstanceProperties; +import org.apache.qpid.server.message.MessageInstance; +import org.apache.qpid.server.message.MessageReference; +import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.protocol.AMQSessionModel; +import org.apache.qpid.server.protocol.v0_8.output.ProtocolOutputConverter; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.consumer.AbstractConsumerTarget; +import org.apache.qpid.server.consumer.Consumer; +import org.apache.qpid.server.txn.AutoCommitTransaction; +import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.util.StateChangeListener; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Encapsulation of a subscription to a queue.

Ties together the protocol session of a subscriber, the consumer tag + * that was given out by the broker and the channel id.

+ */ +public abstract class ConsumerTarget_0_8 extends AbstractConsumerTarget implements FlowCreditManager.FlowCreditManagerListener +{ + + private final StateChangeListener _entryReleaseListener = + new StateChangeListener() + { + @Override + public void stateChanged(final MessageInstance entry, + final MessageInstance.State oldSate, + final MessageInstance.State newState) + { + if (oldSate == QueueEntry.State.ACQUIRED && (newState == QueueEntry.State.AVAILABLE || newState == QueueEntry.State.DEQUEUED)) + { + restoreCredit(entry.getMessage()); + } + entry.removeStateChangeListener(this); + } + }; + + private final ClientDeliveryMethod _deliveryMethod; + private final RecordDeliveryMethod _recordMethod; + + private final AtomicLong _unacknowledgedCount = new AtomicLong(0); + private final AtomicLong _unacknowledgedBytes = new AtomicLong(0); + private Consumer _consumer; + + + public static ConsumerTarget_0_8 createBrowserTarget(AMQChannel channel, + AMQShortString consumerTag, FieldTable filters, + FlowCreditManager creditManager) throws AMQException + { + return new BrowserConsumer(channel, consumerTag, filters, creditManager, channel.getClientDeliveryMethod(), channel.getRecordDeliveryMethod()); + } + + static final class BrowserConsumer extends ConsumerTarget_0_8 + { + public BrowserConsumer(AMQChannel channel, + AMQShortString consumerTag, FieldTable filters, + FlowCreditManager creditManager, + ClientDeliveryMethod deliveryMethod, + RecordDeliveryMethod recordMethod) + throws AMQException + { + super(channel, consumerTag, + filters, creditManager, deliveryMethod, recordMethod); + } + + /** + * 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 + * @param batch + * @throws org.apache.qpid.AMQException + */ + @Override + public void send(MessageInstance entry, boolean batch) 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(); + sendToClient(entry.getMessage(), entry.getInstanceProperties(), deliveryTag); + } + + } + + @Override + public boolean allocateCredit(ServerMessage msg) + { + return true; + } + + } + + public static ConsumerTarget_0_8 createNoAckTarget(AMQChannel channel, + AMQShortString consumerTag, FieldTable filters, + FlowCreditManager creditManager) throws AMQException + { + return new NoAckConsumer(channel, consumerTag, filters, creditManager, channel.getClientDeliveryMethod(), channel.getRecordDeliveryMethod()); + } + + public static ConsumerTarget_0_8 createNoAckTarget(AMQChannel channel, + AMQShortString consumerTag, FieldTable filters, + FlowCreditManager creditManager, + ClientDeliveryMethod deliveryMethod, + RecordDeliveryMethod recordMethod) throws AMQException + { + return new NoAckConsumer(channel, consumerTag, filters, creditManager, deliveryMethod, recordMethod); + } + + public static class NoAckConsumer extends ConsumerTarget_0_8 + { + private final AutoCommitTransaction _txn; + + public NoAckConsumer(AMQChannel channel, + AMQShortString consumerTag, FieldTable filters, + FlowCreditManager creditManager, + ClientDeliveryMethod deliveryMethod, + RecordDeliveryMethod recordMethod) + throws AMQException + { + super(channel, consumerTag, filters, creditManager, deliveryMethod, recordMethod); + + _txn = new AutoCommitTransaction(channel.getVirtualHost().getMessageStore()); + } + + /** + * 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 + * @param batch + * @throws org.apache.qpid.AMQException + */ + @Override + public void send(MessageInstance entry, boolean batch) throws AMQException + { + // 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. + _txn.dequeue(entry.getOwningResource(), entry.getMessage(), NOOP); + + ServerMessage message = entry.getMessage(); + MessageReference ref = message.newReference(); + InstanceProperties props = entry.getInstanceProperties(); + entry.delete(); + + synchronized (getChannel()) + { + getChannel().getProtocolSession().setDeferFlush(batch); + long deliveryTag = getChannel().getNextDeliveryTag(); + + sendToClient(message, props, deliveryTag); + + } + ref.release(); + + + } + + @Override + public boolean allocateCredit(ServerMessage msg) + { + return true; + } + + private static final ServerTransaction.Action NOOP = + new ServerTransaction.Action() + { + @Override + public void postCommit() + { + } + + @Override + public void onRollback() + { + } + }; + } + + /** + * NoAck Subscription for use with BasicGet method. + */ + public static final class GetNoAckConsumer extends NoAckConsumer + { + public GetNoAckConsumer(AMQChannel channel, AMQProtocolSession protocolSession, + AMQShortString consumerTag, FieldTable filters, + boolean noLocal, FlowCreditManager creditManager, + ClientDeliveryMethod deliveryMethod, + RecordDeliveryMethod recordMethod) + throws AMQException + { + super(channel, consumerTag, filters, creditManager, deliveryMethod, recordMethod); + } + + public boolean allocateCredit(ServerMessage msg) + { + return getCreditManager().useCreditForMessage(msg.getSize()); + } + + } + + + public static ConsumerTarget_0_8 createAckTarget(AMQChannel channel, + AMQShortString consumerTag, FieldTable filters, + FlowCreditManager creditManager) + throws AMQException + { + return new AckConsumer(channel,consumerTag,filters,creditManager, channel.getClientDeliveryMethod(), channel.getRecordDeliveryMethod()); + } + + + public static ConsumerTarget_0_8 createAckTarget(AMQChannel channel, + AMQShortString consumerTag, FieldTable filters, + FlowCreditManager creditManager, + ClientDeliveryMethod deliveryMethod, + RecordDeliveryMethod recordMethod) + throws AMQException + { + return new AckConsumer(channel,consumerTag,filters,creditManager, deliveryMethod, recordMethod); + } + + static final class AckConsumer extends ConsumerTarget_0_8 + { + public AckConsumer(AMQChannel channel, + AMQShortString consumerTag, FieldTable filters, + FlowCreditManager creditManager, + ClientDeliveryMethod deliveryMethod, + RecordDeliveryMethod recordMethod) + throws AMQException + { + super(channel, consumerTag, filters, creditManager, deliveryMethod, recordMethod); + } + + /** + * 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 + * @param batch + * @throws org.apache.qpid.AMQException + */ + @Override + public void send(MessageInstance entry, boolean batch) throws AMQException + { + + + synchronized (getChannel()) + { + getChannel().getProtocolSession().setDeferFlush(batch); + long deliveryTag = getChannel().getNextDeliveryTag(); + + addUnacknowledgedMessage(entry); + recordMessageDelivery(entry, deliveryTag); + entry.addStateChangeListener(getReleasedStateChangeListener()); + sendToClient(entry.getMessage(), entry.getInstanceProperties(), deliveryTag); + entry.incrementDeliveryCount(); + + } + } + + + + } + + + private static final Logger _logger = Logger.getLogger(ConsumerTarget_0_8.class); + + private final AMQChannel _channel; + + private final AMQShortString _consumerTag; + + private final FlowCreditManager _creditManager; + + private final Boolean _autoClose; + + private final AtomicBoolean _deleted = new AtomicBoolean(false); + + + + + public ConsumerTarget_0_8(AMQChannel channel, + AMQShortString consumerTag, + FieldTable arguments, + FlowCreditManager creditManager, + ClientDeliveryMethod deliveryMethod, + RecordDeliveryMethod recordMethod) + throws AMQException + { + super(State.ACTIVE); + + _channel = channel; + _consumerTag = consumerTag; + + _creditManager = creditManager; + creditManager.addStateListener(this); + + _deliveryMethod = deliveryMethod; + _recordMethod = recordMethod; + + if (arguments != null) + { + Object autoClose = arguments.get(AMQPFilterTypes.AUTO_CLOSE.getValue()); + if (autoClose != null) + { + _autoClose = (Boolean) autoClose; + } + else + { + _autoClose = false; + } + } + else + { + _autoClose = false; + } + } + + public Consumer getConsumer() + { + return _consumer; + } + + @Override + public void consumerRemoved(final Consumer sub) + { + } + + @Override + public void consumerAdded(final Consumer sub) + { + _consumer = sub; + } + + public AMQSessionModel getSessionModel() + { + return _channel; + } + + public String toString() + { + String subscriber = "[channel=" + _channel + + ", consumerTag=" + _consumerTag + + ", session=" + getProtocolSession().getKey() ; + + return subscriber + "]"; + } + + public boolean isSuspended() + { + return getState()!=State.ACTIVE || _channel.isSuspended() || _deleted.get() || _channel.getConnectionModel().isStopped(); + } + + /** + * Callback indicating that a queue has been deleted. + * + */ + public void queueDeleted() + { + _deleted.set(true); + } + + public boolean isAutoClose() + { + return _autoClose; + } + + public FlowCreditManager getCreditManager() + { + return _creditManager; + } + + + public boolean close() + { + boolean closed = false; + State state = getState(); + + getConsumer().getSendLock(); + try + { + while(!closed && state != State.CLOSED) + { + closed = updateState(state, State.CLOSED); + if(!closed) + { + state = getState(); + } + } + _creditManager.removeListener(this); + return closed; + } + finally + { + getConsumer().releaseSendLock(); + } + } + + + public boolean allocateCredit(ServerMessage msg) + { + return _creditManager.useCreditForMessage(msg.getSize()); + } + + public AMQChannel getChannel() + { + return _channel; + } + + public AMQShortString getConsumerTag() + { + return _consumerTag; + } + + public AMQProtocolSession getProtocolSession() + { + return _channel.getProtocolSession(); + } + + public void restoreCredit(final ServerMessage message) + { + _creditManager.restoreCredit(1, message.getSize()); + } + + protected final StateChangeListener getReleasedStateChangeListener() + { + return _entryReleaseListener; + } + + public void creditStateChanged(boolean hasCredit) + { + + if(hasCredit) + { + if(!updateState(State.SUSPENDED, State.ACTIVE)) + { + // this is a hack to get round the issue of increasing bytes credit + getStateListener().stateChanged(this, State.ACTIVE, State.ACTIVE); + } + } + else + { + updateState(State.ACTIVE, State.SUSPENDED); + } + } + + protected void sendToClient(final ServerMessage message, final InstanceProperties props, final long deliveryTag) + throws AMQException + { + _deliveryMethod.deliverToClient(getConsumer(), message, props, deliveryTag); + + } + + + protected void recordMessageDelivery(final MessageInstance entry, final long deliveryTag) + { + _recordMethod.recordMessageDelivery(getConsumer(),entry,deliveryTag); + } + + + public void confirmAutoClose() + { + ProtocolOutputConverter converter = getChannel().getProtocolSession().getProtocolOutputConverter(); + converter.confirmConsumerAutoClose(getChannel().getChannelId(), getConsumerTag()); + } + + public void queueEmpty() throws AMQException + { + if (isAutoClose()) + { + close(); + confirmAutoClose(); + } + } + + public void flushBatched() + { + _channel.getProtocolSession().setDeferFlush(false); + + _channel.getProtocolSession().flushBatched(); + } + + protected void addUnacknowledgedMessage(MessageInstance entry) + { + final long size = entry.getMessage().getSize(); + _unacknowledgedBytes.addAndGet(size); + _unacknowledgedCount.incrementAndGet(); + entry.addStateChangeListener(new StateChangeListener() + { + public void stateChanged(MessageInstance entry, MessageInstance.State oldState, MessageInstance.State newState) + { + if(oldState.equals(MessageInstance.State.ACQUIRED) && !newState.equals(MessageInstance.State.ACQUIRED)) + { + _unacknowledgedBytes.addAndGet(-size); + _unacknowledgedCount.decrementAndGet(); + entry.removeStateChangeListener(this); + } + } + }); + } + + public long getUnacknowledgedBytes() + { + return _unacknowledgedBytes.longValue(); + } + + public long getUnacknowledgedMessages() + { + return _unacknowledgedCount.longValue(); + } +} diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/ExtractResendAndRequeue.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/ExtractResendAndRequeue.java index 060aebdd65..1de1638c2e 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/ExtractResendAndRequeue.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/ExtractResendAndRequeue.java @@ -23,11 +23,8 @@ package org.apache.qpid.server.protocol.v0_8; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; -import org.apache.qpid.server.queue.QueueEntry; -import org.apache.qpid.server.store.MessageStore; -import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.txn.AutoCommitTransaction; -import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.message.MessageInstance; +import org.apache.qpid.server.consumer.Consumer; import java.util.Map; @@ -35,34 +32,28 @@ public class ExtractResendAndRequeue implements UnacknowledgedMessageMap.Visitor { private static final Logger _log = Logger.getLogger(ExtractResendAndRequeue.class); - private final Map _msgToRequeue; - private final Map _msgToResend; - private final boolean _requeueIfUnableToResend; + private final Map _msgToRequeue; + private final Map _msgToResend; private final UnacknowledgedMessageMap _unacknowledgedMessageMap; - private final MessageStore _transactionLog; public ExtractResendAndRequeue(UnacknowledgedMessageMap unacknowledgedMessageMap, - Map msgToRequeue, - Map msgToResend, - boolean requeueIfUnableToResend, - MessageStore txnLog) + Map msgToRequeue, + Map msgToResend) { _unacknowledgedMessageMap = unacknowledgedMessageMap; _msgToRequeue = msgToRequeue; _msgToResend = msgToResend; - _requeueIfUnableToResend = requeueIfUnableToResend; - _transactionLog = txnLog; } - public boolean callback(final long deliveryTag, QueueEntry message) throws AMQException + public boolean callback(final long deliveryTag, MessageInstance message) throws AMQException { message.setRedelivered(); - final Subscription subscription = message.getDeliveredSubscription(); - if (subscription != null) + final Consumer consumer = message.getDeliveredConsumer(); + if (consumer != null) { // Consumer exists - if (!subscription.isClosed()) + if (!consumer.isClosed()) { _msgToResend.put(deliveryTag, message); } @@ -73,58 +64,13 @@ public class ExtractResendAndRequeue implements UnacknowledgedMessageMap.Visitor } else { - // Message has no consumer tag, so was "delivered" to a GET - // or consumer no longer registered - // cannot resend, so re-queue. - if (!message.isQueueDeleted()) - { - if (_requeueIfUnableToResend) - { - _msgToRequeue.put(deliveryTag, message); - } - else - { - - dequeueEntry(message); - _log.info("No DeadLetter Queue and requeue not requested so dropping message:" + message); - } - } - else - { - dequeueEntry(message); - _log.warn("Message.queue is null and no DeadLetter Queue so dropping message:" + message); - } + _log.info("No DeadLetter Queue and requeue not requested so dropping message:" + message); } // false means continue processing return false; } - - private void dequeueEntry(final QueueEntry node) - { - ServerTransaction txn = new AutoCommitTransaction(_transactionLog); - dequeueEntry(node, txn); - } - - private void dequeueEntry(final QueueEntry node, ServerTransaction txn) - { - txn.dequeue(node.getQueue(), node.getMessage(), - new ServerTransaction.Action() - { - - public void postCommit() - { - node.delete(); - } - - public void onRollback() - { - - } - }); - } - public void visitComplete() { _unacknowledgedMessageMap.clear(); diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/IncomingMessage.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/IncomingMessage.java index 5a9a51ff59..80c4c77b65 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/IncomingMessage.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/IncomingMessage.java @@ -20,15 +20,12 @@ */ package org.apache.qpid.server.protocol.v0_8; -import org.apache.log4j.Logger; - import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.ContentBody; import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.abstraction.MessagePublishInfo; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.store.StoredMessage; +import org.apache.qpid.server.message.MessageDestination; import java.util.ArrayList; import java.util.List; @@ -38,7 +35,7 @@ public class IncomingMessage private final MessagePublishInfo _messagePublishInfo; private ContentHeaderBody _contentHeaderBody; - private Exchange _exchange; + private MessageDestination _messageDestination; /** * Keeps a track of how many bytes we have received in body frames @@ -77,9 +74,9 @@ public class IncomingMessage return _messagePublishInfo.getExchange(); } - public Exchange getExchange() + public MessageDestination getDestination() { - return _exchange; + return _messageDestination; } public ContentHeaderBody getContentHeader() @@ -92,9 +89,9 @@ public class IncomingMessage return getContentHeader().getBodySize(); } - public void setExchange(final Exchange e) + public void setMessageDestination(final MessageDestination e) { - _exchange = e; + _messageDestination = e; } public int getBodyCount() throws AMQException diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/MessageMetaData.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/MessageMetaData.java index ead28c6e26..3665e7f135 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/MessageMetaData.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/MessageMetaData.java @@ -105,7 +105,7 @@ public class MessageMetaData implements StorableMessageMetaData } - public int writeToBuffer(int offset, ByteBuffer dest) + public int writeToBuffer(ByteBuffer dest) { int oldPosition = dest.position(); try diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/RecordDeliveryMethod.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/RecordDeliveryMethod.java new file mode 100644 index 0000000000..70d7da3432 --- /dev/null +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/RecordDeliveryMethod.java @@ -0,0 +1,29 @@ +/* +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +* +*/ +package org.apache.qpid.server.protocol.v0_8; + +import org.apache.qpid.server.message.MessageInstance; +import org.apache.qpid.server.consumer.Consumer; + +public interface RecordDeliveryMethod +{ + void recordMessageDelivery(final Consumer sub, final MessageInstance entry, final long deliveryTag); +} diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/SubscriptionFactory.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/SubscriptionFactory.java deleted file mode 100644 index 6646dc0cc2..0000000000 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/SubscriptionFactory.java +++ /dev/null @@ -1,68 +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.protocol.v0_8; - -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.server.flow.FlowCreditManager; -import org.apache.qpid.server.subscription.ClientDeliveryMethod; -import org.apache.qpid.server.subscription.RecordDeliveryMethod; -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 - * primarily assists testing although in future more sophisticated subscribers may need a different subscription - * implementation. - * - * @see org.apache.qpid.server.queue.AMQQueue - */ -public interface SubscriptionFactory -{ - Subscription createSubscription(int channel, - AMQProtocolSession protocolSession, - AMQShortString consumerTag, - boolean acks, - FieldTable filters, - boolean noLocal, FlowCreditManager creditManager) throws AMQException; - - - Subscription createSubscription(AMQChannel channel, - AMQProtocolSession protocolSession, - AMQShortString consumerTag, - boolean acks, - FieldTable filters, - boolean noLocal, - FlowCreditManager creditManager, - ClientDeliveryMethod clientMethod, - RecordDeliveryMethod recordMethod) throws AMQException; - - - Subscription createBasicGetNoAckSubscription(AMQChannel channel, - AMQProtocolSession session, - AMQShortString consumerTag, - FieldTable filters, - boolean noLocal, - FlowCreditManager creditManager, - ClientDeliveryMethod deliveryMethod, - RecordDeliveryMethod recordMethod) throws AMQException; - -} diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/SubscriptionFactoryImpl.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/SubscriptionFactoryImpl.java deleted file mode 100644 index 93b51a0567..0000000000 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/SubscriptionFactoryImpl.java +++ /dev/null @@ -1,109 +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.protocol.v0_8; - -import org.apache.qpid.AMQException; -import org.apache.qpid.common.AMQPFilterTypes; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.server.flow.FlowCreditManager; -import org.apache.qpid.server.subscription.ClientDeliveryMethod; -import org.apache.qpid.server.subscription.RecordDeliveryMethod; -import org.apache.qpid.server.subscription.Subscription; - -public class SubscriptionFactoryImpl implements SubscriptionFactory -{ - - public Subscription createSubscription(int channelId, AMQProtocolSession protocolSession, - AMQShortString consumerTag, boolean acks, FieldTable filters, - boolean noLocal, FlowCreditManager creditManager) throws AMQException - { - AMQChannel channel = protocolSession.getChannel(channelId); - if (channel == null) - { - throw new AMQException(AMQConstant.NOT_FOUND, "channel :" + channelId + " not found in protocol session"); - } - ClientDeliveryMethod clientMethod = channel.getClientDeliveryMethod(); - RecordDeliveryMethod recordMethod = channel.getRecordDeliveryMethod(); - - - return createSubscription(channel, protocolSession, consumerTag, acks, filters, - noLocal, - creditManager, - clientMethod, - recordMethod - ); - } - - public Subscription createSubscription(final AMQChannel channel, - final AMQProtocolSession protocolSession, - final AMQShortString consumerTag, - final boolean acks, - final FieldTable filters, - final boolean noLocal, - final FlowCreditManager creditManager, - final ClientDeliveryMethod clientMethod, - final RecordDeliveryMethod recordMethod - ) - throws AMQException - { - boolean isBrowser; - - if (filters != null) - { - 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, clientMethod, recordMethod); - } - else if(acks) - { - return new SubscriptionImpl.AckSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager, clientMethod, recordMethod); - } - else - { - return new SubscriptionImpl.NoAckSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager, clientMethod, recordMethod); - } - } - - public SubscriptionImpl.GetNoAckSubscription createBasicGetNoAckSubscription(final AMQChannel channel, - final AMQProtocolSession session, - final AMQShortString consumerTag, - final FieldTable filters, - final boolean noLocal, - final FlowCreditManager creditManager, - final ClientDeliveryMethod deliveryMethod, - final RecordDeliveryMethod recordMethod) throws AMQException - { - return new SubscriptionImpl.GetNoAckSubscription(channel, session, null, null, false, creditManager, deliveryMethod, recordMethod); - } - - public static final SubscriptionFactoryImpl INSTANCE = new SubscriptionFactoryImpl(); - -} diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/SubscriptionImpl.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/SubscriptionImpl.java deleted file mode 100644 index 7c52fbe3b0..0000000000 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/SubscriptionImpl.java +++ /dev/null @@ -1,858 +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.protocol.v0_8; - -import org.apache.log4j.Logger; - -import org.apache.qpid.AMQException; -import org.apache.qpid.common.AMQPFilterTypes; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.server.filter.FilterManager; -import org.apache.qpid.server.filter.FilterManagerFactory; -import org.apache.qpid.server.flow.FlowCreditManager; -import org.apache.qpid.server.logging.LogActor; -import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.actors.SubscriptionActor; -import org.apache.qpid.server.logging.messages.SubscriptionMessages; -import org.apache.qpid.server.logging.subjects.SubscriptionLogSubject; -import org.apache.qpid.server.message.InstanceProperties; -import org.apache.qpid.server.message.MessageReference; -import org.apache.qpid.server.message.ServerMessage; -import org.apache.qpid.server.protocol.MessageConverterRegistry; -import org.apache.qpid.server.protocol.v0_8.output.ProtocolOutputConverter; -import org.apache.qpid.server.protocol.AMQSessionModel; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.QueueEntry; -import org.apache.qpid.server.subscription.ClientDeliveryMethod; -import org.apache.qpid.server.subscription.RecordDeliveryMethod; -import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.txn.AutoCommitTransaction; -import org.apache.qpid.server.txn.ServerTransaction; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -/** - * Encapsulation of a subscription to a queue.

Ties together the protocol session of a subscriber, the consumer tag - * that was given out by the broker and the channel id.

- */ -public abstract class SubscriptionImpl implements Subscription, FlowCreditManager.FlowCreditManagerListener -{ - - private StateListener _stateListener = new StateListener() - { - - public void stateChange(Subscription sub, State oldState, State newState) - { - - } - }; - - - private final AtomicReference _state = new AtomicReference(State.ACTIVE); - private volatile AMQQueue.Context _queueContext; - - private final ClientDeliveryMethod _deliveryMethod; - private final RecordDeliveryMethod _recordMethod; - - private final QueueEntry.SubscriptionAcquiredState _owningState = new QueueEntry.SubscriptionAcquiredState(this); - - private final Map _properties = new ConcurrentHashMap(); - - private final Lock _stateChangeLock; - - private final long _subscriptionID; - private LogSubject _logSubject; - private LogActor _logActor; - private final AtomicLong _deliveredCount = new AtomicLong(0); - private final AtomicLong _deliveredBytes = new AtomicLong(0); - - private final AtomicLong _unacknowledgedCount = new AtomicLong(0); - private final AtomicLong _unacknowledgedBytes = new AtomicLong(0); - - private long _createTime = System.currentTimeMillis(); - - - static final class BrowserSubscription extends SubscriptionImpl - { - public BrowserSubscription(AMQChannel channel, AMQProtocolSession protocolSession, - AMQShortString consumerTag, FieldTable filters, - boolean noLocal, FlowCreditManager creditManager, - ClientDeliveryMethod deliveryMethod, - RecordDeliveryMethod recordMethod) - throws AMQException - { - super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod); - } - - - public boolean 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 entry - * @param batch - * @throws AMQException - */ - @Override - public void send(QueueEntry entry, boolean batch) 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(); - sendToClient(entry.getMessage(), entry.getInstanceProperties(), deliveryTag); - } - - } - - @Override - public boolean wouldSuspend(QueueEntry msg) - { - return false; - } - - } - - public static class NoAckSubscription extends SubscriptionImpl - { - private final AutoCommitTransaction _txn; - - public NoAckSubscription(AMQChannel channel, AMQProtocolSession protocolSession, - AMQShortString consumerTag, FieldTable filters, - boolean noLocal, FlowCreditManager creditManager, - ClientDeliveryMethod deliveryMethod, - RecordDeliveryMethod recordMethod) - throws AMQException - { - super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod); - _txn = new AutoCommitTransaction(protocolSession.getVirtualHost().getMessageStore()); - } - - - public boolean isBrowser() - { - return false; - } - - @Override - public boolean isExplicitAcknowledge() - { - 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 - * @param batch - * @throws AMQException - */ - @Override - public void send(QueueEntry entry, boolean batch) throws AMQException - { - // 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. - _txn.dequeue(getQueue(), entry.getMessage(), NOOP); - - ServerMessage message = entry.getMessage(); - MessageReference ref = message.newReference(); - InstanceProperties props = entry.getInstanceProperties(); - entry.delete(); - - synchronized (getChannel()) - { - getChannel().getProtocolSession().setDeferFlush(batch); - long deliveryTag = getChannel().getNextDeliveryTag(); - - sendToClient(message, props, deliveryTag); - - } - ref.release(); - - - } - - @Override - public boolean wouldSuspend(QueueEntry msg) - { - return false; - } - - private static final ServerTransaction.Action NOOP = - new ServerTransaction.Action() - { - @Override - public void postCommit() - { - } - - @Override - public void onRollback() - { - } - }; - } - - /** - * NoAck Subscription for use with BasicGet method. - */ - public static final class GetNoAckSubscription extends SubscriptionImpl.NoAckSubscription - { - public GetNoAckSubscription(AMQChannel channel, AMQProtocolSession protocolSession, - AMQShortString consumerTag, FieldTable filters, - boolean noLocal, FlowCreditManager creditManager, - ClientDeliveryMethod deliveryMethod, - RecordDeliveryMethod recordMethod) - throws AMQException - { - super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod); - } - - public boolean isTransient() - { - return true; - } - - public boolean wouldSuspend(QueueEntry msg) - { - return !getCreditManager().useCreditForMessage(msg.getMessage().getSize()); - } - - } - - static final class AckSubscription extends SubscriptionImpl - { - public AckSubscription(AMQChannel channel, AMQProtocolSession protocolSession, - AMQShortString consumerTag, FieldTable filters, - boolean noLocal, FlowCreditManager creditManager, - ClientDeliveryMethod deliveryMethod, - RecordDeliveryMethod recordMethod) - throws AMQException - { - super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod); - } - - - public boolean 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 - * @param batch - * @throws AMQException - */ - @Override - public void send(QueueEntry entry, boolean batch) throws AMQException - { - - - synchronized (getChannel()) - { - getChannel().getProtocolSession().setDeferFlush(batch); - long deliveryTag = getChannel().getNextDeliveryTag(); - - addUnacknowledgedMessage(entry); - recordMessageDelivery(entry, deliveryTag); - sendToClient(entry.getMessage(), entry.getInstanceProperties(), deliveryTag); - entry.incrementDeliveryCount(); - - } - } - - - - } - - - private static final Logger _logger = Logger.getLogger(SubscriptionImpl.class); - - private final AMQChannel _channel; - - private final AMQShortString _consumerTag; - - - private boolean _noLocal; - - private final FlowCreditManager _creditManager; - - private FilterManager _filters; - - private final Boolean _autoClose; - - private AMQQueue _queue; - private final AtomicBoolean _deleted = new AtomicBoolean(false); - - - - - public SubscriptionImpl(AMQChannel channel, AMQProtocolSession protocolSession, - AMQShortString consumerTag, FieldTable arguments, - boolean noLocal, FlowCreditManager creditManager, - ClientDeliveryMethod deliveryMethod, - RecordDeliveryMethod recordMethod) - throws AMQException - { - _subscriptionID = SUB_ID_GENERATOR.getAndIncrement(); - _channel = channel; - _consumerTag = consumerTag; - - _creditManager = creditManager; - creditManager.addStateListener(this); - - _noLocal = noLocal; - - - _filters = FilterManagerFactory.createManager(FieldTable.convertToMap(arguments)); - - _deliveryMethod = deliveryMethod; - _recordMethod = recordMethod; - - - _stateChangeLock = new ReentrantLock(); - - - if (arguments != null) - { - Object autoClose = arguments.get(AMQPFilterTypes.AUTO_CLOSE.getValue()); - if (autoClose != null) - { - _autoClose = (Boolean) autoClose; - } - else - { - _autoClose = false; - } - } - else - { - _autoClose = false; - } - - } - - public AMQSessionModel getSessionModel() - { - return _channel; - } - - public Long getDelivered() - { - return _deliveredCount.get(); - } - - public synchronized void setQueue(AMQQueue queue, boolean exclusive) - { - if(getQueue() != null) - { - throw new IllegalStateException("Attempt to set queue for subscription " + this + " to " + queue + "when already set to " + getQueue()); - } - _queue = queue; - - _logSubject = new SubscriptionLogSubject(this); - _logActor = new SubscriptionActor(CurrentActor.get().getRootMessageLogger(), this); - - if (CurrentActor.get().getRootMessageLogger(). - isMessageEnabled(CurrentActor.get(), _logSubject, SubscriptionMessages.CREATE_LOG_HIERARCHY)) - { - // Get the string value of the filters - String filterLogString = null; - if (_filters != null && _filters.hasFilters()) - { - filterLogString = _filters.toString(); - } - - if (isAutoClose()) - { - if (filterLogString == null) - { - filterLogString = ""; - } - else - { - filterLogString += ","; - } - filterLogString += "AutoClose"; - } - - if (isBrowser()) - { - // We do not need to check for null here as all Browsers are AutoClose - filterLogString +=",Browser"; - } - - CurrentActor.get(). - message(_logSubject, - SubscriptionMessages.CREATE(filterLogString, - queue.isDurable() && exclusive, - filterLogString != null)); - } - } - - 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 entry - * @param batch - * @throws AMQException - */ - abstract public void send(QueueEntry entry, boolean batch) throws AMQException; - - - public boolean isSuspended() - { - return !isActive() || _channel.isSuspended() || _deleted.get() || _channel.getConnectionModel().isStopped(); - } - - /** - * Callback indicating that a queue has been deleted. - * - * @param queue The queue to delete - */ - public void queueDeleted(AMQQueue queue) - { - _deleted.set(true); - } - - public boolean hasInterest(QueueEntry entry) - { - //check that the message hasn't been rejected - if (entry.isRejectedBy(getSubscriptionID())) - { - if (_logger.isDebugEnabled()) - { - _logger.debug("Subscription:" + this + " rejected message:" + entry); - } - } - - if(entry.getMessage() instanceof AMQMessage) - { - if (_noLocal) - { - AMQMessage message = (AMQMessage) entry.getMessage(); - - final Object publisherReference = message.getConnectionReference(); - - // We don't want local messages so check to see if message is one we sent - Object localReference = getProtocolSession().getReference(); - - if(publisherReference != null && publisherReference.equals(localReference)) - { - return false; - } - } - } - else - { - // No interest in messages we can't convert to AMQMessage - if(MessageConverterRegistry.getConverter(entry.getMessage().getClass(), AMQMessage.class)==null) - { - return false; - } - } - - - if (_logger.isDebugEnabled()) - { - _logger.debug("(" + this + ") checking filters for message (" + entry); - } - return checkFilters(entry); - - } - - private boolean checkFilters(QueueEntry msg) - { - return (_filters == null) || _filters.allAllow(msg.asFilterable()); - } - - public boolean isAutoClose() - { - return _autoClose; - } - - public FlowCreditManager getCreditManager() - { - return _creditManager; - } - - - public void close() - { - boolean closed = false; - State state = getState(); - - _stateChangeLock.lock(); - try - { - 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); - } - finally - { - _stateChangeLock.unlock(); - } - //Log Subscription closed - CurrentActor.get().message(_logSubject, SubscriptionMessages.CLOSE()); - } - - public boolean isClosed() - { - return getState() == State.CLOSED; - } - - - public boolean wouldSuspend(QueueEntry msg) - { - return !_creditManager.useCreditForMessage(msg.getMessage().getSize()); - } - - public boolean trySendLock() - { - return _stateChangeLock.tryLock(); - } - - public void getSendLock() - { - _stateChangeLock.lock(); - } - - public void releaseSendLock() - { - _stateChangeLock.unlock(); - } - - public AMQChannel getChannel() - { - return _channel; - } - - public AMQShortString getConsumerTag() - { - return _consumerTag; - } - - public String getConsumerName() - { - return _consumerTag == null ? null : _consumerTag.asString(); - } - - public long getSubscriptionID() - { - return _subscriptionID; - } - - public AMQProtocolSession getProtocolSession() - { - return _channel.getProtocolSession(); - } - - public LogActor getLogActor() - { - return _logActor; - } - - public AMQQueue getQueue() - { - return _queue; - } - - public void onDequeue(final QueueEntry queueEntry) - { - restoreCredit(queueEntry); - } - - public void releaseQueueEntry(final QueueEntry queueEntry) - { - restoreCredit(queueEntry); - } - - public void restoreCredit(final QueueEntry queueEntry) - { - _creditManager.restoreCredit(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 - { - // this is a hack to get round the issue of increasing bytes credit - _stateListener.stateChange(this, State.ACTIVE, State.ACTIVE); - } - } - else - { - if(_state.compareAndSet(State.ACTIVE, State.SUSPENDED)) - { - _stateListener.stateChange(this, State.ACTIVE, State.SUSPENDED); - } - } - CurrentActor.get().message(_logSubject,SubscriptionMessages.STATE(_state.get().toString())); - } - - public State getState() - { - return _state.get(); - } - - - public void setStateListener(final StateListener listener) - { - _stateListener = listener; - } - - - public AMQQueue.Context getQueueContext() - { - return _queueContext; - } - - public void setQueueContext(AMQQueue.Context context) - { - _queueContext = context; - } - - - protected void sendToClient(final ServerMessage message, final InstanceProperties props, final long deliveryTag) - throws AMQException - { - _deliveryMethod.deliverToClient(this, message, props, deliveryTag); - _deliveredCount.incrementAndGet(); - _deliveredBytes.addAndGet(message.getSize()); - } - - - protected void recordMessageDelivery(final QueueEntry entry, final long deliveryTag) - { - _recordMethod.recordMessageDelivery(this,entry,deliveryTag); - } - - - public boolean isActive() - { - return getState() == State.ACTIVE; - } - - public QueueEntry.SubscriptionAcquiredState getOwningState() - { - return _owningState; - } - - public void confirmAutoClose() - { - ProtocolOutputConverter converter = getChannel().getProtocolSession().getProtocolOutputConverter(); - converter.confirmConsumerAutoClose(getChannel().getChannelId(), getConsumerTag()); - } - - public boolean acquires() - { - return !isBrowser(); - } - - public boolean seesRequeues() - { - return !isBrowser(); - } - - public boolean isTransient() - { - return false; - } - - public void set(String key, Object value) - { - _properties.put(key, value); - } - - public Object get(String key) - { - return _properties.get(key); - } - - - public void setNoLocal(boolean noLocal) - { - _noLocal = noLocal; - } - - abstract boolean isBrowser(); - - public String getCreditMode() - { - return "WINDOW"; - } - - public boolean isBrowsing() - { - return isBrowser(); - } - - public boolean isExplicitAcknowledge() - { - return true; - } - - public boolean isDurable() - { - return false; - } - - public boolean isExclusive() - { - return getQueue().hasExclusiveSubscriber(); - } - - public String getName() - { - return String.valueOf(_consumerTag); - } - - public Map getArguments() - { - return null; - } - - public boolean isSessionTransactional() - { - return _channel.isTransactional(); - } - - public long getCreateTime() - { - return _createTime; - } - - public void queueEmpty() throws AMQException - { - if (isAutoClose()) - { - _queue.unregisterSubscription(this); - - confirmAutoClose(); - } - } - - public void flushBatched() - { - _channel.getProtocolSession().setDeferFlush(false); - - _channel.getProtocolSession().flushBatched(); - } - - public long getBytesOut() - { - return _deliveredBytes.longValue(); - } - - public long getMessagesOut() - { - return _deliveredCount.longValue(); - } - - - protected void addUnacknowledgedMessage(QueueEntry entry) - { - final long size = entry.getSize(); - _unacknowledgedBytes.addAndGet(size); - _unacknowledgedCount.incrementAndGet(); - entry.addStateChangeListener(new QueueEntry.StateChangeListener() - { - public void stateChanged(QueueEntry entry, QueueEntry.State oldState, QueueEntry.State newState) - { - if(oldState.equals(QueueEntry.State.ACQUIRED) && !newState.equals(QueueEntry.State.ACQUIRED)) - { - _unacknowledgedBytes.addAndGet(-size); - _unacknowledgedCount.decrementAndGet(); - entry.removeStateChangeListener(this); - } - } - }); - } - - public long getUnacknowledgedBytes() - { - return _unacknowledgedBytes.longValue(); - } - - public long getUnacknowledgedMessages() - { - return _unacknowledgedCount.longValue(); - } -} diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/UnacknowledgedMessageMap.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/UnacknowledgedMessageMap.java index 1d41bcdcf4..fcbbadd507 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/UnacknowledgedMessageMap.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/UnacknowledgedMessageMap.java @@ -21,6 +21,7 @@ package org.apache.qpid.server.protocol.v0_8; import org.apache.qpid.AMQException; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.queue.QueueEntry; import java.util.Collection; @@ -36,24 +37,24 @@ public interface UnacknowledgedMessageMap *@param message the message being iterated over @return true to stop iteration, false to continue * @throws AMQException */ - boolean callback(final long deliveryTag, QueueEntry message) throws AMQException; + boolean callback(final long deliveryTag, MessageInstance message) throws AMQException; void visitComplete(); } void visit(Visitor visitor) throws AMQException; - void add(long deliveryTag, QueueEntry message); + void add(long deliveryTag, MessageInstance message); - QueueEntry remove(long deliveryTag); + MessageInstance remove(long deliveryTag); - Collection cancelAllMessages(); + Collection cancelAllMessages(); int size(); void clear(); - QueueEntry get(long deliveryTag); + MessageInstance get(long deliveryTag); /** * Get the set of delivery tags that are outstanding. @@ -62,7 +63,7 @@ public interface UnacknowledgedMessageMap */ Set getDeliveryTags(); - Collection acknowledge(long deliveryTag, boolean multiple); + Collection acknowledge(long deliveryTag, boolean multiple); } diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/UnacknowledgedMessageMapImpl.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/UnacknowledgedMessageMapImpl.java index 17b2c7b985..8d70e769d3 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/UnacknowledgedMessageMapImpl.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/UnacknowledgedMessageMapImpl.java @@ -21,6 +21,7 @@ package org.apache.qpid.server.protocol.v0_8; import org.apache.qpid.AMQException; +import org.apache.qpid.server.message.MessageInstance; import org.apache.qpid.server.queue.QueueEntry; import java.util.Collection; @@ -34,7 +35,7 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap private long _unackedSize; - private Map _map; + private Map _map; private long _lastDeliveryTag; @@ -43,10 +44,10 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap public UnacknowledgedMessageMapImpl(int prefetchLimit) { _prefetchLimit = prefetchLimit; - _map = new LinkedHashMap(prefetchLimit); + _map = new LinkedHashMap(prefetchLimit); } - public void collect(long deliveryTag, boolean multiple, Map msgs) + public void collect(long deliveryTag, boolean multiple, Map msgs) { if (multiple) { @@ -54,7 +55,7 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap } else { - final QueueEntry entry = get(deliveryTag); + final MessageInstance entry = get(deliveryTag); if(entry != null) { msgs.put(deliveryTag, entry); @@ -63,7 +64,7 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap } - public void remove(Map msgs) + public void remove(Map msgs) { synchronized (_lock) { @@ -74,12 +75,12 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap } } - public QueueEntry remove(long deliveryTag) + public MessageInstance remove(long deliveryTag) { synchronized (_lock) { - QueueEntry message = _map.remove(deliveryTag); + MessageInstance message = _map.remove(deliveryTag); if(message != null) { _unackedSize -= message.getMessage().getSize(); @@ -94,8 +95,8 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap { synchronized (_lock) { - Set> currentEntries = _map.entrySet(); - for (Map.Entry entry : currentEntries) + Set> currentEntries = _map.entrySet(); + for (Map.Entry entry : currentEntries) { visitor.callback(entry.getKey().longValue(), entry.getValue()); } @@ -103,7 +104,7 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap } } - public void add(long deliveryTag, QueueEntry message) + public void add(long deliveryTag, MessageInstance message) { synchronized (_lock) { @@ -113,12 +114,12 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap } } - public Collection cancelAllMessages() + public Collection cancelAllMessages() { synchronized (_lock) { - Collection currentEntries = _map.values(); - _map = new LinkedHashMap(_prefetchLimit); + Collection currentEntries = _map.values(); + _map = new LinkedHashMap(_prefetchLimit); _unackedSize = 0l; return currentEntries; } @@ -141,7 +142,7 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap } } - public QueueEntry get(long key) + public MessageInstance get(long key) { synchronized (_lock) { @@ -157,19 +158,19 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap } } - public Collection acknowledge(long deliveryTag, boolean multiple) + public Collection acknowledge(long deliveryTag, boolean multiple) { - Map ackedMessageMap = new LinkedHashMap(); + Map ackedMessageMap = new LinkedHashMap(); collect(deliveryTag, multiple, ackedMessageMap); remove(ackedMessageMap); return ackedMessageMap.values(); } - private void collect(long key, Map msgs) + private void collect(long key, Map msgs) { synchronized (_lock) { - for (Map.Entry entry : _map.entrySet()) + for (Map.Entry entry : _map.entrySet()) { msgs.put(entry.getKey(),entry.getValue()); if (entry.getKey() == key) diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/BasicConsumeMethodHandler.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/BasicConsumeMethodHandler.java index 836de44f4e..526bc9b9fe 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/BasicConsumeMethodHandler.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/BasicConsumeMethodHandler.java @@ -28,6 +28,7 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicConsumeBody; import org.apache.qpid.framing.MethodRegistry; import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.message.MessageSource; import org.apache.qpid.server.protocol.v0_8.AMQChannel; import org.apache.qpid.server.protocol.v0_8.AMQProtocolSession; import org.apache.qpid.server.protocol.AMQSessionModel; @@ -73,7 +74,7 @@ public class BasicConsumeMethodHandler implements StateAwareMethodListener { private static final Logger _log = Logger.getLogger(BasicGetMethodHandler.class); @@ -128,7 +132,7 @@ public class BasicGetMethodHandler implements StateAwareMethodListener options = EnumSet.of(Consumer.Option.TRANSIENT, Consumer.Option.ACQUIRES, + Consumer.Option.SEES_REQUEUES); if(acks) { - sub = SubscriptionFactoryImpl.INSTANCE.createSubscription(channel, session, null, acks, null, false, singleMessageCredit, getDeliveryMethod, getRecordMethod); + + target = ConsumerTarget_0_8.createAckTarget(channel, + AMQShortString.EMPTY_STRING, null, + singleMessageCredit, getDeliveryMethod, getRecordMethod); } else { - sub = SubscriptionFactoryImpl.INSTANCE.createBasicGetNoAckSubscription(channel, session, null, null, false, singleMessageCredit, getDeliveryMethod, getRecordMethod); + target = ConsumerTarget_0_8.createNoAckTarget(channel, + AMQShortString.EMPTY_STRING, null, + singleMessageCredit, getDeliveryMethod, getRecordMethod); } - queue.registerSubscription(sub,false); - queue.flushSubscription(sub); - queue.unregisterSubscription(sub); + Consumer sub = queue.addConsumer(target, null, AMQMessage.class, "", options); + sub.flush(); + sub.close(); return(!singleMessageCredit.hasCredit()); diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/BasicPublishMethodHandler.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/BasicPublishMethodHandler.java index 497e97db3e..f8a7722447 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/BasicPublishMethodHandler.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/BasicPublishMethodHandler.java @@ -28,6 +28,7 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicPublishBody; import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.message.MessageDestination; import org.apache.qpid.server.protocol.v0_8.AMQChannel; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.protocol.v0_8.AMQProtocolSession; @@ -67,7 +68,7 @@ public class BasicPublishMethodHandler implements StateAwareMethodListener() { + public void performAction(AMQQueue queue) { protocolConnection.removeSessionCloseTask(sessionCloseTask); } @@ -245,9 +246,9 @@ public class QueueDeclareHandler implements StateAwareMethodListener() { - public void doTask(AMQQueue queue) + public void performAction(AMQQueue queue) { session.removeSessionCloseTask(deleteQueueTask); } diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/TxRollbackHandler.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/TxRollbackHandler.java index 19d0da007b..69ad1a0a21 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/TxRollbackHandler.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/main/java/org/apache/qpid/server/protocol/v0_8/handler/TxRollbackHandler.java @@ -74,7 +74,7 @@ public class TxRollbackHandler implements StateAwareMethodListener _referenceList = new LinkedList(); + private AMQQueue _queue; + private LinkedList _referenceList = new LinkedList(); + private Consumer _consumer; + private boolean _queueDeleted; @Override public void setUp() throws AMQException { + _queueDeleted = false; _unacknowledgedMessageMap = new UnacknowledgedMessageMapImpl(100); + _queue = mock(AMQQueue.class); + when(_queue.getName()).thenReturn(getName()); + when(_queue.isDeleted()).thenReturn(_queueDeleted); + _consumer = mock(Consumer.class); + when(_consumer.getId()).thenReturn(Consumer.SUB_ID_GENERATOR.getAndIncrement()); + long id = 0; - SimpleQueueEntryList list = new SimpleQueueEntryList(_queue); // Add initial messages to QueueEntryList for (int count = 0; count < INITIAL_MSG_COUNT; count++) { - AMQMessage msg = new MockAMQMessage(id); - - list.add(msg); - + ServerMessage msg = mock(ServerMessage.class); + when(msg.getMessageNumber()).thenReturn(id); + final QueueEntry entry = mock(QueueEntry.class); + when(entry.getMessage()).thenReturn(msg); + when(entry.getQueue()).thenReturn(_queue); + when(entry.isQueueDeleted()).thenReturn(_queueDeleted); + doAnswer(new Answer() + { + @Override + public Object answer(final InvocationOnMock invocation) throws Throwable + { + when(entry.isDeleted()).thenReturn(true); + return null; + } + }).when(entry).delete(); + + _unacknowledgedMessageMap.add(id, entry); + _referenceList.add(entry); //Increment ID; id++; } - // Iterate through the QueueEntryList and add entries to unacknowledgedMessageMap and referenceList - QueueEntryIterator queueEntries = list.iterator(); - while(queueEntries.advance()) - { - QueueEntry entry = queueEntries.getNode(); - _unacknowledgedMessageMap.add(entry.getMessage().getMessageNumber(), entry); - - // Store the entry for future inspection - _referenceList.add(entry); - } - assertEquals("Map does not contain correct setup data", INITIAL_MSG_COUNT, _unacknowledgedMessageMap.size()); } @@ -103,17 +115,14 @@ public class ExtractResendAndRequeueTest extends TestCase * * @return Subscription that performed the acquire */ - private Subscription createSubscriptionAndAcquireMessages(LinkedList messageList) + private void acquireMessages(LinkedList messageList) { - Subscription subscription = new MockSubscription(); - // Aquire messages in subscription - for (QueueEntry entry : messageList) + // Acquire messages in subscription + for(MessageInstance entry : messageList) { - entry.acquire(subscription); + when(entry.getDeliveredConsumer()).thenReturn(_consumer); } - - return subscription; } /** @@ -128,14 +137,14 @@ public class ExtractResendAndRequeueTest extends TestCase public void testResend() throws AMQException { //We don't need the subscription object here. - createSubscriptionAndAcquireMessages(_referenceList); + acquireMessages(_referenceList); - final Map msgToRequeue = new LinkedHashMap(); - final Map msgToResend = new LinkedHashMap(); + final Map msgToRequeue = new LinkedHashMap(); + final Map msgToResend = new LinkedHashMap(); // requeueIfUnableToResend doesn't matter here. _unacknowledgedMessageMap.visit(new ExtractResendAndRequeue(_unacknowledgedMessageMap, msgToRequeue, - msgToResend, true, _messageStore)); + msgToResend)); assertEquals("Message count for resend not correct.", INITIAL_MSG_COUNT, msgToResend.size()); assertEquals("Message count for requeue not correct.", 0, msgToRequeue.size()); @@ -154,100 +163,22 @@ public class ExtractResendAndRequeueTest extends TestCase */ public void testRequeueDueToSubscriptionClosure() throws AMQException { - Subscription subscription = createSubscriptionAndAcquireMessages(_referenceList); + acquireMessages(_referenceList); // Close subscription - subscription.close(); + when(_consumer.isClosed()).thenReturn(true); - final Map msgToRequeue = new LinkedHashMap(); - final Map msgToResend = new LinkedHashMap(); + final Map msgToRequeue = new LinkedHashMap(); + final Map msgToResend = new LinkedHashMap(); // requeueIfUnableToResend doesn't matter here. _unacknowledgedMessageMap.visit(new ExtractResendAndRequeue(_unacknowledgedMessageMap, msgToRequeue, - msgToResend, true, _messageStore)); + msgToResend)); assertEquals("Message count for resend not correct.", 0, msgToResend.size()); assertEquals("Message count for requeue not correct.", INITIAL_MSG_COUNT, msgToRequeue.size()); assertEquals("Map was not emptied", 0, _unacknowledgedMessageMap.size()); } - /** - * If the subscription is null, due to message being retrieved via a GET, And we request that messages are requeued - * requeueIfUnableToResend(set to true) then all messages should be sent to the msgToRequeue map. - * - * @throws AMQException the visit interface throws this - */ - - public void testRequeueDueToMessageHavingNoConsumerTag() throws AMQException - { - final Map msgToRequeue = new LinkedHashMap(); - final Map msgToResend = new LinkedHashMap(); - - // requeueIfUnableToResend = true so all messages should go to msgToRequeue - _unacknowledgedMessageMap.visit(new ExtractResendAndRequeue(_unacknowledgedMessageMap, msgToRequeue, - msgToResend, true, _messageStore)); - - assertEquals("Message count for resend not correct.", 0, msgToResend.size()); - assertEquals("Message count for requeue not correct.", INITIAL_MSG_COUNT, msgToRequeue.size()); - assertEquals("Map was not emptied", 0, _unacknowledgedMessageMap.size()); - } - - /** - * If the subscription is null, due to message being retrieved via a GET, And we request that we don't - * requeueIfUnableToResend(set to false) then all messages should be dropped as we do not have a dead letter queue. - * - * @throws AMQException the visit interface throws this - */ - - public void testDrop() throws AMQException - { - final Map msgToRequeue = new LinkedHashMap(); - final Map msgToResend = new LinkedHashMap(); - - // requeueIfUnableToResend = false so all messages should be dropped all maps should be empty - _unacknowledgedMessageMap.visit(new ExtractResendAndRequeue(_unacknowledgedMessageMap, msgToRequeue, - msgToResend, false, _messageStore)); - - assertEquals("Message count for resend not correct.", 0, msgToResend.size()); - assertEquals("Message count for requeue not correct.", 0, msgToRequeue.size()); - assertEquals("Map was not emptied", 0, _unacknowledgedMessageMap.size()); - - - for (QueueEntry entry : _referenceList) - { - assertTrue("Message was not discarded", entry.isDeleted()); - } - - } - - /** - * If the subscription is null, due to message being retrieved via a GET, AND the queue upon which the message was - * delivered has been deleted then it is not possible to requeue. Currently we simply discard the message but in the - * future we may wish to dead letter the message. - * - * Validate that at the end of the visit all Maps are empty and all messages are marked as deleted - * - * @throws AMQException the visit interface throws this - */ - public void testDiscard() throws AMQException - { - final Map msgToRequeue = new LinkedHashMap(); - final Map msgToResend = new LinkedHashMap(); - - _queue.delete(); - - // requeueIfUnableToResend : value doesn't matter here as queue has been deleted - _unacknowledgedMessageMap.visit(new ExtractResendAndRequeue(_unacknowledgedMessageMap, msgToRequeue, - msgToResend, false, _messageStore)); - - assertEquals("Message count for resend not correct.", 0, msgToResend.size()); - assertEquals("Message count for requeue not correct.", 0, msgToRequeue.size()); - assertEquals("Map was not emptied", 0, _unacknowledgedMessageMap.size()); - - for (QueueEntry entry : _referenceList) - { - assertTrue("Message was not discarded", entry.isDeleted()); - } - } } diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/test/java/org/apache/qpid/server/protocol/v0_8/InternalTestProtocolSession.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/test/java/org/apache/qpid/server/protocol/v0_8/InternalTestProtocolSession.java index ef0837b3c6..1fad8fb41f 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/test/java/org/apache/qpid/server/protocol/v0_8/InternalTestProtocolSession.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/test/java/org/apache/qpid/server/protocol/v0_8/InternalTestProtocolSession.java @@ -47,11 +47,9 @@ import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.protocol.v0_8.output.ProtocolOutputConverter; -import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.security.auth.AuthenticatedPrincipal; import org.apache.qpid.server.security.auth.UsernamePrincipal; -import org.apache.qpid.server.subscription.ClientDeliveryMethod; -import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.consumer.Consumer; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.transport.Sender; import org.apache.qpid.transport.network.NetworkConnection; @@ -60,7 +58,7 @@ public class InternalTestProtocolSession extends AMQProtocolEngine implements Pr { private static final Logger _logger = Logger.getLogger(InternalTestProtocolSession.class); // ChannelID(LIST) -> LinkedList - private final Map>> _channelDelivers; + private final Map>> _channelDelivers; private AtomicInteger _deliveryCount = new AtomicInteger(0); private static final AtomicLong ID_GENERATOR = new AtomicLong(0); @@ -68,7 +66,7 @@ public class InternalTestProtocolSession extends AMQProtocolEngine implements Pr { super(broker, new TestNetworkConnection(), ID_GENERATOR.getAndIncrement(), null, null); - _channelDelivers = new HashMap>>(); + _channelDelivers = new HashMap>>(); setTestAuthorizedSubject(); setVirtualHost(virtualHost); @@ -117,7 +115,7 @@ public class InternalTestProtocolSession extends AMQProtocolEngine implements Pr { synchronized (_channelDelivers) { - List all =_channelDelivers.get(channelId).get(consumerTag); + List all =_channelDelivers.get(channelId).get(AMQShortString.toString(consumerTag)); if (all == null) { @@ -153,23 +151,23 @@ public class InternalTestProtocolSession extends AMQProtocolEngine implements Pr synchronized (_channelDelivers) { - Map> consumers = _channelDelivers.get(channelId); + Map> consumers = _channelDelivers.get(channelId); if (consumers == null) { - consumers = new HashMap>(); + consumers = new HashMap>(); _channelDelivers.put(channelId, consumers); } - LinkedList consumerDelivers = consumers.get(consumerTag); + LinkedList consumerDelivers = consumers.get(AMQShortString.toString(consumerTag)); if (consumerDelivers == null) { consumerDelivers = new LinkedList(); - consumers.put(consumerTag, consumerDelivers); + consumers.put(consumerTag.toString(), consumerDelivers); } - consumerDelivers.add(new DeliveryPair(deliveryTag, (AMQMessage)msg)); + consumerDelivers.add(new DeliveryPair(deliveryTag, msg)); } } @@ -247,27 +245,27 @@ public class InternalTestProtocolSession extends AMQProtocolEngine implements Pr @Override - public void deliverToClient(Subscription sub, ServerMessage message, + public void deliverToClient(Consumer sub, ServerMessage message, InstanceProperties props, long deliveryTag) throws AMQException { _deliveryCount.incrementAndGet(); synchronized (_channelDelivers) { - Map> consumers = _channelDelivers.get(_channelId); + Map> consumers = _channelDelivers.get(_channelId); if (consumers == null) { - consumers = new HashMap>(); + consumers = new HashMap>(); _channelDelivers.put(_channelId, consumers); } - LinkedList consumerDelivers = consumers.get(((SubscriptionImpl)sub).getConsumerTag()); + LinkedList consumerDelivers = consumers.get(sub.getName()); if (consumerDelivers == null) { consumerDelivers = new LinkedList(); - consumers.put(((SubscriptionImpl)sub).getConsumerTag(), consumerDelivers); + consumers.put(sub.getName(), consumerDelivers); } consumerDelivers.add(new DeliveryPair(deliveryTag, message)); diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/test/java/org/apache/qpid/server/protocol/v0_8/QueueBrowserUsesNoAckTest.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/test/java/org/apache/qpid/server/protocol/v0_8/QueueBrowserUsesNoAckTest.java index 8c716a0b56..e895f81c44 100644 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/test/java/org/apache/qpid/server/protocol/v0_8/QueueBrowserUsesNoAckTest.java +++ b/qpid/java/broker-plugins/amqp-0-8-protocol/src/test/java/org/apache/qpid/server/protocol/v0_8/QueueBrowserUsesNoAckTest.java @@ -29,7 +29,7 @@ import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.SimpleAMQQueue; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.TestableMemoryMessageStore; -import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.consumer.Consumer; import org.apache.qpid.server.util.BrokerTestHelper; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.test.utils.QpidTestCase; @@ -130,8 +130,7 @@ public class QueueBrowserUsesNoAckTest extends QpidTestCase //Check the process didn't suspend the subscription as this would // indicate we are using the prefetch credit. i.e. using acks not No-Ack assertTrue("The subscription has been suspended", - !getChannel().getSubscription(browser).getState() - .equals(Subscription.State.SUSPENDED)); + !getChannel().getSubscription(browser).isSuspended()); } private void checkStoreContents(int messageCount) @@ -144,6 +143,6 @@ public class QueueBrowserUsesNoAckTest extends QpidTestCase FieldTable filters = new FieldTable(); filters.put(AMQPFilterTypes.NO_CONSUME.getValue(), true); - return channel.subscribeToQueue(null, queue, true, filters, false, true); + return channel.consumeFromSource(null, queue, true, filters, true); } } diff --git a/qpid/java/broker-plugins/amqp-0-8-protocol/src/test/java/org/apache/qpid/server/protocol/v0_8/SubscriptionFactoryImplTest.java b/qpid/java/broker-plugins/amqp-0-8-protocol/src/test/java/org/apache/qpid/server/protocol/v0_8/SubscriptionFactoryImplTest.java deleted file mode 100644 index e0d1b28007..0000000000 --- a/qpid/java/broker-plugins/amqp-0-8-protocol/src/test/java/org/apache/qpid/server/protocol/v0_8/SubscriptionFactoryImplTest.java +++ /dev/null @@ -1,96 +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.protocol.v0_8; - -import org.apache.qpid.common.AMQPFilterTypes; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.server.logging.UnitTestMessageLogger; -import org.apache.qpid.server.logging.actors.GenericActor; -import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.util.BrokerTestHelper; -import org.apache.qpid.test.utils.QpidTestCase; - -public class SubscriptionFactoryImplTest extends QpidTestCase -{ - private AMQChannel _channel; - private AMQProtocolSession _session; - - @Override - public void setUp() throws Exception - { - super.setUp(); - BrokerTestHelper.setUp(); - _channel = BrokerTestHelper_0_8.createChannel(); - _session = _channel.getProtocolSession(); - GenericActor.setDefaultMessageLogger(new UnitTestMessageLogger(false)); - } - - @Override - public void tearDown() throws Exception - { - try - { - if (_channel != null) - { - _channel.getVirtualHost().close(); - } - } - finally - { - BrokerTestHelper.tearDown(); - super.tearDown(); - } - } - - /** - * Tests that while creating Subscriptions of various types, the - * ID numbers assigned are allocated from a common sequence - * (in increasing order). - */ - public void testDifferingSubscriptionTypesShareCommonIdNumberingSequence() throws Exception - { - //create a No-Ack subscription, get the first Subscription ID - long previousId = 0; - Subscription noAckSub = SubscriptionFactoryImpl.INSTANCE.createSubscription(1, _session, new AMQShortString("1"), false, null, false, _channel.getCreditManager()); - previousId = noAckSub.getSubscriptionID(); - - //create an ack subscription, verify the next Subscription ID is used - Subscription ackSub = SubscriptionFactoryImpl.INSTANCE.createSubscription(1, _session, new AMQShortString("1"), true, null, false, _channel.getCreditManager()); - assertEquals("Unexpected Subscription ID allocated", previousId + 1, ackSub.getSubscriptionID()); - previousId = ackSub.getSubscriptionID(); - - //create a browser subscription - FieldTable filters = new FieldTable(); - filters.put(AMQPFilterTypes.NO_CONSUME.getValue(), true); - Subscription browserSub = SubscriptionFactoryImpl.INSTANCE.createSubscription(1, _session, new AMQShortString("1"), true, null, false, _channel.getCreditManager()); - assertEquals("Unexpected Subscription ID allocated", previousId + 1, browserSub.getSubscriptionID()); - previousId = browserSub.getSubscriptionID(); - - //create an BasicGet NoAck subscription - Subscription getNoAckSub = SubscriptionFactoryImpl.INSTANCE.createBasicGetNoAckSubscription(_channel, _session, new AMQShortString("1"), null, false, - _channel.getCreditManager(),_channel.getClientDeliveryMethod(), _channel.getRecordDeliveryMethod()); - assertEquals("Unexpected Subscription ID allocated", previousId + 1, getNoAckSub.getSubscriptionID()); - previousId = getNoAckSub.getSubscriptionID(); - - } - -} diff --git a/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/Connection_1_0.java b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/Connection_1_0.java index 4082f22e9c..41e2fef03f 100644 --- a/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/Connection_1_0.java +++ b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/Connection_1_0.java @@ -34,6 +34,7 @@ import org.apache.qpid.server.model.Transport; import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.stats.StatisticsCounter; +import org.apache.qpid.server.util.Action; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.ArrayList; @@ -53,16 +54,8 @@ public class Connection_1_0 implements ConnectionEventListener private final Collection _sessions = Collections.synchronizedCollection(new ArrayList()); private final Object _reference = new Object(); - - - public static interface Task - { - public void doTask(Connection_1_0 connection); - } - - - private List _closeTasks = - Collections.synchronizedList(new ArrayList()); + private List> _closeTasks = + Collections.synchronizedList(new ArrayList>()); @@ -98,26 +91,26 @@ public class Connection_1_0 implements ConnectionEventListener _sessions.remove(session); } - void removeConnectionCloseTask(final Task task) + void removeConnectionCloseTask(final Action task) { _closeTasks.remove( task ); } - void addConnectionCloseTask(final Task task) + void addConnectionCloseTask(final Action task) { _closeTasks.add( task ); } public void closeReceived() { - List taskCopy; + List> taskCopy; synchronized (_closeTasks) { - taskCopy = new ArrayList(_closeTasks); + taskCopy = new ArrayList>(_closeTasks); } - for(Task task : taskCopy) + for(Action task : taskCopy) { - task.doTask(this); + task.performAction(this); } synchronized (_closeTasks) { diff --git a/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/ConsumerTarget_1_0.java b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/ConsumerTarget_1_0.java new file mode 100644 index 0000000000..027c40aabe --- /dev/null +++ b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/ConsumerTarget_1_0.java @@ -0,0 +1,526 @@ +/* + * + * 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.protocol.v1_0; + +import org.apache.qpid.AMQException; +import org.apache.qpid.amqp_1_0.codec.ValueHandler; +import org.apache.qpid.amqp_1_0.messaging.SectionEncoder; +import org.apache.qpid.amqp_1_0.messaging.SectionEncoderImpl; +import org.apache.qpid.amqp_1_0.transport.SendingLinkEndpoint; +import org.apache.qpid.amqp_1_0.type.AmqpErrorException; +import org.apache.qpid.amqp_1_0.type.Binary; +import org.apache.qpid.amqp_1_0.type.DeliveryState; +import org.apache.qpid.amqp_1_0.type.Outcome; +import org.apache.qpid.amqp_1_0.type.UnsignedInteger; +import org.apache.qpid.amqp_1_0.type.codec.AMQPDescribedTypeRegistry; +import org.apache.qpid.amqp_1_0.type.messaging.Accepted; +import org.apache.qpid.amqp_1_0.type.messaging.Header; +import org.apache.qpid.amqp_1_0.type.messaging.Modified; +import org.apache.qpid.amqp_1_0.type.messaging.Released; +import org.apache.qpid.amqp_1_0.type.transaction.TransactionalState; +import org.apache.qpid.amqp_1_0.type.transport.SenderSettleMode; +import org.apache.qpid.amqp_1_0.type.transport.Transfer; +import org.apache.qpid.server.message.MessageInstance; +import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.plugin.MessageConverter; +import org.apache.qpid.server.protocol.AMQSessionModel; +import org.apache.qpid.server.protocol.MessageConverterRegistry; +import org.apache.qpid.server.consumer.AbstractConsumerTarget; +import org.apache.qpid.server.consumer.Consumer; +import org.apache.qpid.server.txn.ServerTransaction; + +import java.nio.ByteBuffer; +import java.util.List; + +class ConsumerTarget_1_0 extends AbstractConsumerTarget +{ + private final boolean _acquires; + private SendingLink_1_0 _link; + + private long _deliveryTag = 0L; + + private Binary _transactionId; + private final AMQPDescribedTypeRegistry _typeRegistry; + private final SectionEncoder _sectionEncoder; + private Consumer _consumer; + + public ConsumerTarget_1_0(final SendingLink_1_0 link, + boolean acquires) + { + super(State.SUSPENDED); + _link = link; + _typeRegistry = link.getEndpoint().getSession().getConnection().getDescribedTypeRegistry(); + _sectionEncoder = new SectionEncoderImpl(_typeRegistry); + _acquires = acquires; + } + + public Consumer getConsumer() + { + return _consumer; + } + + private SendingLinkEndpoint getEndpoint() + { + return _link.getEndpoint(); + } + + public boolean isSuspended() + { + return _link.getSession().getConnectionModel().isStopped() || getState() != State.ACTIVE;// || !getEndpoint().hasCreditToSend(); + + } + + public boolean close() + { + boolean closed = false; + State state = getState(); + + getConsumer().getSendLock(); + try + { + while(!closed && state != State.CLOSED) + { + closed = updateState(state, State.CLOSED); + if(!closed) + { + state = getState(); + } + } + return closed; + } + finally + { + getConsumer().releaseSendLock(); + } + } + + public void send(MessageInstance entry, boolean batch) throws AMQException + { + // TODO + send(entry); + } + + public void flushBatched() + { + // TODO + } + + public void send(final MessageInstance queueEntry) throws AMQException + { + ServerMessage serverMessage = queueEntry.getMessage(); + Message_1_0 message; + if(serverMessage instanceof Message_1_0) + { + message = (Message_1_0) serverMessage; + } + else + { + final MessageConverter converter = MessageConverterRegistry.getConverter(serverMessage.getClass(), Message_1_0.class); + message = (Message_1_0) converter.convert(serverMessage, _link.getVirtualHost()); + } + + Transfer transfer = new Transfer(); + //TODO + + + List fragments = message.getFragments(); + ByteBuffer payload; + if(fragments.size() == 1) + { + payload = fragments.get(0); + } + else + { + int size = 0; + for(ByteBuffer fragment : fragments) + { + size += fragment.remaining(); + } + + payload = ByteBuffer.allocate(size); + + for(ByteBuffer fragment : fragments) + { + payload.put(fragment.duplicate()); + } + + payload.flip(); + } + + if(queueEntry.getDeliveryCount() != 0) + { + payload = payload.duplicate(); + ValueHandler valueHandler = new ValueHandler(_typeRegistry); + + Header oldHeader = null; + try + { + ByteBuffer encodedBuf = payload.duplicate(); + Object value = valueHandler.parse(payload); + if(value instanceof Header) + { + oldHeader = (Header) value; + } + else + { + payload.position(0); + } + } + catch (AmqpErrorException e) + { + //TODO + throw new RuntimeException(e); + } + + Header header = new Header(); + if(oldHeader != null) + { + header.setDurable(oldHeader.getDurable()); + header.setPriority(oldHeader.getPriority()); + header.setTtl(oldHeader.getTtl()); + } + header.setDeliveryCount(UnsignedInteger.valueOf(queueEntry.getDeliveryCount())); + _sectionEncoder.reset(); + _sectionEncoder.encodeObject(header); + Binary encodedHeader = _sectionEncoder.getEncoding(); + + ByteBuffer oldPayload = payload; + payload = ByteBuffer.allocate(oldPayload.remaining() + encodedHeader.getLength()); + payload.put(encodedHeader.getArray(),encodedHeader.getArrayOffset(),encodedHeader.getLength()); + payload.put(oldPayload); + payload.flip(); + } + + transfer.setPayload(payload); + byte[] data = new byte[8]; + ByteBuffer.wrap(data).putLong(_deliveryTag++); + final Binary tag = new Binary(data); + + transfer.setDeliveryTag(tag); + + synchronized(_link.getLock()) + { + if(_link.isAttached()) + { + if(SenderSettleMode.SETTLED.equals(getEndpoint().getSendingSettlementMode())) + { + transfer.setSettled(true); + } + else + { + UnsettledAction action = _acquires + ? new DispositionAction(tag, queueEntry) + : new DoNothingAction(tag, queueEntry); + + _link.addUnsettled(tag, action, queueEntry); + } + + if(_transactionId != null) + { + TransactionalState state = new TransactionalState(); + state.setTxnId(_transactionId); + transfer.setState(state); + } + // TODO - need to deal with failure here + if(_acquires && _transactionId != null) + { + ServerTransaction txn = _link.getTransaction(_transactionId); + if(txn != null) + { + txn.addPostTransactionAction(new ServerTransaction.Action(){ + + public void postCommit() + { + //To change body of implemented methods use File | Settings | File Templates. + } + + public void onRollback() + { + if(queueEntry.isAcquiredBy(getConsumer())) + { + queueEntry.release(); + _link.getEndpoint().updateDisposition(tag, (DeliveryState)null, true); + + + } + } + }); + } + + } + getSession().getConnectionModel().registerMessageDelivered(message.getSize()); + getEndpoint().transfer(transfer); + } + else + { + queueEntry.release(); + } + } + + } + + public void queueDeleted() + { + //TODO + getEndpoint().setSource(null); + getEndpoint().detach(); + } + + public boolean allocateCredit(final ServerMessage msg) + { + synchronized (_link.getLock()) + { + final boolean hasCredit = _link.isAttached() && getEndpoint().hasCreditToSend(); + if(!hasCredit && getState() == State.ACTIVE) + { + suspend(); + } + + return hasCredit; + } + } + + + public void suspend() + { + synchronized(_link.getLock()) + { + updateState(State.ACTIVE, State.SUSPENDED); + } + } + + + public void restoreCredit(final ServerMessage message) + { + //TODO + } + + public void queueEmpty() + { + synchronized(_link.getLock()) + { + if(_link.drained()) + { + updateState(State.ACTIVE, State.SUSPENDED); + } + } + } + + public void flowStateChanged() + { + synchronized(_link.getLock()) + { + if(isSuspended() && getEndpoint() != null) + { + updateState(State.SUSPENDED, State.ACTIVE); + _transactionId = _link.getTransactionId(); + } + } + } + + public Session_1_0 getSession() + { + return _link.getSession(); + } + + private class DispositionAction implements UnsettledAction + { + + private final MessageInstance _queueEntry; + private final Binary _deliveryTag; + + public DispositionAction(Binary tag, MessageInstance queueEntry) + { + _deliveryTag = tag; + _queueEntry = queueEntry; + } + + public boolean process(DeliveryState state, final Boolean settled) + { + + Binary transactionId = null; + final Outcome outcome; + // If disposition is settled this overrides the txn? + if(state instanceof TransactionalState) + { + transactionId = ((TransactionalState)state).getTxnId(); + outcome = ((TransactionalState)state).getOutcome(); + } + else if (state instanceof Outcome) + { + outcome = (Outcome) state; + } + else + { + outcome = null; + } + + + ServerTransaction txn = _link.getTransaction(transactionId); + + if(outcome instanceof Accepted) + { + txn.dequeue(_queueEntry.getOwningResource(), _queueEntry.getMessage(), + new ServerTransaction.Action() + { + + public void postCommit() + { + if(_queueEntry.isAcquiredBy(getConsumer())) + { + _queueEntry.delete(); + } + } + + public void onRollback() + { + + } + }); + txn.addPostTransactionAction(new ServerTransaction.Action() + { + public void postCommit() + { + //_link.getEndpoint().settle(_deliveryTag); + _link.getEndpoint().updateDisposition(_deliveryTag, (DeliveryState)outcome, true); + _link.getEndpoint().sendFlowConditional(); + } + + public void onRollback() + { + if(Boolean.TRUE.equals(settled)) + { + final Modified modified = new Modified(); + modified.setDeliveryFailed(true); + _link.getEndpoint().updateDisposition(_deliveryTag, modified, true); + _link.getEndpoint().sendFlowConditional(); + } + } + }); + } + else if(outcome instanceof Released) + { + txn.addPostTransactionAction(new ServerTransaction.Action() + { + public void postCommit() + { + + _queueEntry.release(); + _link.getEndpoint().settle(_deliveryTag); + } + + public void onRollback() + { + _link.getEndpoint().settle(_deliveryTag); + } + }); + } + + else if(outcome instanceof Modified) + { + txn.addPostTransactionAction(new ServerTransaction.Action() + { + public void postCommit() + { + + _queueEntry.release(); + if(Boolean.TRUE.equals(((Modified)outcome).getDeliveryFailed())) + { + _queueEntry.incrementDeliveryCount(); + } + _link.getEndpoint().settle(_deliveryTag); + } + + public void onRollback() + { + if(Boolean.TRUE.equals(settled)) + { + final Modified modified = new Modified(); + modified.setDeliveryFailed(true); + _link.getEndpoint().updateDisposition(_deliveryTag, modified, true); + _link.getEndpoint().sendFlowConditional(); + } + } + }); + } + + return (transactionId == null && outcome != null); + } + } + + private class DoNothingAction implements UnsettledAction + { + public DoNothingAction(final Binary tag, + final MessageInstance queueEntry) + { + } + + public boolean process(final DeliveryState state, final Boolean settled) + { + Binary transactionId = null; + Outcome outcome = null; + // If disposition is settled this overrides the txn? + if(state instanceof TransactionalState) + { + transactionId = ((TransactionalState)state).getTxnId(); + outcome = ((TransactionalState)state).getOutcome(); + } + else if (state instanceof Outcome) + { + outcome = (Outcome) state; + } + return true; + } + } + + @Override + public AMQSessionModel getSessionModel() + { + return getSession(); + } + + @Override + public void consumerAdded(final Consumer sub) + { + _consumer = sub; + } + + @Override + public void consumerRemoved(final Consumer sub) + { + + } + + @Override + public long getUnacknowledgedBytes() + { + // TODO + return 0; + } + + @Override + public long getUnacknowledgedMessages() + { + // TODO + return 0; + } + +} diff --git a/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/MessageConverter_to_1_0.java b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/MessageConverter_to_1_0.java index 78ca9ff2a6..a96d951de6 100644 --- a/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/MessageConverter_to_1_0.java +++ b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/MessageConverter_to_1_0.java @@ -23,7 +23,6 @@ package org.apache.qpid.server.protocol.v1_0; import java.io.EOFException; import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.ListIterator; @@ -286,7 +285,7 @@ public abstract class MessageConverter_to_1_0 implement Binary dataEncoding = sectionEncoder.getEncoding(); final ByteBuffer allData = ByteBuffer.allocate(headerSize + dataEncoding.getLength()); - metaData.writeToBuffer(0,allData); + metaData.writeToBuffer(allData); allData.put(dataEncoding.getArray(),dataEncoding.getArrayOffset(),dataEncoding.getLength()); return allData; } diff --git a/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/MessageMetaData_1_0.java b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/MessageMetaData_1_0.java index 5026007360..be9d7a2d60 100755 --- a/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/MessageMetaData_1_0.java +++ b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/MessageMetaData_1_0.java @@ -314,7 +314,7 @@ public class MessageMetaData_1_0 implements StorableMessageMetaData return buf; } - public int writeToBuffer(int offsetInMetaData, ByteBuffer dest) + public int writeToBuffer(ByteBuffer dest) { ByteBuffer buf = _encoded; @@ -326,7 +326,7 @@ public class MessageMetaData_1_0 implements StorableMessageMetaData buf = buf.duplicate(); - buf.position(offsetInMetaData); + buf.position(0); if(dest.remaining() < buf.limit()) { diff --git a/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/MessageSourceDestination.java b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/MessageSourceDestination.java new file mode 100644 index 0000000000..6f37d2d831 --- /dev/null +++ b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/MessageSourceDestination.java @@ -0,0 +1,59 @@ +/* + * + * 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.protocol.v1_0; + +import org.apache.log4j.Logger; +import org.apache.qpid.amqp_1_0.type.Outcome; +import org.apache.qpid.amqp_1_0.type.messaging.Accepted; +import org.apache.qpid.server.message.MessageSource; +import org.apache.qpid.server.txn.ServerTransaction; + +public class MessageSourceDestination implements SendingDestination +{ + private static final Logger _logger = Logger.getLogger(MessageSourceDestination.class); + private static final Accepted ACCEPTED = new Accepted(); + private static final Outcome[] OUTCOMES = new Outcome[] { ACCEPTED }; + + + private MessageSource _queue; + + public MessageSourceDestination(MessageSource queue) + { + _queue = queue; + } + + public Outcome[] getOutcomes() + { + return OUTCOMES; + } + + public int getCredit() + { + // TODO - fix + return 100; + } + + public MessageSource getQueue() + { + return _queue; + } + +} diff --git a/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/NodeReceivingDestination.java b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/NodeReceivingDestination.java new file mode 100644 index 0000000000..70f659b546 --- /dev/null +++ b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/NodeReceivingDestination.java @@ -0,0 +1,106 @@ +/* + * + * 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.protocol.v1_0; + +import org.apache.qpid.amqp_1_0.type.Outcome; +import org.apache.qpid.amqp_1_0.type.messaging.Accepted; +import org.apache.qpid.amqp_1_0.type.messaging.Rejected; +import org.apache.qpid.amqp_1_0.type.messaging.TerminusDurability; +import org.apache.qpid.amqp_1_0.type.messaging.TerminusExpiryPolicy; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.message.InstanceProperties; +import org.apache.qpid.server.message.MessageDestination; +import org.apache.qpid.server.txn.ServerTransaction; + +public class NodeReceivingDestination implements ReceivingDestination +{ + private static final Accepted ACCEPTED = new Accepted(); + public static final Rejected REJECTED = new Rejected(); + private static final Outcome[] OUTCOMES = { ACCEPTED, REJECTED}; + + private MessageDestination _exchange; + private TerminusDurability _durability; + private TerminusExpiryPolicy _expiryPolicy; + + public NodeReceivingDestination(MessageDestination exchange, TerminusDurability durable, TerminusExpiryPolicy expiryPolicy) + { + _exchange = exchange; + _durability = durable; + _expiryPolicy = expiryPolicy; + } + + public Outcome[] getOutcomes() + { + return OUTCOMES; + } + + public Outcome send(final Message_1_0 message, ServerTransaction txn) + { + final InstanceProperties instanceProperties = + new InstanceProperties() + { + + @Override + public Object getProperty(final Property prop) + { + switch(prop) + { + case MANDATORY: + return false; + case REDELIVERED: + return false; + case PERSISTENT: + return message.isPersistent(); + case IMMEDIATE: + return false; + case EXPIRATION: + return message.getExpiration(); + } + return null; + }}; + + int enqueues = _exchange.send(message, instanceProperties, txn, null); + + + return enqueues == 0 ? REJECTED : ACCEPTED; + } + + TerminusDurability getDurability() + { + return _durability; + } + + TerminusExpiryPolicy getExpiryPolicy() + { + return _expiryPolicy; + } + + public int getCredit() + { + // TODO - fix + return 20000; + } + + public MessageDestination getDestination() + { + return _exchange; + } +} diff --git a/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/QueueDestination.java b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/QueueDestination.java index b9c10b925f..3d6bb5e3db 100644 --- a/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/QueueDestination.java +++ b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/QueueDestination.java @@ -24,22 +24,21 @@ import org.apache.log4j.Logger; import org.apache.qpid.amqp_1_0.type.Outcome; import org.apache.qpid.amqp_1_0.type.messaging.Accepted; +import org.apache.qpid.server.message.MessageSource; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.txn.ServerTransaction; -public class QueueDestination implements SendingDestination, ReceivingDestination +public class QueueDestination extends MessageSourceDestination implements SendingDestination, ReceivingDestination { private static final Logger _logger = Logger.getLogger(QueueDestination.class); private static final Accepted ACCEPTED = new Accepted(); private static final Outcome[] OUTCOMES = new Outcome[] { ACCEPTED }; - private AMQQueue _queue; - public QueueDestination(AMQQueue queue) { - _queue = queue; + super(queue); } public Outcome[] getOutcomes() @@ -52,7 +51,7 @@ public class QueueDestination implements SendingDestination, ReceivingDestinatio try { - txn.enqueue(_queue,message, new ServerTransaction.Action() + txn.enqueue(getQueue(),message, new ServerTransaction.Action() { @@ -60,8 +59,7 @@ public class QueueDestination implements SendingDestination, ReceivingDestinatio { try { - - _queue.enqueue(message); + getQueue().enqueue(message,null); } catch (Exception e) { @@ -93,7 +91,7 @@ public class QueueDestination implements SendingDestination, ReceivingDestinatio public AMQQueue getQueue() { - return _queue; + return (AMQQueue) super.getQueue(); } } diff --git a/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLink_1_0.java b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLink_1_0.java index 4abf1bf76b..9e0327fe76 100644 --- a/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLink_1_0.java +++ b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLink_1_0.java @@ -22,6 +22,7 @@ package org.apache.qpid.server.protocol.v1_0; import java.util.ArrayList; import java.util.Collections; +import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -64,11 +65,14 @@ import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.exchange.TopicExchange; import org.apache.qpid.server.filter.JMSSelectorFilter; import org.apache.qpid.server.filter.SimpleFilterManager; +import org.apache.qpid.server.message.MessageInstance; +import org.apache.qpid.server.message.MessageSource; import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.consumer.Consumer; import org.apache.qpid.server.txn.AutoCommitTransaction; import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.util.Action; import org.apache.qpid.server.virtualhost.VirtualHost; public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryStateHandler @@ -78,18 +82,22 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS private VirtualHost _vhost; private SendingDestination _destination; - private Subscription_1_0 _subscription; + private Consumer _consumer; + private ConsumerTarget_1_0 _target; + private boolean _draining; - private final Map _unsettledMap = - new HashMap(); + private final Map _unsettledMap = + new HashMap(); private final ConcurrentHashMap _unsettledActionMap = new ConcurrentHashMap(); private volatile SendingLinkAttachment _linkAttachment; private TerminusDurability _durability; - private List _resumeFullTransfers = new ArrayList(); + private List _resumeFullTransfers = new ArrayList(); private List _resumeAcceptedTransfers = new ArrayList(); private Runnable _closeAction; + private final MessageSource _queue; + public SendingLink_1_0(final SendingLinkAttachment linkAttachment, final VirtualHost vhost, @@ -103,24 +111,22 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS _durability = source.getDurable(); linkAttachment.setDeliveryStateHandler(this); QueueDestination qd = null; - AMQQueue queue = null; + EnumSet options = EnumSet.noneOf(Consumer.Option.class); boolean noLocal = false; JMSSelectorFilter messageFilter = null; - if(destination instanceof QueueDestination) + if(destination instanceof MessageSourceDestination) { - queue = ((QueueDestination) _destination).getQueue(); + _queue = ((MessageSourceDestination) _destination).getQueue(); - if(queue.getAvailableAttributes().contains("topic")) + if(_queue instanceof AMQQueue && ((AMQQueue)_queue).getAvailableAttributes().contains("topic")) { source.setDistributionMode(StdDistMode.COPY); } - qd = (QueueDestination) destination; - Map filters = source.getFilter(); Map actualFilters = new HashMap(); @@ -167,7 +173,13 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS } source.setFilter(actualFilters.isEmpty() ? null : actualFilters); - _subscription = new Subscription_1_0(this, qd, source.getDistributionMode() != StdDistMode.COPY); + _target = new ConsumerTarget_1_0(this, source.getDistributionMode() != StdDistMode.COPY); + if(source.getDistributionMode() != StdDistMode.COPY) + { + options.add(Consumer.Option.ACQUIRES); + options.add(Consumer.Option.SEES_REQUEUES); + } + } else if(destination instanceof ExchangeDestination) { @@ -199,7 +211,7 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS name = UUID.randomUUID().toString(); } - queue = _vhost.getQueue(name); + AMQQueue queue = _vhost.getQueue(name); Exchange exchange = exchangeDestination.getExchange(); if(queue == null) @@ -299,9 +311,10 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS } } } + _queue = queue; source.setFilter(actualFilters.isEmpty() ? null : actualFilters); - exchange.addBinding(binding,queue,null); + exchange.addBinding(binding, queue,null); source.setDistributionMode(StdDistMode.COPY); if(!isDurable) @@ -309,10 +322,10 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS final String queueName = name; final AMQQueue tempQueue = queue; - final Connection_1_0.Task deleteQueueTask = - new Connection_1_0.Task() + final Action deleteQueueTask = + new Action() { - public void doTask(Connection_1_0 session) + public void performAction(Connection_1_0 session) { if (_vhost.getQueue(queueName) == tempQueue) { @@ -331,9 +344,9 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS getSession().getConnection().addConnectionCloseTask(deleteQueueTask); - queue.addQueueDeleteTask(new AMQQueue.Task() + queue.addQueueDeleteTask(new Action() { - public void doTask(AMQQueue queue) + public void performAction(AMQQueue queue) { getSession().getConnection().removeConnectionCloseTask(deleteQueueTask); } @@ -347,31 +360,46 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS catch (AMQSecurityException e) { _logger.error("Security error", e); + throw new RuntimeException(e); } catch (AMQInternalException e) { _logger.error("Internal error", e); + throw new RuntimeException(e); } catch (AMQException e) { _logger.error("Error", e); + throw new RuntimeException(e); } - _subscription = new Subscription_1_0(this, qd, true); + + _target = new ConsumerTarget_1_0(this, true); + options.add(Consumer.Option.ACQUIRES); + options.add(Consumer.Option.SEES_REQUEUES); + + } + else + { + throw new RuntimeException("Unknown destination type"); } - if(_subscription != null) + if(_target != null) { - _subscription.setNoLocal(noLocal); - if(messageFilter!=null) + if(noLocal) { - _subscription.setFilters(new SimpleFilterManager(messageFilter)); + options.add(Consumer.Option.NO_LOCAL); } + + _consumer.setNoLocal(noLocal); + + try { - - queue.registerSubscription(_subscription, false); + _consumer = _queue.addConsumer(_target, + messageFilter == null ? null : new SimpleFilterManager(messageFilter), + Message_1_0.class, getEndpoint().getName(), options); } catch (AMQException e) { @@ -394,12 +422,11 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS // if not durable or close if(!TerminusDurability.UNSETTLED_STATE.equals(_durability)) { - AMQQueue queue = _subscription.getQueue(); try { - queue.unregisterSubscription(_subscription); + _consumer.close(); } catch (AMQException e) @@ -426,7 +453,7 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS { try { - queue.getVirtualHost().removeQueue(queue); + _vhost.removeQueue((AMQQueue)_queue); } catch(AMQException e) { @@ -443,7 +470,7 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS else if(detach == null || detach.getError() != null) { _linkAttachment = null; - _subscription.flowStateChanged(); + _target.flowStateChanged(); } else { @@ -491,7 +518,7 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS } if(_resumeAcceptedTransfers.isEmpty()) { - _subscription.flowStateChanged(); + _target.flowStateChanged(); } } @@ -531,7 +558,7 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS } } - public void addUnsettled(Binary tag, UnsettledAction unsettledAction, QueueEntry queueEntry) + public void addUnsettled(Binary tag, UnsettledAction unsettledAction, MessageInstance queueEntry) { _unsettledActionMap.put(tag,unsettledAction); if(getTransactionId() == null) @@ -593,9 +620,9 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS public synchronized void setLinkAttachment(SendingLinkAttachment linkAttachment) { - if(_subscription.isActive()) + if(_consumer.isActive()) { - _subscription.suspend(); + _target.suspend(); } _linkAttachment = linkAttachment; @@ -603,14 +630,14 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS SendingLinkEndpoint endpoint = linkAttachment.getEndpoint(); endpoint.setDeliveryStateHandler(this); Map initialUnsettledMap = endpoint.getInitialUnsettledMap(); - Map unsettledCopy = new HashMap(_unsettledMap); + Map unsettledCopy = new HashMap(_unsettledMap); _resumeAcceptedTransfers.clear(); _resumeFullTransfers.clear(); - for(Map.Entry entry : unsettledCopy.entrySet()) + for(Map.Entry entry : unsettledCopy.entrySet()) { Binary deliveryTag = entry.getKey(); - final QueueEntry queueEntry = entry.getValue(); + final MessageInstance queueEntry = entry.getValue(); if(initialUnsettledMap == null || !initialUnsettledMap.containsKey(deliveryTag)) { queueEntry.setRedelivered(); @@ -624,7 +651,7 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS if(outcome instanceof Accepted) { AutoCommitTransaction txn = new AutoCommitTransaction(_vhost.getMessageStore()); - if(_subscription.acquires()) + if(_consumer.acquires()) { txn.dequeue(Collections.singleton(queueEntry), new ServerTransaction.Action() @@ -644,7 +671,7 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS else if(outcome instanceof Released) { AutoCommitTransaction txn = new AutoCommitTransaction(_vhost.getMessageStore()); - if(_subscription.acquires()) + if(_consumer.acquires()) { txn.dequeue(Collections.singleton(queueEntry), new ServerTransaction.Action() @@ -678,9 +705,9 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS public Map getUnsettledOutcomeMap() { - Map unsettled = new HashMap(_unsettledMap); + Map unsettled = new HashMap(_unsettledMap); - for(Map.Entry entry : unsettled.entrySet()) + for(Map.Entry entry : unsettled.entrySet()) { entry.setValue(null); } @@ -692,4 +719,9 @@ public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryS { _closeAction = action; } + + public VirtualHost getVirtualHost() + { + return _vhost; + } } diff --git a/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/Session_1_0.java b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/Session_1_0.java index 823e4cb16d..beed6be84b 100644 --- a/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/Session_1_0.java +++ b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/Session_1_0.java @@ -41,6 +41,8 @@ import org.apache.qpid.AMQSecurityException; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.logging.LogSubject; +import org.apache.qpid.server.message.MessageDestination; +import org.apache.qpid.server.message.MessageSource; import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.protocol.AMQSessionModel; @@ -48,6 +50,7 @@ import org.apache.qpid.server.protocol.LinkRegistry; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.txn.AutoCommitTransaction; import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.util.Action; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.*; @@ -108,11 +111,11 @@ public class Session_1_0 implements SessionEventListener, AMQSessionModel, LogSu source.setAddress(tempQueue.getName()); } String addr = source.getAddress(); - AMQQueue queue = _vhost.getQueue(addr); + MessageSource queue = _vhost.getMessageSource(addr); if(queue != null) { - destination = new QueueDestination(queue); + destination = new MessageSourceDestination(queue); @@ -249,11 +252,11 @@ public class Session_1_0 implements SessionEventListener, AMQSessionModel, LogSu } String addr = target.getAddress(); - Exchange exchg = _vhost.getExchange(addr); - if(exchg != null) + MessageDestination messageDestination = _vhost.getMessageDestination(addr); + if(messageDestination != null) { - destination = new ExchangeDestination(exchg, target.getDurable(), - target.getExpiryPolicy()); + destination = new NodeReceivingDestination(messageDestination, target.getDurable(), + target.getExpiryPolicy()); } else { @@ -343,10 +346,10 @@ public class Session_1_0 implements SessionEventListener, AMQSessionModel, LogSu if (lifetimePolicy == null || lifetimePolicy instanceof DeleteOnClose) { - final Connection_1_0.Task deleteQueueTask = - new Connection_1_0.Task() + final Action deleteQueueTask = + new Action() { - public void doTask(Connection_1_0 session) + public void performAction(Connection_1_0 session) { if (_vhost.getQueue(queueName) == tempQueue) { @@ -365,9 +368,9 @@ public class Session_1_0 implements SessionEventListener, AMQSessionModel, LogSu _connection.addConnectionCloseTask(deleteQueueTask); - queue.addQueueDeleteTask(new AMQQueue.Task() + queue.addQueueDeleteTask(new Action() { - public void doTask(AMQQueue queue) + public void performAction(AMQQueue queue) { _connection.removeConnectionCloseTask(deleteQueueTask); } diff --git a/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java b/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java deleted file mode 100644 index 6a3f5b46e1..0000000000 --- a/qpid/java/broker-plugins/amqp-1-0-protocol/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java +++ /dev/null @@ -1,694 +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.protocol.v1_0; - -import java.nio.ByteBuffer; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.ReentrantLock; -import org.apache.qpid.AMQException; -import org.apache.qpid.amqp_1_0.codec.ValueHandler; -import org.apache.qpid.amqp_1_0.messaging.SectionEncoder; -import org.apache.qpid.amqp_1_0.messaging.SectionEncoderImpl; -import org.apache.qpid.amqp_1_0.transport.SendingLinkEndpoint; -import org.apache.qpid.amqp_1_0.type.AmqpErrorException; -import org.apache.qpid.amqp_1_0.type.Binary; -import org.apache.qpid.amqp_1_0.type.DeliveryState; -import org.apache.qpid.amqp_1_0.type.Outcome; -import org.apache.qpid.amqp_1_0.type.UnsignedInteger; -import org.apache.qpid.amqp_1_0.type.codec.AMQPDescribedTypeRegistry; -import org.apache.qpid.amqp_1_0.type.messaging.Accepted; -import org.apache.qpid.amqp_1_0.type.messaging.Header; -import org.apache.qpid.amqp_1_0.type.messaging.Modified; -import org.apache.qpid.amqp_1_0.type.messaging.Released; -import org.apache.qpid.amqp_1_0.type.messaging.Source; -import org.apache.qpid.amqp_1_0.type.messaging.StdDistMode; -import org.apache.qpid.amqp_1_0.type.transaction.TransactionalState; -import org.apache.qpid.amqp_1_0.type.transport.SenderSettleMode; -import org.apache.qpid.amqp_1_0.type.transport.Transfer; -import org.apache.qpid.server.plugin.MessageConverter; -import org.apache.qpid.server.protocol.MessageConverterRegistry; -import org.apache.qpid.server.filter.FilterManager; -import org.apache.qpid.server.logging.LogActor; -import org.apache.qpid.server.message.ServerMessage; -import org.apache.qpid.server.protocol.AMQSessionModel; -import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.queue.QueueEntry; -import org.apache.qpid.server.subscription.Subscription; -import org.apache.qpid.server.txn.ServerTransaction; - -class - Subscription_1_0 implements Subscription -{ - private SendingLink_1_0 _link; - - private AMQQueue _queue; - - private final AtomicReference _state = new AtomicReference(State.SUSPENDED); - - private final QueueEntry.SubscriptionAcquiredState _owningState = new QueueEntry.SubscriptionAcquiredState(this); - private final long _id; - private final boolean _acquires; - private volatile AMQQueue.Context _queueContext; - private Map _properties = new ConcurrentHashMap(); - private ReentrantLock _stateChangeLock = new ReentrantLock(); - - private boolean _noLocal; - private FilterManager _filters; - - private long _deliveryTag = 0L; - private StateListener _stateListener; - - private Binary _transactionId; - private final AMQPDescribedTypeRegistry _typeRegistry = AMQPDescribedTypeRegistry.newInstance() - .registerTransportLayer() - .registerMessagingLayer() - .registerTransactionLayer() - .registerSecurityLayer(); - private SectionEncoder _sectionEncoder = new SectionEncoderImpl(_typeRegistry); - - public Subscription_1_0(final SendingLink_1_0 link, final QueueDestination destination) - { - this(link, destination, ((Source)link.getEndpoint().getSource()).getDistributionMode() != StdDistMode.COPY); - } - - public Subscription_1_0(final SendingLink_1_0 link, final QueueDestination destination, boolean acquires) - { - _link = link; - _queue = destination.getQueue(); - _id = getEndpoint().getLocalHandle().longValue(); - _acquires = acquires; - } - - private SendingLinkEndpoint getEndpoint() - { - return _link.getEndpoint(); - } - - public LogActor getLogActor() - { - return null; //TODO - } - - public boolean isTransient() - { - return true; //TODO - } - - public AMQQueue getQueue() - { - return _queue; - } - - public QueueEntry.SubscriptionAcquiredState getOwningState() - { - return _owningState; - } - - public void setQueue(final AMQQueue queue, final boolean exclusive) - { - //TODO - } - - public void setNoLocal(final boolean noLocal) - { - _noLocal = noLocal; - } - - public long getSubscriptionID() - { - return _id; - } - - public boolean isSuspended() - { - return _link.getSession().getConnectionModel().isStopped() || !isActive();// || !getEndpoint().hasCreditToSend(); - - } - - public boolean hasInterest(final QueueEntry entry) - { - if(_noLocal && entry.getMessage().getConnectionReference() == getSession().getConnection().getReference()) - { - return false; - } - else if(!(entry.getMessage() instanceof Message_1_0) - && MessageConverterRegistry.getConverter(entry.getMessage().getClass(), Message_1_0.class)==null) - { - return false; - } - return checkFilters(entry); - - } - - private boolean checkFilters(final QueueEntry entry) - { - return (_filters == null) || _filters.allAllow(entry.asFilterable()); - } - - public boolean isClosed() - { - return !getEndpoint().isAttached(); - } - - public boolean acquires() - { - return _acquires; - } - - public boolean seesRequeues() - { - // TODO - return acquires(); - } - - public void close() - { - getEndpoint().detach(); - } - - public void send(QueueEntry entry, boolean batch) throws AMQException - { - // TODO - send(entry); - } - - public void flushBatched() - { - // TODO - } - - public void send(final QueueEntry queueEntry) throws AMQException - { - ServerMessage serverMessage = queueEntry.getMessage(); - Message_1_0 message; - if(serverMessage instanceof Message_1_0) - { - message = (Message_1_0) serverMessage; - } - else - { - final MessageConverter converter = MessageConverterRegistry.getConverter(serverMessage.getClass(), Message_1_0.class); - message = (Message_1_0) converter.convert(serverMessage, queueEntry.getQueue().getVirtualHost()); - } - - Transfer transfer = new Transfer(); - //TODO - - - List fragments = message.getFragments(); - ByteBuffer payload; - if(fragments.size() == 1) - { - payload = fragments.get(0); - } - else - { - int size = 0; - for(ByteBuffer fragment : fragments) - { - size += fragment.remaining(); - } - - payload = ByteBuffer.allocate(size); - - for(ByteBuffer fragment : fragments) - { - payload.put(fragment.duplicate()); - } - - payload.flip(); - } - - if(queueEntry.getDeliveryCount() != 0) - { - payload = payload.duplicate(); - ValueHandler valueHandler = new ValueHandler(_typeRegistry); - - Header oldHeader = null; - try - { - ByteBuffer encodedBuf = payload.duplicate(); - Object value = valueHandler.parse(payload); - if(value instanceof Header) - { - oldHeader = (Header) value; - } - else - { - payload.position(0); - } - } - catch (AmqpErrorException e) - { - //TODO - throw new RuntimeException(e); - } - - Header header = new Header(); - if(oldHeader != null) - { - header.setDurable(oldHeader.getDurable()); - header.setPriority(oldHeader.getPriority()); - header.setTtl(oldHeader.getTtl()); - } - header.setDeliveryCount(UnsignedInteger.valueOf(queueEntry.getDeliveryCount())); - _sectionEncoder.reset(); - _sectionEncoder.encodeObject(header); - Binary encodedHeader = _sectionEncoder.getEncoding(); - - ByteBuffer oldPayload = payload; - payload = ByteBuffer.allocate(oldPayload.remaining() + encodedHeader.getLength()); - payload.put(encodedHeader.getArray(),encodedHeader.getArrayOffset(),encodedHeader.getLength()); - payload.put(oldPayload); - payload.flip(); - } - - transfer.setPayload(payload); - byte[] data = new byte[8]; - ByteBuffer.wrap(data).putLong(_deliveryTag++); - final Binary tag = new Binary(data); - - transfer.setDeliveryTag(tag); - - synchronized(_link.getLock()) - { - if(_link.isAttached()) - { - if(SenderSettleMode.SETTLED.equals(getEndpoint().getSendingSettlementMode())) - { - transfer.setSettled(true); - } - else - { - UnsettledAction action = _acquires - ? new DispositionAction(tag, queueEntry) - : new DoNothingAction(tag, queueEntry); - - _link.addUnsettled(tag, action, queueEntry); - } - - if(_transactionId != null) - { - TransactionalState state = new TransactionalState(); - state.setTxnId(_transactionId); - transfer.setState(state); - } - // TODO - need to deal with failure here - if(_acquires && _transactionId != null) - { - ServerTransaction txn = _link.getTransaction(_transactionId); - if(txn != null) - { - txn.addPostTransactionAction(new ServerTransaction.Action(){ - - public void postCommit() - { - //To change body of implemented methods use File | Settings | File Templates. - } - - public void onRollback() - { - if(queueEntry.isAcquiredBy(Subscription_1_0.this)) - { - queueEntry.release(); - _link.getEndpoint().updateDisposition(tag, (DeliveryState)null, true); - - - } - } - }); - } - - } - getSession().getConnectionModel().registerMessageDelivered(message.getSize()); - getEndpoint().transfer(transfer); - } - else - { - queueEntry.release(); - } - } - - } - - public void queueDeleted(final AMQQueue queue) - { - //TODO - getEndpoint().setSource(null); - getEndpoint().detach(); - } - - public boolean wouldSuspend(final QueueEntry msg) - { - synchronized (_link.getLock()) - { - final boolean hasCredit = _link.isAttached() && getEndpoint().hasCreditToSend(); - if(!hasCredit && getState() == State.ACTIVE) - { - suspend(); - } - - return !hasCredit; - } - } - - public boolean trySendLock() - { - return _stateChangeLock.tryLock(); - } - - public void suspend() - { - synchronized(_link.getLock()) - { - if(_state.compareAndSet(State.ACTIVE, State.SUSPENDED)) - { - _stateListener.stateChange(this, State.ACTIVE, State.SUSPENDED); - } - } - } - - public void getSendLock() - { - _stateChangeLock.lock(); - } - - public void releaseSendLock() - { - _stateChangeLock.unlock(); - } - - public void releaseQueueEntry(QueueEntry queueEntryImpl) - { - //To change body of implemented methods use File | Settings | File Templates. - } - - - public void onDequeue(final QueueEntry queueEntry) - { - //TODO - } - - public void restoreCredit(final QueueEntry queueEntry) - { - //TODO - } - - public void setStateListener(final StateListener listener) - { - _stateListener = listener; - } - - public State getState() - { - return _state.get(); - } - - public AMQQueue.Context getQueueContext() - { - return _queueContext; - } - - public void setQueueContext(AMQQueue.Context queueContext) - { - _queueContext = queueContext; - } - - - public boolean isActive() - { - return getState() == State.ACTIVE; - } - - public void set(String key, Object value) - { - _properties.put(key, value); - } - - public Object get(String key) - { - return _properties.get(key); - } - - public boolean isSessionTransactional() - { - return false; //TODO - } - - public void queueEmpty() - { - synchronized(_link.getLock()) - { - if(_link.drained()) - { - if(_state.compareAndSet(State.ACTIVE, State.SUSPENDED)) - { - _stateListener.stateChange(this, State.ACTIVE, State.SUSPENDED); - } - } - } - } - - public void flowStateChanged() - { - synchronized(_link.getLock()) - { - if(isSuspended() && getEndpoint() != null) - { - if(_state.compareAndSet(State.SUSPENDED, State.ACTIVE)) - { - _stateListener.stateChange(this, State.SUSPENDED, State.ACTIVE); - } - _transactionId = _link.getTransactionId(); - } - } - } - - public Session_1_0 getSession() - { - return _link.getSession(); - } - - private class DispositionAction implements UnsettledAction - { - - private final QueueEntry _queueEntry; - private final Binary _deliveryTag; - - public DispositionAction(Binary tag, QueueEntry queueEntry) - { - _deliveryTag = tag; - _queueEntry = queueEntry; - } - - public boolean process(DeliveryState state, final Boolean settled) - { - - Binary transactionId = null; - final Outcome outcome; - // If disposition is settled this overrides the txn? - if(state instanceof TransactionalState) - { - transactionId = ((TransactionalState)state).getTxnId(); - outcome = ((TransactionalState)state).getOutcome(); - } - else if (state instanceof Outcome) - { - outcome = (Outcome) state; - } - else - { - outcome = null; - } - - - ServerTransaction txn = _link.getTransaction(transactionId); - - if(outcome instanceof Accepted) - { - txn.dequeue(_queueEntry.getQueue(), _queueEntry.getMessage(), - new ServerTransaction.Action() - { - - public void postCommit() - { - if(_queueEntry.isAcquiredBy(Subscription_1_0.this)) - { - _queueEntry.delete(); - } - } - - public void onRollback() - { - - } - }); - txn.addPostTransactionAction(new ServerTransaction.Action() - { - public void postCommit() - { - //_link.getEndpoint().settle(_deliveryTag); - _link.getEndpoint().updateDisposition(_deliveryTag, (DeliveryState)outcome, true); - _link.getEndpoint().sendFlowConditional(); - } - - public void onRollback() - { - if(Boolean.TRUE.equals(settled)) - { - final Modified modified = new Modified(); - modified.setDeliveryFailed(true); - _link.getEndpoint().updateDisposition(_deliveryTag, modified, true); - _link.getEndpoint().sendFlowConditional(); - } - } - }); - } - else if(outcome instanceof Released) - { - txn.addPostTransactionAction(new ServerTransaction.Action() - { - public void postCommit() - { - - _queueEntry.release(); - _link.getEndpoint().settle(_deliveryTag); - } - - public void onRollback() - { - _link.getEndpoint().settle(_deliveryTag); - } - }); - } - - else if(outcome instanceof Modified) - { - txn.addPostTransactionAction(new ServerTransaction.Action() - { - public void postCommit() - { - - _queueEntry.release(); - if(Boolean.TRUE.equals(((Modified)outcome).getDeliveryFailed())) - { - _queueEntry.incrementDeliveryCount(); - } - _link.getEndpoint().settle(_deliveryTag); - } - - public void onRollback() - { - if(Boolean.TRUE.equals(settled)) - { - final Modified modified = new Modified(); - modified.setDeliveryFailed(true); - _link.getEndpoint().updateDisposition(_deliveryTag, modified, true); - _link.getEndpoint().sendFlowConditional(); - } - } - }); - } - - return (transactionId == null && outcome != null); - } - } - - private class DoNothingAction implements UnsettledAction - { - public DoNothingAction(final Binary tag, - final QueueEntry queueEntry) - { - } - - public boolean process(final DeliveryState state, final Boolean settled) - { - Binary transactionId = null; - Outcome outcome = null; - // If disposition is settled this overrides the txn? - if(state instanceof TransactionalState) - { - transactionId = ((TransactionalState)state).getTxnId(); - outcome = ((TransactionalState)state).getOutcome(); - } - else if (state instanceof Outcome) - { - outcome = (Outcome) state; - } - return true; - } - } - - public FilterManager getFilters() - { - return _filters; - } - - public void setFilters(final FilterManager filters) - { - _filters = filters; - } - - @Override - public AMQSessionModel getSessionModel() - { - // TODO - return getSession(); - } - - @Override - public long getBytesOut() - { - // TODO - return 0; - } - - @Override - public long getMessagesOut() - { - // TODO - return 0; - } - - @Override - public long getUnacknowledgedBytes() - { - // TODO - return 0; - } - - @Override - public long getUnacknowledgedMessages() - { - // TODO - return 0; - } - - @Override - public String getConsumerName() - { - //TODO - return "TODO"; - } -} diff --git a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java index a71d833fc3..9ca23ce1ce 100644 --- a/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java +++ b/qpid/java/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/MessageServlet.java @@ -20,7 +20,6 @@ package org.apache.qpid.server.management.plugin.servlet.rest; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -40,7 +39,7 @@ import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.queue.QueueEntryVisitor; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.access.Operation; -import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.consumer.Consumer; import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.SerializationConfig; @@ -327,8 +326,8 @@ public class MessageServlet extends AbstractServlet : entry.isAcquired() ? "Acquired" : ""); - final Subscription deliveredSubscription = entry.getDeliveredSubscription(); - object.put("deliveredTo", deliveredSubscription == null ? null : deliveredSubscription.getSubscriptionID()); + final Consumer deliveredConsumer = entry.getDeliveredConsumer(); + object.put("deliveredTo", deliveredConsumer == null ? null : deliveredConsumer.getId()); ServerMessage message = entry.getMessage(); if(message != null) diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ConsumerLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ConsumerLoggingTest.java new file mode 100644 index 0000000000..db6da6f739 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ConsumerLoggingTest.java @@ -0,0 +1,455 @@ +/* + * + * 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.logging; + +import javax.jms.QueueBrowser; +import junit.framework.AssertionFailedError; + +import org.apache.qpid.client.AMQConnection; + +import javax.jms.Connection; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.Queue; +import javax.jms.Session; +import javax.jms.Topic; +import java.io.IOException; +import java.util.List; + +/** + * Subscription + * + * The Subscription test suite validates that the follow log messages as specified in the Functional Specification. + * + * This suite of tests validate that the Subscription messages occur correctly and according to the following format: + * + * SUB-1001 : Create : [Durable] [Arguments : ] + * SUB-1002 : Close + * SUB-1003 : State : + */ +public class ConsumerLoggingTest extends AbstractTestLogging +{ + static final String SUB_PREFIX = "SUB-"; + + private Connection _connection; + private Session _session; + private Queue _queue; + private Topic _topic; + + @Override + public void setUp() throws Exception + { + super.setUp(); + //Remove broker startup logging messages + _monitor.markDiscardPoint(); + + _connection = getConnection(); + + _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + + _queue = _session.createQueue(getTestQueueName() + "Queue"); + _topic = _session.createTopic(getTestQueueName() + "Topic"); + } + + /** + * Description: + * When a Subscription is created it will be logged. This test validates that Subscribing to a transient queue is correctly logged. + * Input: + * + * 1. Running Broker + * 2. Create a new Subscription to a transient queue/topic. + * Output: 6 + * + * SUB-1001 : Create + * + * Validation Steps: + * 3. The SUB ID is correct + * + * @throws java.io.IOException - if there is a problem getting the matches + * @throws javax.jms.JMSException - if there is a problem creating the consumer + */ + public void testSubscriptionCreate() throws JMSException, IOException + { + _session.createConsumer(_queue); + + //Validate + + //Ensure that we wait for the SUB log message + waitAndFindMatches("SUB-1001"); + + List results = findMatches(SUB_PREFIX); + + assertEquals("Result set larger than expected.", 1, results.size()); + + String log = getLogMessage(results, 0); + + validateMessageID("SUB-1001", log); + + assertEquals("Log Message not as expected", "Create", getMessageString(fromMessage(log))); + } + + /** + * Description: + * The creation of a Durable Subscription, such as a JMS DurableTopicSubscriber will result in an extra Durable tag being included in the Create log message + * Input: + * + * 1. Running Broker + * 2. Creation of a JMS DurableTopicSubiber + * Output: + * + * SUB-1001 : Create : Durable + * + * Validation Steps: + * 3. The SUB ID is correct + * 4. The Durable tag is present in the message + * NOTE: A Subscription is not Durable, the queue it consumes from is. + * + * @throws java.io.IOException - if there is a problem getting the matches + * @throws javax.jms.JMSException - if there is a problem creating the consumer + */ + public void testSubscriptionCreateDurable() throws JMSException, IOException + { + _session.createDurableSubscriber(_topic, getName()); + + //Validate + //Ensure that we wait for the SUB log message + waitAndFindMatches("SUB-1001"); + + List results = findMatches(SUB_PREFIX); + + assertEquals("Result set not as expected.", 1, results.size()); + + String log = getLogMessage(results, 0); + + validateMessageID("SUB-1001", log); + + String message = getMessageString(fromMessage(log)); + assertTrue("Durable not on log message:" + message, message.contains("Durable")); + } + + /** + * Description: + * The creation of a QueueBrowser will provides a number arguments and so should form part of the SUB-1001 Create message. + * Input: + * + * 1. Running Broker + * 2. Java Client creates a QueueBroweser + * Output: + * + * SUB-1001 : Create : Arguments : + * + * Validation Steps: + * 3. The SUB ID is correct + * 4. The Arguments are present in the message + * 5. Arguments keys include AutoClose and Browser. + * + * @throws java.io.IOException - if there is a problem getting the matches + * @throws javax.jms.JMSException - if there is a problem creating the consumer + */ + public void testSubscriptionCreateQueueBrowser() throws JMSException, IOException + { + _connection.start(); + QueueBrowser browser = _session.createBrowser(_queue); + + browser.getEnumeration(); + //Validate + //Ensure that we wait for the SUB log message + waitAndFindMatches("SUB-1001"); + + List results = findMatches(SUB_PREFIX); + + final int expected = isBroker010() ? 5 : 2; + assertEquals("Result set larger than expected.", expected, results.size()); + + String log = getLogMessage(results, 0); + + validateMessageID("SUB-1001", log); + + String message = getMessageString(fromMessage(log)); + assertTrue("Browser not on log message:" + message, message.contains("Browser")); + + // Beacause it is an auto close and we have no messages on the queue we + // will get a close message + log = getLogMessage(results, expected-1); + validateMessageID("SUB-1002", log); + + } + + /** + * Description: + * The creation of a Subscriber with a JMS Selector will result in the Argument field being populated. These argument key/value pairs are then shown in the log message. + * Input: + * + * 1. Running Broker + * 2. Subscriber created with a JMS Selector. + * Output: + * + * SUB-1001 : Create : Arguments : + * + * Validation Steps: + * 3. The SUB ID is correct + * 4. Argument tag is present in the message + * + * @throws java.io.IOException - if there is a problem getting the matches + * @throws javax.jms.JMSException - if there is a problem creating the consumer + */ + public void testSubscriptionCreateWithArguments() throws JMSException, IOException + { + final String SELECTOR = "Selector='True'"; + _session.createConsumer(_queue, SELECTOR); + + //Validate + + //Ensure that we wait for the SUB log message + waitAndFindMatches("SUB-1001"); + + List results = findMatches(SUB_PREFIX); + + assertEquals("Result set larger than expected.", 1, results.size()); + + String log = getLogMessage(results, 0); + + validateMessageID("SUB-1001", log); + + String message = getMessageString(fromMessage(log)); + assertTrue("Selector not on log message:" + message, message.contains(SELECTOR)); + } + + /** + * Description: + * The final combination of SUB-1001 Create messages involves the creation of a Durable Subscription that also contains a set of Arguments, such as those provided via a JMS Selector. + * Input: + * + * 1. Running Broker + * 2. Java Client creates a Durable Subscription with Selector + * Output: + * + * SUB-1001 : Create : Durable Arguments : + * + * Validation Steps: + * 3. The SUB ID is correct + * 4. The tag Durable is present in the message + * 5. The Arguments are present in the message + * + * @throws java.io.IOException - if there is a problem getting the matches + * @throws javax.jms.JMSException - if there is a problem creating the consumer + */ + public void testSubscriptionCreateDurableWithArguments() throws JMSException, IOException + { + final String SELECTOR = "Selector='True'"; + _session.createDurableSubscriber(_topic, getName(), SELECTOR, false); + + //Validate + + //Ensure that we wait for the SUB log message + waitAndFindMatches("SUB-1001"); + + List results = findMatches(SUB_PREFIX); + + assertEquals("Result set larger than expected.", 1, results.size()); + + String log = getLogMessage(results, 0); + + validateMessageID("SUB-1001", log); + + String message = getMessageString(fromMessage(log)); + assertTrue("Durable not on log message:" + message, message.contains("Durable")); + assertTrue("Selector not on log message:" + message, message.contains(SELECTOR)); + } + + /** + * Description: + * When a Subscription is closed it will log this so that it can be correlated with the Create. + * Input: + * + * 1. Running Broker + * 2. Client with a subscription. + * 3. The subscription is then closed. + * Output: + * + * SUB-1002 : Close + * + * Validation Steps: + * 1. The SUB ID is correct + * 2. There must be a SUB-1001 Create message preceding this message + * 3. This must be the last message from the given Subscription + * + * @throws java.io.IOException - if there is a problem getting the matches + * @throws javax.jms.JMSException - if there is a problem creating the consumer + */ + public void testSubscriptionClose() throws JMSException, IOException + { + _session.createConsumer(_queue).close(); + + //Validate + //Ensure that we wait for the SUB log message + waitAndFindMatches("SUB-1002"); + + List results = findMatches(SUB_PREFIX); + + //3 + assertEquals("Result set larger than expected.", 2, results.size()); + + // 2 + String log = getLogMessage(results, 0); + validateMessageID("SUB-1001", log); + // 1 + log = getLogMessage(results, 1); + validateMessageID("SUB-1002", log); + + String message = getMessageString(fromMessage(log)); + assertEquals("Log message is not close", "Close", message); + + } + + /** + * Description: + * When a Subscription fills its prefetch it will become suspended. This + * will be logged as a SUB-1003 message. + * Input: + * + * 1. Running broker + * 2. Message Producer to put more data on the queue than the client's prefetch + * 3. Client that ensures that its prefetch becomes full + * Output: + * + * SUB-1003 : State : + * + * Validation Steps: + * 1. The SUB ID is correct + * 2. The state is correct + * + * @throws java.io.IOException - if there is a problem getting the matches + * @throws javax.jms.JMSException - if there is a problem creating the consumer + */ + public void testSubscriptionSuspend() throws Exception, IOException + { + //Close session with large prefetch + _connection.createSession(false, Session.AUTO_ACKNOWLEDGE).close(); + + int PREFETCH = 15; + + //Create new session with small prefetch + _session = ((AMQConnection) _connection).createSession(true, Session.SESSION_TRANSACTED, PREFETCH); + + MessageConsumer consumer = _session.createConsumer(_queue); + + _connection.start(); + + //Start the dispatcher & Unflow the channel. + consumer.receiveNoWait(); + + //Fill the prefetch and two extra so that our receive bellow allows the + // subscription to become active + // Previously we set this to 17 so that it would return to a suspended + // state. However, testing has shown that the state change can occur + // sufficiently quickly that logging does not occur consistently enough + // for testing. + int SEND_COUNT = 16; + sendMessage(_session, _queue, SEND_COUNT); + _session.commit(); + // Retreive the first message, and start the flow of messages + Message msg = consumer.receive(1000); + assertNotNull("First message not retreived", msg); + _session.commit(); + + // Drain the queue to ensure there is time for the ACTIVE log message + // Check that we can received all the messages + int receivedCount = 0; + while (msg != null) + { + receivedCount++; + msg = consumer.receive(1000); + _session.commit(); + } + + //Validate we received all the messages + assertEquals("Not all sent messages received.", SEND_COUNT, receivedCount); + + // Fill the queue again to suspend the consumer + sendMessage(_session, _queue, SEND_COUNT); + _session.commit(); + + //Validate + List results = waitAndFindMatches("SUB-1003"); + + try + { + // Validation expects three messages. + // The Actor can be any one of the following depending on the exactly what is going on on the broker. + // Ideally we would test that we can get all of them but setting up + // the timing to do this in a consistent way is not benefitial. + // Ensuring the State is as expected is sufficient. +// INFO - MESSAGE [vh(/test)/qu(example.queue)] [sub:6(qu(example.queue))] SUB-1003 : State : +// INFO - MESSAGE [con:6(guest@anonymous(26562441)/test)/ch:3] [sub:6(qu(example.queue))] SUB-1003 : State : +// INFO - MESSAGE [sub:6(vh(test)/qu(example.queue))] [sub:6(qu(example.queue))] SUB-1003 : State : + + assertEquals("Result set not expected size:", 3, results.size()); + + // Validate Initial Suspension + String expectedState = "SUSPENDED"; + String log = getLogMessage(results, 0); + validateSubscriptionState(log, expectedState); + + // After being suspended the subscription should become active. + expectedState = "ACTIVE"; + log = getLogMessage(results, 1); + validateSubscriptionState(log, expectedState); + + // Validate that it was re-suspended + expectedState = "SUSPENDED"; + log = getLogMessage(results, 2); + validateSubscriptionState(log, expectedState); + // We only need validate the state. + } + catch (AssertionFailedError afe) + { + System.err.println("Log Dump:"); + for (String log : results) + { + System.err.println(log); + } + throw afe; + } + _connection.close(); + } + + /** + * Validate that the given log statement is a well formatted SUB-1003 + * message. That means the ID and expected state are correct. + * + * @param log the log to test + * @param expectedState the state that should be logged. + */ + private void validateSubscriptionState(String log, String expectedState) + { + validateMessageID("SUB-1003", log); + String logMessage = getMessageString(fromMessage(log)); + assertTrue("Log Message does not start with 'State'" + logMessage, + logMessage.startsWith("State")); + + assertTrue("Log Message does not have expected State of '" + + expectedState + "'" + logMessage, + logMessage.endsWith(expectedState)); + } + +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java deleted file mode 100644 index 9f532ec5f7..0000000000 --- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java +++ /dev/null @@ -1,458 +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.logging; - -import javax.jms.QueueBrowser; -import junit.framework.AssertionFailedError; - -import org.apache.qpid.client.AMQConnection; - -import javax.jms.Connection; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageConsumer; -import javax.jms.Queue; -import javax.jms.Session; -import javax.jms.Topic; -import java.io.IOException; -import java.util.List; - -/** - * Subscription - * - * The Subscription test suite validates that the follow log messages as specified in the Functional Specification. - * - * This suite of tests validate that the Subscription messages occur correctly and according to the following format: - * - * SUB-1001 : Create : [Durable] [Arguments : ] - * SUB-1002 : Close - * SUB-1003 : State : - */ -public class SubscriptionLoggingTest extends AbstractTestLogging -{ - static final String SUB_PREFIX = "SUB-"; - - private Connection _connection; - private Session _session; - private Queue _queue; - private Topic _topic; - - @Override - public void setUp() throws Exception - { - super.setUp(); - //Remove broker startup logging messages - _monitor.markDiscardPoint(); - - _connection = getConnection(); - - _session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); - - _queue = _session.createQueue(getTestQueueName() + "Queue"); - _topic = _session.createTopic(getTestQueueName() + "Topic"); - } - - /** - * Description: - * When a Subscription is created it will be logged. This test validates that Subscribing to a transient queue is correctly logged. - * Input: - * - * 1. Running Broker - * 2. Create a new Subscription to a transient queue/topic. - * Output: 6 - * - * SUB-1001 : Create - * - * Validation Steps: - * 3. The SUB ID is correct - * - * @throws java.io.IOException - if there is a problem getting the matches - * @throws javax.jms.JMSException - if there is a problem creating the consumer - */ - public void testSubscriptionCreate() throws JMSException, IOException - { - _session.createConsumer(_queue); - - //Validate - - //Ensure that we wait for the SUB log message - waitAndFindMatches("SUB-1001"); - - List results = findMatches(SUB_PREFIX); - - assertEquals("Result set larger than expected.", 1, results.size()); - - String log = getLogMessage(results, 0); - - validateMessageID("SUB-1001", log); - - assertEquals("Log Message not as expected", "Create", getMessageString(fromMessage(log))); - } - - /** - * Description: - * The creation of a Durable Subscription, such as a JMS DurableTopicSubscriber will result in an extra Durable tag being included in the Create log message - * Input: - * - * 1. Running Broker - * 2. Creation of a JMS DurableTopicSubiber - * Output: - * - * SUB-1001 : Create : Durable - * - * Validation Steps: - * 3. The SUB ID is correct - * 4. The Durable tag is present in the message - * NOTE: A Subscription is not Durable, the queue it consumes from is. - * - * @throws java.io.IOException - if there is a problem getting the matches - * @throws javax.jms.JMSException - if there is a problem creating the consumer - */ - public void testSubscriptionCreateDurable() throws JMSException, IOException - { - _session.createDurableSubscriber(_topic, getName()); - - //Validate - //Ensure that we wait for the SUB log message - waitAndFindMatches("SUB-1001"); - - List results = findMatches(SUB_PREFIX); - - assertEquals("Result set not as expected.", 1, results.size()); - - String log = getLogMessage(results, 0); - - validateMessageID("SUB-1001", log); - - String message = getMessageString(fromMessage(log)); - assertTrue("Durable not on log message:" + message, message.contains("Durable")); - } - - /** - * Description: - * The creation of a QueueBrowser will provides a number arguments and so should form part of the SUB-1001 Create message. - * Input: - * - * 1. Running Broker - * 2. Java Client creates a QueueBroweser - * Output: - * - * SUB-1001 : Create : Arguments : - * - * Validation Steps: - * 3. The SUB ID is correct - * 4. The Arguments are present in the message - * 5. Arguments keys include AutoClose and Browser. - * - * @throws java.io.IOException - if there is a problem getting the matches - * @throws javax.jms.JMSException - if there is a problem creating the consumer - */ - public void testSubscriptionCreateQueueBrowser() throws JMSException, IOException - { - _connection.start(); - QueueBrowser browser = _session.createBrowser(_queue); - - browser.getEnumeration(); - //Validate - //Ensure that we wait for the SUB log message - waitAndFindMatches("SUB-1001"); - - List results = findMatches(SUB_PREFIX); - - assertEquals("Result set larger than expected.", 2, results.size()); - - String log = getLogMessage(results, 0); - - validateMessageID("SUB-1001", log); - - String message = getMessageString(fromMessage(log)); - assertTrue("Browser not on log message:" + message, message.contains("Browser")); - if(!isBroker010()) - { - assertTrue("AutoClose not on log message:" + message, message.contains("AutoClose")); - } - - // Beacause it is an auto close and we have no messages on the queue we - // will get a close message - log = getLogMessage(results, 1); - validateMessageID("SUB-1002", log); - - } - - /** - * Description: - * The creation of a Subscriber with a JMS Selector will result in the Argument field being populated. These argument key/value pairs are then shown in the log message. - * Input: - * - * 1. Running Broker - * 2. Subscriber created with a JMS Selector. - * Output: - * - * SUB-1001 : Create : Arguments : - * - * Validation Steps: - * 3. The SUB ID is correct - * 4. Argument tag is present in the message - * - * @throws java.io.IOException - if there is a problem getting the matches - * @throws javax.jms.JMSException - if there is a problem creating the consumer - */ - public void testSubscriptionCreateWithArguments() throws JMSException, IOException - { - final String SELECTOR = "Selector='True'"; - _session.createConsumer(_queue, SELECTOR); - - //Validate - - //Ensure that we wait for the SUB log message - waitAndFindMatches("SUB-1001"); - - List results = findMatches(SUB_PREFIX); - - assertEquals("Result set larger than expected.", 1, results.size()); - - String log = getLogMessage(results, 0); - - validateMessageID("SUB-1001", log); - - String message = getMessageString(fromMessage(log)); - assertTrue("Selector not on log message:" + message, message.contains(SELECTOR)); - } - - /** - * Description: - * The final combination of SUB-1001 Create messages involves the creation of a Durable Subscription that also contains a set of Arguments, such as those provided via a JMS Selector. - * Input: - * - * 1. Running Broker - * 2. Java Client creates a Durable Subscription with Selector - * Output: - * - * SUB-1001 : Create : Durable Arguments : - * - * Validation Steps: - * 3. The SUB ID is correct - * 4. The tag Durable is present in the message - * 5. The Arguments are present in the message - * - * @throws java.io.IOException - if there is a problem getting the matches - * @throws javax.jms.JMSException - if there is a problem creating the consumer - */ - public void testSubscriptionCreateDurableWithArguments() throws JMSException, IOException - { - final String SELECTOR = "Selector='True'"; - _session.createDurableSubscriber(_topic, getName(), SELECTOR, false); - - //Validate - - //Ensure that we wait for the SUB log message - waitAndFindMatches("SUB-1001"); - - List results = findMatches(SUB_PREFIX); - - assertEquals("Result set larger than expected.", 1, results.size()); - - String log = getLogMessage(results, 0); - - validateMessageID("SUB-1001", log); - - String message = getMessageString(fromMessage(log)); - assertTrue("Durable not on log message:" + message, message.contains("Durable")); - assertTrue("Selector not on log message:" + message, message.contains(SELECTOR)); - } - - /** - * Description: - * When a Subscription is closed it will log this so that it can be correlated with the Create. - * Input: - * - * 1. Running Broker - * 2. Client with a subscription. - * 3. The subscription is then closed. - * Output: - * - * SUB-1002 : Close - * - * Validation Steps: - * 1. The SUB ID is correct - * 2. There must be a SUB-1001 Create message preceding this message - * 3. This must be the last message from the given Subscription - * - * @throws java.io.IOException - if there is a problem getting the matches - * @throws javax.jms.JMSException - if there is a problem creating the consumer - */ - public void testSubscriptionClose() throws JMSException, IOException - { - _session.createConsumer(_queue).close(); - - //Validate - //Ensure that we wait for the SUB log message - waitAndFindMatches("SUB-1002"); - - List results = findMatches(SUB_PREFIX); - - //3 - assertEquals("Result set larger than expected.", 2, results.size()); - - // 2 - String log = getLogMessage(results, 0); - validateMessageID("SUB-1001", log); - // 1 - log = getLogMessage(results, 1); - validateMessageID("SUB-1002", log); - - String message = getMessageString(fromMessage(log)); - assertEquals("Log message is not close", "Close", message); - - } - - /** - * Description: - * When a Subscription fills its prefetch it will become suspended. This - * will be logged as a SUB-1003 message. - * Input: - * - * 1. Running broker - * 2. Message Producer to put more data on the queue than the client's prefetch - * 3. Client that ensures that its prefetch becomes full - * Output: - * - * SUB-1003 : State : - * - * Validation Steps: - * 1. The SUB ID is correct - * 2. The state is correct - * - * @throws java.io.IOException - if there is a problem getting the matches - * @throws javax.jms.JMSException - if there is a problem creating the consumer - */ - public void testSubscriptionSuspend() throws Exception, IOException - { - //Close session with large prefetch - _connection.createSession(false, Session.AUTO_ACKNOWLEDGE).close(); - - int PREFETCH = 15; - - //Create new session with small prefetch - _session = ((AMQConnection) _connection).createSession(true, Session.SESSION_TRANSACTED, PREFETCH); - - MessageConsumer consumer = _session.createConsumer(_queue); - - _connection.start(); - - //Start the dispatcher & Unflow the channel. - consumer.receiveNoWait(); - - //Fill the prefetch and two extra so that our receive bellow allows the - // subscription to become active - // Previously we set this to 17 so that it would return to a suspended - // state. However, testing has shown that the state change can occur - // sufficiently quickly that logging does not occur consistently enough - // for testing. - int SEND_COUNT = 16; - sendMessage(_session, _queue, SEND_COUNT); - _session.commit(); - // Retreive the first message, and start the flow of messages - Message msg = consumer.receive(1000); - assertNotNull("First message not retreived", msg); - _session.commit(); - - // Drain the queue to ensure there is time for the ACTIVE log message - // Check that we can received all the messages - int receivedCount = 0; - while (msg != null) - { - receivedCount++; - msg = consumer.receive(1000); - _session.commit(); - } - - //Validate we received all the messages - assertEquals("Not all sent messages received.", SEND_COUNT, receivedCount); - - // Fill the queue again to suspend the consumer - sendMessage(_session, _queue, SEND_COUNT); - _session.commit(); - - //Validate - List results = waitAndFindMatches("SUB-1003"); - - try - { - // Validation expects three messages. - // The Actor can be any one of the following depending on the exactly what is going on on the broker. - // Ideally we would test that we can get all of them but setting up - // the timing to do this in a consistent way is not benefitial. - // Ensuring the State is as expected is sufficient. -// INFO - MESSAGE [vh(/test)/qu(example.queue)] [sub:6(qu(example.queue))] SUB-1003 : State : -// INFO - MESSAGE [con:6(guest@anonymous(26562441)/test)/ch:3] [sub:6(qu(example.queue))] SUB-1003 : State : -// INFO - MESSAGE [sub:6(vh(test)/qu(example.queue))] [sub:6(qu(example.queue))] SUB-1003 : State : - - assertEquals("Result set not expected size:", 3, results.size()); - - // Validate Initial Suspension - String expectedState = "SUSPENDED"; - String log = getLogMessage(results, 0); - validateSubscriptionState(log, expectedState); - - // After being suspended the subscription should become active. - expectedState = "ACTIVE"; - log = getLogMessage(results, 1); - validateSubscriptionState(log, expectedState); - - // Validate that it was re-suspended - expectedState = "SUSPENDED"; - log = getLogMessage(results, 2); - validateSubscriptionState(log, expectedState); - // We only need validate the state. - } - catch (AssertionFailedError afe) - { - System.err.println("Log Dump:"); - for (String log : results) - { - System.err.println(log); - } - throw afe; - } - _connection.close(); - } - - /** - * Validate that the given log statement is a well formatted SUB-1003 - * message. That means the ID and expected state are correct. - * - * @param log the log to test - * @param expectedState the state that should be logged. - */ - private void validateSubscriptionState(String log, String expectedState) - { - validateMessageID("SUB-1003", log); - String logMessage = getMessageString(fromMessage(log)); - assertTrue("Log Message does not start with 'State'" + logMessage, - logMessage.startsWith("State")); - - assertTrue("Log Message does not have expected State of '" - + expectedState + "'" + logMessage, - logMessage.endsWith(expectedState)); - } - -} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/ConnectionRestTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/ConnectionRestTest.java index 05c8e362a1..d558e07ed7 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/ConnectionRestTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/systest/rest/ConnectionRestTest.java @@ -67,16 +67,15 @@ public class ConnectionRestTest extends QpidRestTestCase producer.send(_session.createTextMessage("Test-" + i)); } _session.commit(); - Message m = consumer.receive(1000l); - assertNotNull("Message was not received", m); + assertNotNull("First message was not received", m); _session.commit(); // receive the rest of messages for rollback for (int i = 0; i < MESSAGE_NUMBER - 1; i++) { m = consumer.receive(1000l); - assertNotNull("Message was not received", m); + assertNotNull("Subsequent messages were not received", m); } _session.rollback(); @@ -84,7 +83,7 @@ public class ConnectionRestTest extends QpidRestTestCase for (int i = 0; i < MESSAGE_NUMBER - 1; i++) { m = consumer.receive(1000l); - assertNotNull("Message was not received", m); + assertNotNull("Message was not received after rollback", m); } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/RollbackOrderTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/RollbackOrderTest.java index 54c3225cec..b6f81da690 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/RollbackOrderTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/RollbackOrderTest.java @@ -32,6 +32,7 @@ import javax.jms.MessageListener; import javax.jms.Queue; import javax.jms.Session; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -169,7 +170,8 @@ public class RollbackOrderTest extends QpidBrokerTestCase //Start the session now so we _connection.start(); - count.await(); + count.await(10l, TimeUnit.SECONDS); + assertEquals("Not all message received. Count should be 0.", 0, count.getCount()); for (Exception e : exceptions) { diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/MaxDeliveryCountTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/MaxDeliveryCountTest.java index 40db17f799..6356b17e6f 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/MaxDeliveryCountTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/MaxDeliveryCountTest.java @@ -26,6 +26,7 @@ import org.apache.qpid.AMQException; import org.apache.qpid.client.AMQDestination; import org.apache.qpid.client.AMQQueue; import org.apache.qpid.client.AMQSession; +import org.apache.qpid.client.RejectBehaviour; import org.apache.qpid.configuration.ClientProperties; import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.test.utils.QpidBrokerTestCase; @@ -86,7 +87,7 @@ public class MaxDeliveryCountTest extends QpidBrokerTestCase // dead-lettered or requeued. if (!isBroker010()) { - setTestClientSystemProperty(ClientProperties.REJECT_BEHAVIOUR_PROP_NAME, "server"); + setTestClientSystemProperty(ClientProperties.REJECT_BEHAVIOUR_PROP_NAME, RejectBehaviour.SERVER.toString()); } super.setUp(); diff --git a/qpid/java/test-profiles/Java010Excludes b/qpid/java/test-profiles/Java010Excludes index 8da2b5edd8..67c2fcd5ad 100755 --- a/qpid/java/test-profiles/Java010Excludes +++ b/qpid/java/test-profiles/Java010Excludes @@ -33,7 +33,7 @@ org.apache.qpid.test.unit.topic.DurableSubscriptionTest#testUnsubscribeWhenUsing // 0-10 and 0-9 connections dont generate the exact same logging due to protocol differences org.apache.qpid.server.logging.ChannelLoggingTest#testChannelStartsFlowStopped org.apache.qpid.server.logging.ChannelLoggingTest#testChannelStartConsumerFlowStarted -org.apache.qpid.server.logging.SubscriptionLoggingTest#testSubscriptionSuspend +org.apache.qpid.server.logging.ConsumerLoggingTest#testSubscriptionSuspend org.apache.qpid.server.logging.ChannelLoggingTest#testChannelClosedOnQueueArgumentsMismatch // 0-10 is not supported by the MethodRegistry -- cgit v1.2.1