From 633c33f224f3196f3f9bd80bd2e418d8143fea06 Mon Sep 17 00:00:00 2001 From: Kim van der Riet Date: Fri, 4 May 2012 15:39:19 +0000 Subject: QPID-3858: Updated branch - merged from trunk r.1333987 git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/asyncstore@1334037 13f79535-47bb-0310-9956-ffa450edef68 --- java/broker/src/main/java/broker.bnd | 2 +- .../org/apache/qpid/qmf/ManagementExchange.java | 22 +- .../main/java/org/apache/qpid/qmf/QMFService.java | 5 + .../apache/qpid/server/AMQBrokerManagerMBean.java | 74 +- .../java/org/apache/qpid/server/AMQChannel.java | 92 +- .../main/java/org/apache/qpid/server/Broker.java | 147 +- .../java/org/apache/qpid/server/BrokerOptions.java | 3 - .../src/main/java/org/apache/qpid/server/Main.java | 7 + .../org/apache/qpid/server/ProtocolExclusion.java | 3 +- .../org/apache/qpid/server/binding/Binding.java | 2 +- .../apache/qpid/server/binding/BindingFactory.java | 94 +- .../server/configuration/ServerConfiguration.java | 13 +- .../ServerNetworkTransportConfiguration.java | 18 +- .../server/configuration/VirtualHostConfig.java | 2 - .../configuration/VirtualHostConfiguration.java | 30 +- .../qpid/server/connection/ConnectionRegistry.java | 12 +- .../server/connection/IConnectionRegistry.java | 9 +- .../qpid/server/exchange/AbstractExchange.java | 27 +- .../server/exchange/DefaultExchangeFactory.java | 27 +- .../server/exchange/DefaultExchangeRegistry.java | 53 +- .../qpid/server/exchange/DirectExchange.java | 11 +- .../org/apache/qpid/server/exchange/Exchange.java | 6 +- .../qpid/server/exchange/ExchangeFactory.java | 7 + .../qpid/server/exchange/ExchangeInitialiser.java | 5 +- .../qpid/server/exchange/ExchangeRegistry.java | 7 +- .../apache/qpid/server/exchange/ExchangeType.java | 4 +- .../qpid/server/exchange/FanoutExchange.java | 10 +- .../qpid/server/exchange/HeadersBinding.java | 42 +- .../qpid/server/exchange/HeadersExchange.java | 42 +- .../apache/qpid/server/exchange/TopicExchange.java | 216 +- .../org/apache/qpid/server/federation/Bridge.java | 10 +- .../apache/qpid/server/federation/BrokerLink.java | 4 +- .../qpid/server/filter/SimpleFilterManager.java | 6 + .../handler/ConnectionOpenMethodHandler.java | 9 +- .../server/handler/ExchangeDeclareHandler.java | 29 +- .../qpid/server/handler/QueueBindHandler.java | 2 +- .../qpid/server/handler/QueueDeclareHandler.java | 12 +- .../qpid/server/handler/QueueDeleteHandler.java | 2 +- .../qpid/server/logging/actors/CurrentActor.java | 16 +- .../messages/ConfigStore_logmessages.properties | 3 +- .../messages/MessageStore_logmessages.properties | 6 +- .../messages/TransactionLog_logmessages.properties | 9 +- .../server/logging/subjects/BindingLogSubject.java | 3 +- .../logging/subjects/MessageStoreLogSubject.java | 8 +- .../AbstractAMQManagedConnectionObject.java | 20 + .../management/JMXManagedObjectRegistry.java | 4 +- .../qpid/server/message/MessageMetaData_0_10.java | 11 + .../qpid/server/message/MessageMetaData_1_0.java | 522 +++++ .../java/org/apache/qpid/server/model/Binding.java | 75 + .../server/model/ConfigurationChangeListener.java | 38 + .../apache/qpid/server/model/ConfiguredObject.java | 229 ++ .../org/apache/qpid/server/model/Consumer.java | 73 + .../org/apache/qpid/server/model/Exchange.java | 91 + .../model/IllegalStateTransitionException.java | 43 + .../apache/qpid/server/model/LifetimePolicy.java | 27 + .../org/apache/qpid/server/model/Publisher.java | 25 + .../java/org/apache/qpid/server/model/Queue.java | 146 ++ .../java/org/apache/qpid/server/model/State.java | 30 + .../org/apache/qpid/server/model/Statistics.java | 25 + .../apache/qpid/server/model/UUIDGenerator.java | 54 + .../qpid/server/protocol/AMQProtocolEngine.java | 8 +- .../qpid/server/protocol/AMQSessionModel.java | 8 +- .../qpid/server/protocol/AmqpProtocolVersion.java | 2 +- .../protocol/MultiVersionProtocolEngine.java | 64 +- .../qpid/server/protocol/ProtocolEngine_1_0_0.java | 394 ++++ .../server/protocol/ProtocolEngine_1_0_0_SASL.java | 449 ++++ .../qpid/server/protocol/v1_0/Connection_1_0.java | 98 + .../qpid/server/protocol/v1_0/Destination.java | 28 + .../server/protocol/v1_0/ExchangeDestination.java | 108 + .../qpid/server/protocol/v1_0/LinkRegistry.java | 59 + .../apache/qpid/server/protocol/v1_0/Link_1_0.java | 26 + .../qpid/server/protocol/v1_0/Message_1_0.java | 172 ++ .../server/protocol/v1_0/QueueDestination.java | 100 + .../server/protocol/v1_0/ReceivingDestination.java | 35 + .../protocol/v1_0/ReceivingLinkAttachment.java | 51 + .../server/protocol/v1_0/ReceivingLink_1_0.java | 305 +++ .../server/protocol/v1_0/SendingDestination.java | 27 + .../protocol/v1_0/SendingLinkAttachment.java | 44 + .../qpid/server/protocol/v1_0/SendingLink_1_0.java | 648 ++++++ .../qpid/server/protocol/v1_0/Session_1_0.java | 446 ++++ .../server/protocol/v1_0/Subscription_1_0.java | 634 +++++ .../protocol/v1_0/TxnCoordinatorLink_1_0.java | 195 ++ .../qpid/server/protocol/v1_0/UnsettledAction.java | 8 + .../apache/qpid/server/queue/AMQPriorityQueue.java | 9 +- .../org/apache/qpid/server/queue/AMQQueue.java | 6 +- .../apache/qpid/server/queue/AMQQueueFactory.java | 58 +- .../apache/qpid/server/queue/AMQQueueMBean.java | 12 +- .../apache/qpid/server/queue/ConflationQueue.java | 13 +- .../qpid/server/queue/DefaultQueueRegistry.java | 38 + .../apache/qpid/server/queue/IncomingMessage.java | 41 +- .../apache/qpid/server/queue/OutOfOrderQueue.java | 29 +- .../qpid/server/queue/PriorityQueueList.java | 50 +- .../org/apache/qpid/server/queue/QueueContext.java | 9 + .../apache/qpid/server/queue/QueueEntryImpl.java | 17 +- .../qpid/server/queue/QueueEntryVisitor.java | 22 + .../apache/qpid/server/queue/QueueRegistry.java | 5 + .../apache/qpid/server/queue/SimpleAMQQueue.java | 223 +- .../qpid/server/queue/SimpleQueueEntryList.java | 25 +- .../org/apache/qpid/server/queue/SortedQueue.java | 11 +- .../qpid/server/registry/ApplicationRegistry.java | 60 +- .../qpid/server/registry/BrokerConfigAdapter.java | 1 - .../ConfigurationFileApplicationRegistry.java | 18 - .../auth/manager/AuthenticationManager.java | 3 + .../PrincipalDatabaseAuthenticationManager.java | 5 + .../sasl/anonymous/AnonymousSaslServerFactory.java | 2 +- .../security/auth/sasl/plain/PlainSaslServer.java | 63 +- .../qpid/server/store/AbstractMessageStore.java | 43 - .../server/store/ConfigurationRecoveryHandler.java | 15 +- .../qpid/server/store/ConfiguredObjectHelper.java | 183 ++ .../qpid/server/store/ConfiguredObjectRecord.java | 65 + .../qpid/server/store/DerbyMessageStore.java | 2363 ------------------- .../server/store/DurableConfigurationStore.java | 29 +- .../java/org/apache/qpid/server/store/Event.java | 32 + .../apache/qpid/server/store/EventListener.java | 25 + .../org/apache/qpid/server/store/EventManager.java | 55 + .../qpid/server/store/MemoryMessageStore.java | 185 +- .../server/store/MemoryMessageStoreFactory.java | 37 + .../qpid/server/store/MessageMetaDataType.java | 5 +- .../org/apache/qpid/server/store/MessageStore.java | 110 +- .../qpid/server/store/MessageStoreConstants.java | 27 + .../qpid/server/store/MessageStoreFactory.java | 27 + .../apache/qpid/server/store/NullMessageStore.java | 146 ++ .../server/store/OperationalLoggingListener.java | 73 + .../java/org/apache/qpid/server/store/State.java | 38 + .../org/apache/qpid/server/store/StateManager.java | 151 ++ .../org/apache/qpid/server/store/StoreFuture.java | 40 + .../qpid/server/store/StoredMemoryMessage.java | 4 +- .../apache/qpid/server/store/StoredMessage.java | 2 +- .../org/apache/qpid/server/store/Transaction.java | 81 + .../store/TransactionLogRecoveryHandler.java | 13 +- .../qpid/server/store/TransactionLogResource.java | 4 +- .../qpid/server/store/derby/DerbyMessageStore.java | 2449 ++++++++++++++++++++ .../store/derby/DerbyMessageStoreFactory.java | 40 + .../qpid/server/subscription/Subscription.java | 8 +- .../qpid/server/subscription/SubscriptionImpl.java | 14 +- .../server/subscription/Subscription_0_10.java | 35 +- .../server/transport/ServerConnectionDelegate.java | 24 +- .../qpid/server/transport/ServerSession.java | 194 +- .../server/transport/ServerSessionDelegate.java | 446 +++- .../qpid/server/txn/AlreadyKnownDtxException.java | 32 + .../server/txn/AsyncAutoCommitTransaction.java | 35 +- .../qpid/server/txn/AutoCommitTransaction.java | 11 +- .../qpid/server/txn/DistributedTransaction.java | 247 ++ .../java/org/apache/qpid/server/txn/DtxBranch.java | 349 +++ .../org/apache/qpid/server/txn/DtxException.java | 44 + .../qpid/server/txn/DtxNotSelectedException.java | 30 + .../org/apache/qpid/server/txn/DtxRegistry.java | 333 +++ .../server/txn/IncorrectDtxStateException.java | 32 + .../qpid/server/txn/JoinAndResumeDtxException.java | 32 + .../apache/qpid/server/txn/LocalTransaction.java | 3 +- .../qpid/server/txn/NotAssociatedDtxException.java | 32 + .../qpid/server/txn/RollbackOnlyDtxException.java | 32 + .../apache/qpid/server/txn/ServerTransaction.java | 5 +- .../server/txn/SuspendAndFailDtxException.java | 32 + .../qpid/server/txn/TimeoutDtxException.java | 32 + .../qpid/server/txn/UnknownDtxBranchException.java | 32 + .../apache/qpid/server/util/MapJsonSerializer.java | 69 + .../org/apache/qpid/server/virtualhost/State.java | 29 + .../qpid/server/virtualhost/VirtualHost.java | 21 +- .../VirtualHostConfigRecoveryHandler.java | 259 ++- .../qpid/server/virtualhost/VirtualHostImpl.java | 650 +++--- .../qpid/server/AMQBrokerManagerMBeanTest.java | 3 +- .../org/apache/qpid/server/AMQChannelTest.java | 52 + .../server/configuration/MockConnectionConfig.java | 20 + .../VirtualHostConfigurationTest.java | 3 +- .../exchange/AbstractHeadersExchangeTestBase.java | 57 +- .../qpid/server/exchange/ExchangeMBeanTest.java | 13 +- .../qpid/server/exchange/TopicExchangeTest.java | 2 +- .../logging/messages/MessageStoreMessagesTest.java | 58 +- .../subjects/MessageStoreLogSubjectTest.java | 4 +- .../server/plugins/OsgiSystemPackageUtilTest.java | 4 +- .../protocol/AMQProtocolSessionMBeanTest.java | 4 +- .../protocol/InternalTestProtocolSession.java | 5 +- .../MultiVersionProtocolEngineFactoryTest.java | 18 +- .../qpid/server/queue/AMQQueueAlertTest.java | 39 +- .../qpid/server/queue/AMQQueueFactoryTest.java | 5 +- .../qpid/server/queue/AMQQueueMBeanTest.java | 19 +- .../java/org/apache/qpid/server/queue/AckTest.java | 13 +- .../org/apache/qpid/server/queue/MockAMQQueue.java | 5 +- .../qpid/server/queue/MockStoredMessage.java | 6 +- .../qpid/server/queue/PriorityQueueListTest.java | 117 + .../qpid/server/queue/QueueEntryListTestBase.java | 33 + .../qpid/server/queue/SimpleAMQQueueTest.java | 59 +- .../server/queue/SimpleQueueEntryImplTest.java | 20 + .../server/queue/SimpleQueueEntryListTest.java | 19 +- .../server/queue/SortedQueueEntryListTest.java | 16 +- .../registry/ApplicationRegistryShutdownTest.java | 5 +- .../auth/rmi/RMIPasswordAuthenticatorTest.java | 30 +- .../security/auth/sasl/SaslServerTestCase.java | 2 +- .../store/DurableConfigurationStoreTest.java | 377 +++ .../apache/qpid/server/store/EventManagerTest.java | 72 + .../apache/qpid/server/store/MessageStoreTest.java | 16 +- .../store/OperationalLoggingListenerTest.java | 181 ++ .../qpid/server/store/ReferenceCountingTest.java | 4 +- .../qpid/server/store/SkeletonMessageStore.java | 157 -- .../apache/qpid/server/store/StateManagerTest.java | 195 ++ .../qpid/server/store/TestMemoryMessageStore.java | 98 - .../server/store/TestableMemoryMessageStore.java | 57 +- .../store/TestableMemoryMessageStoreFactory.java | 37 + .../qpid/server/subscription/MockSubscription.java | 4 + .../qpid/server/transport/ServerSessionTest.java | 67 + .../qpid/server/txn/MockStoreTransaction.java | 54 +- .../qpid/server/util/InternalBrokerBaseCase.java | 7 +- .../qpid/server/util/MapJsonSerializerTest.java | 53 + .../qpid/server/virtualhost/MockVirtualHost.java | 26 +- .../server/virtualhost/VirtualHostImplTest.java | 84 +- 206 files changed, 14049 insertions(+), 4528 deletions(-) create mode 100755 java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData_1_0.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/model/Binding.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/model/ConfigurationChangeListener.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/model/Consumer.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/model/Exchange.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/model/IllegalStateTransitionException.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/model/LifetimePolicy.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/model/Publisher.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/model/Queue.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/model/State.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/model/Statistics.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java create mode 100755 java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0_SASL.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Connection_1_0.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Destination.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ExchangeDestination.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/LinkRegistry.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Link_1_0.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Message_1_0.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/QueueDestination.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingDestination.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingLinkAttachment.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingLink_1_0.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingDestination.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLinkAttachment.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLink_1_0.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Session_1_0.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/TxnCoordinatorLink_1_0.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/UnsettledAction.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryVisitor.java delete mode 100644 java/broker/src/main/java/org/apache/qpid/server/store/AbstractMessageStore.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/store/ConfiguredObjectHelper.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecord.java delete mode 100644 java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/store/Event.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/store/EventListener.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/store/EventManager.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStoreFactory.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/store/MessageStoreConstants.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/store/MessageStoreFactory.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/store/NullMessageStore.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/store/OperationalLoggingListener.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/store/State.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/store/StateManager.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/store/StoreFuture.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/store/Transaction.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStore.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStoreFactory.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/txn/AlreadyKnownDtxException.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/txn/DistributedTransaction.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/txn/DtxBranch.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/txn/DtxException.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/txn/DtxNotSelectedException.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/txn/DtxRegistry.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/txn/IncorrectDtxStateException.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/txn/JoinAndResumeDtxException.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/txn/NotAssociatedDtxException.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/txn/RollbackOnlyDtxException.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/txn/SuspendAndFailDtxException.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/txn/TimeoutDtxException.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/txn/UnknownDtxBranchException.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/util/MapJsonSerializer.java create mode 100644 java/broker/src/main/java/org/apache/qpid/server/virtualhost/State.java create mode 100644 java/broker/src/test/java/org/apache/qpid/server/AMQChannelTest.java create mode 100644 java/broker/src/test/java/org/apache/qpid/server/queue/PriorityQueueListTest.java create mode 100644 java/broker/src/test/java/org/apache/qpid/server/store/DurableConfigurationStoreTest.java create mode 100644 java/broker/src/test/java/org/apache/qpid/server/store/EventManagerTest.java create mode 100644 java/broker/src/test/java/org/apache/qpid/server/store/OperationalLoggingListenerTest.java delete mode 100644 java/broker/src/test/java/org/apache/qpid/server/store/SkeletonMessageStore.java create mode 100644 java/broker/src/test/java/org/apache/qpid/server/store/StateManagerTest.java delete mode 100644 java/broker/src/test/java/org/apache/qpid/server/store/TestMemoryMessageStore.java create mode 100644 java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStoreFactory.java create mode 100644 java/broker/src/test/java/org/apache/qpid/server/transport/ServerSessionTest.java create mode 100644 java/broker/src/test/java/org/apache/qpid/server/util/MapJsonSerializerTest.java (limited to 'java/broker/src') diff --git a/java/broker/src/main/java/broker.bnd b/java/broker/src/main/java/broker.bnd index 25b0495a63..fa433848a6 100755 --- a/java/broker/src/main/java/broker.bnd +++ b/java/broker/src/main/java/broker.bnd @@ -17,7 +17,7 @@ # under the License. # -ver: 0.15.0 +ver: 0.17.0 Bundle-SymbolicName: qpid-broker Bundle-Version: ${ver} diff --git a/java/broker/src/main/java/org/apache/qpid/qmf/ManagementExchange.java b/java/broker/src/main/java/org/apache/qpid/qmf/ManagementExchange.java index 1a098d0446..034a4ae53c 100644 --- a/java/broker/src/main/java/org/apache/qpid/qmf/ManagementExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/qmf/ManagementExchange.java @@ -37,6 +37,7 @@ import org.apache.qpid.server.exchange.topic.TopicNormalizer; import org.apache.qpid.server.exchange.topic.TopicParser; import org.apache.qpid.server.message.InboundMessage; import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.virtualhost.HouseKeepingTask; @@ -83,7 +84,8 @@ public class ManagementExchange implements Exchange, QMFService.Listener private class ManagementQueue implements BaseQueue { - private final String NAME_AS_STRING = "##__mgmt_pseudo_queue__##" + UUID.randomUUID().toString(); + private final UUID QUEUE_ID = UUIDGenerator.generateUUID(); + private final String NAME_AS_STRING = "##__mgmt_pseudo_queue__##" + QUEUE_ID.toString(); private final AMQShortString NAME_AS_SHORT_STRING = new AMQShortString(NAME_AS_STRING); public void enqueue(ServerMessage message) throws AMQException @@ -129,9 +131,10 @@ public class ManagementExchange implements Exchange, QMFService.Listener return NAME_AS_SHORT_STRING; } - public String getResourceName() + @Override + public UUID getId() { - return NAME_AS_STRING; + return QUEUE_ID; } } @@ -155,14 +158,14 @@ public class ManagementExchange implements Exchange, QMFService.Listener return ManagementExchange.class; } - public ManagementExchange newInstance(VirtualHost host, + public ManagementExchange newInstance(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException { ManagementExchange exch = new ManagementExchange(); - exch.initialise(host, name, durable, ticket, autoDelete); + exch.initialise(id, host, name, durable, ticket, autoDelete); return exch; } @@ -183,7 +186,7 @@ public class ManagementExchange implements Exchange, QMFService.Listener return QPID_MANAGEMENT_TYPE; } - public void initialise(VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) + public void initialise(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException { if(!QPID_MANAGEMENT.equals(name)) @@ -191,7 +194,7 @@ public class ManagementExchange implements Exchange, QMFService.Listener throw new AMQException("Can't create more than one Management exchange"); } _virtualHost = host; - _id = host.getConfigStore().createId(); + _id = id; _virtualHost.scheduleHouseKeepingTask(_virtualHost.getBroker().getManagementPublishInterval(), new UpdateTask(_virtualHost)); getConfigStore().addConfiguredObject(this); getQMFService().addListener(this); @@ -410,6 +413,11 @@ public class ManagementExchange implements Exchange, QMFService.Listener return queues; } + public boolean isBound(String bindingKey, Map arguments, AMQQueue queue) + { + return false; //TODO + } + public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) { return false; //To change body of implemented methods use File | Settings | File Templates. diff --git a/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java b/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java index 4367a04da9..900b722886 100644 --- a/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java +++ b/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java @@ -658,6 +658,11 @@ public class QMFService implements ConfigStore.ConfigEventListener, Closeable return _obj.getStagingThreshold(); } + public Boolean getMgmtPublish() + { + return true; + } + public Integer getMgmtPubInterval() { return _obj.getManagementPublishInterval(); diff --git a/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java b/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java index 265aa7714e..0f32b98aa8 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java +++ b/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java @@ -33,11 +33,11 @@ import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.actors.ManagementActor; import org.apache.qpid.server.management.AMQManagedObject; import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.queue.AMQQueueMBean; import org.apache.qpid.server.queue.QueueRegistry; -import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostImpl; @@ -49,6 +49,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.UUID; /** * This MBean implements the broker management interface and exposes the @@ -60,8 +61,6 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr private final QueueRegistry _queueRegistry; private final ExchangeRegistry _exchangeRegistry; private final ExchangeFactory _exchangeFactory; - private final Exchange _defaultExchange; - private final DurableConfigurationStore _durableConfig; private final VirtualHostImpl.VirtualHostMBean _virtualHostMBean; @@ -75,8 +74,6 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr _queueRegistry = virtualHost.getQueueRegistry(); _exchangeRegistry = virtualHost.getExchangeRegistry(); - _defaultExchange = _exchangeRegistry.getDefaultExchange(); - _durableConfig = virtualHost.getDurableConfigurationStore(); _exchangeFactory = virtualHost.getExchangeFactory(); } @@ -176,12 +173,12 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr Exchange exchange = _exchangeRegistry.getExchange(new AMQShortString(exchangeName)); if (exchange == null) { - exchange = _exchangeFactory.createExchange(new AMQShortString(exchangeName), new AMQShortString(type), - durable, false, 0); + exchange = _exchangeFactory.createExchange(new AMQShortString(exchangeName), + new AMQShortString(type), durable, false, 0); _exchangeRegistry.registerExchange(exchange); if (durable) { - _durableConfig.createExchange(exchange); + getVirtualHost().getMessageStore().createExchange(exchange); } } else @@ -249,45 +246,42 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr public void createNewQueue(String queueName, String owner, boolean durable, Map arguments) throws JMException { final AMQShortString queueNameAsAMQShortString = new AMQShortString(queueName); - AMQQueue queue = _queueRegistry.getQueue(queueNameAsAMQShortString); - if (queue != null) + synchronized (_queueRegistry) { - throw new JMException("The queue \"" + queueName + "\" already exists."); - } - - CurrentActor.set(new ManagementActor(getLogActor().getRootMessageLogger())); - try - { - AMQShortString ownerShortString = null; - if (owner != null) + AMQQueue queue = _queueRegistry.getQueue(queueNameAsAMQShortString); + if (queue != null) { - ownerShortString = new AMQShortString(owner); + throw new JMException("The queue \"" + queueName + "\" already exists."); } - FieldTable args = null; - if(arguments != null) + CurrentActor.set(new ManagementActor(getLogActor().getRootMessageLogger())); + try { - args = FieldTable.convertToFieldTable(arguments); - } - final VirtualHost virtualHost = getVirtualHost(); + FieldTable args = null; + if(arguments != null) + { + args = FieldTable.convertToFieldTable(arguments); + } + final VirtualHost virtualHost = getVirtualHost(); + + queue = AMQQueueFactory.createAMQQueueImpl(UUIDGenerator.generateUUID(), queueName, durable, owner, + false, false, getVirtualHost(), arguments); + if (queue.isDurable() && !queue.isAutoDelete()) + { + getVirtualHost().getMessageStore().createQueue(queue, args); + } - queue = AMQQueueFactory.createAMQQueueImpl(queueNameAsAMQShortString, durable, ownerShortString, - false, false, getVirtualHost(), args); - if (queue.isDurable() && !queue.isAutoDelete()) + virtualHost.getBindingFactory().addBinding(queueName, queue, _exchangeRegistry.getDefaultExchange(), null); + } + catch (AMQException ex) { - _durableConfig.createQueue(queue, args); + JMException jme = new JMException(ex.toString()); + throw new MBeanException(jme, "Error in creating queue " + queueName); + } + finally + { + CurrentActor.remove(); } - - virtualHost.getBindingFactory().addBinding(queueName, queue, _defaultExchange, null); - } - catch (AMQException ex) - { - JMException jme = new JMException(ex.toString()); - throw new MBeanException(jme, "Error in creating queue " + queueName); - } - finally - { - CurrentActor.remove(); } } @@ -317,7 +311,7 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr queue.delete(); if (queue.isDurable()) { - _durableConfig.removeQueue(queue); + getVirtualHost().getMessageStore().removeQueue(queue); } } catch (AMQException ex) diff --git a/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java b/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java index e5e755bd23..8198cec821 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java +++ b/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -82,11 +83,13 @@ import org.apache.qpid.server.queue.IncomingMessage; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.registry.ApplicationRegistry; 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.subscription.SubscriptionFactoryImpl; +import org.apache.qpid.server.subscription.SubscriptionImpl; import org.apache.qpid.server.txn.AsyncAutoCommitTransaction; import org.apache.qpid.server.txn.LocalTransaction; import org.apache.qpid.server.txn.ServerTransaction; @@ -273,7 +276,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm { throw new AMQSecurityException("Permission denied: " + e.getName()); } - _currentMessage = new IncomingMessage(info); + _currentMessage = new IncomingMessage(info, getProtocolSession().getReference()); _currentMessage.setExchange(e); } @@ -295,26 +298,9 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm _currentMessage.setExpiration(); + _currentMessage.headersReceived(getProtocolSession().getLastReceivedTime()); - MessageMetaData mmd = _currentMessage.headersReceived(getProtocolSession().getLastReceivedTime()); - final StoredMessage handle = _messageStore.addMessage(mmd); - _currentMessage.setStoredMessage(handle); - - routeCurrentMessage(); - - - _transaction.addPostTransactionAction(new ServerTransaction.Action() - { - - public void postCommit() - { - } - - public void onRollback() - { - handle.remove(); - } - }); + _currentMessage.route(); deliverCurrentMessageIfComplete(); } @@ -346,17 +332,41 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm { _actor.message(ExchangeMessages.DISCARDMSG(_currentMessage.getExchange().asString(), _currentMessage.getRoutingKey())); } - } else { + final StoredMessage handle = _messageStore.addMessage(_currentMessage.getMessageMetaData()); + _currentMessage.setStoredMessage(handle); + int bodyCount = _currentMessage.getBodyCount(); + if(bodyCount > 0) + { + long bodyLengthReceived = 0; + for(int i = 0 ; i < bodyCount ; i++) + { + ContentChunk contentChunk = _currentMessage.getContentChunk(i); + handle.addContent((int)bodyLengthReceived, ByteBuffer.wrap(contentChunk.getData())); + bodyLengthReceived += contentChunk.getSize(); + } + } + + _transaction.addPostTransactionAction(new ServerTransaction.Action() + { + public void postCommit() + { + } + + public void onRollback() + { + handle.remove(); + } + }); + _transaction.enqueue(destinationQueues, _currentMessage, new MessageDeliveryAction(_currentMessage, destinationQueues), getProtocolSession().getLastReceivedTime()); incrementOutstandingTxnsIfNecessary(); - updateTransactionalActivity(); + updateTransactionalActivity(); + _currentMessage.getStoredMessage().flushToStore(); } } - _currentMessage.getStoredMessage().flushToStore(); - } finally { @@ -383,9 +393,6 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm try { - - // returns true iff the message was delivered (i.e. if all data was - // received final ContentChunk contentChunk = _session.getMethodRegistry().getProtocolVersionMethodConverter().convertToContentChunk(contentBody); @@ -409,11 +416,6 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm } } - protected void routeCurrentMessage() throws AMQException - { - _currentMessage.route(); - } - public long getNextDeliveryTag() { return ++_deliveryTag; @@ -1123,11 +1125,6 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm } - public Object getID() - { - return _channelId; - } - public AMQConnectionModel getConnectionModel() { return _session; @@ -1377,7 +1374,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm { if(_blockingQueues.remove(queue)) { - if(_blocking.compareAndSet(true,false)) + if(_blocking.compareAndSet(true,false) && !isClosing()) { _actor.message(_logSubject, ChannelMessages.FLOW_REMOVED()); @@ -1386,6 +1383,16 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm } } + public boolean onSameConnection(InboundMessage inbound) + { + if(inbound instanceof IncomingMessage) + { + IncomingMessage incoming = (IncomingMessage) inbound; + return getProtocolSession().getReference() == incoming.getConnectionReference(); + } + return false; + } + private void flow(boolean flow) { MethodRegistry methodRegistry = _session.getMethodRegistry(); @@ -1551,7 +1558,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm } } - public void recordFuture(final MessageStore.StoreFuture future, final ServerTransaction.Action action) + public void recordFuture(final StoreFuture future, final ServerTransaction.Action action) { _unfinishedCommandsQueue.add(new AsyncCommand(future, action)); } @@ -1585,10 +1592,10 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm private static class AsyncCommand { - private final MessageStore.StoreFuture _future; + private final StoreFuture _future; private ServerTransaction.Action _action; - public AsyncCommand(final MessageStore.StoreFuture future, final ServerTransaction.Action action) + public AsyncCommand(final StoreFuture future, final ServerTransaction.Action action) { _future = future; _action = action; @@ -1615,9 +1622,8 @@ public class AMQChannel implements SessionConfig, AMQSessionModel, AsyncAutoComm } } - @Override public int compareTo(AMQSessionModel session) { - return getId().toString().compareTo(session.getID().toString()); + return getId().compareTo(session.getId()); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/Broker.java b/java/broker/src/main/java/org/apache/qpid/server/Broker.java index 072f8dc132..5004d320c2 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/Broker.java +++ b/java/broker/src/main/java/org/apache/qpid/server/Broker.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server; +import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.xml.QpidLog4JConfigurator; @@ -52,15 +53,24 @@ import java.io.InputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.EnumSet; +import java.util.Formatter; import java.util.HashSet; import java.util.List; import java.util.Properties; import java.util.Set; +import java.util.logging.ConsoleHandler; +import java.util.logging.FileHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; public class Broker { + private static final Logger LOGGER = Logger.getLogger(Broker.class); + private static final int IPV4_ADDRESS_LENGTH = 4; private static final char IPV4_LITERAL_SEPARATOR = '.'; + private volatile Thread _shutdownHookThread; protected static class InitException extends RuntimeException { @@ -74,7 +84,14 @@ public class Broker public void shutdown() { - ApplicationRegistry.remove(); + try + { + removeShutdownHook(); + } + finally + { + ApplicationRegistry.remove(); + } } public void startup() throws Exception @@ -88,6 +105,7 @@ public class Broker { CurrentActor.set(new BrokerActor(new SystemOutMessageLogger())); startupImpl(options); + addShutdownHook(); } finally { @@ -147,6 +165,12 @@ public class Broker parsePortList(sslPorts, serverConfig.getSSLPorts()); } + Set exclude_1_0 = new HashSet(options.getExcludedPorts(ProtocolExclusion.v1_0)); + if(exclude_1_0.isEmpty()) + { + parsePortList(exclude_1_0, serverConfig.getPortExclude10()); + } + Set exclude_0_10 = new HashSet(options.getExcludedPorts(ProtocolExclusion.v0_10)); if(exclude_0_10.isEmpty()) { @@ -177,14 +201,14 @@ public class Broker bindAddr = serverConfig.getBind(); } - InetAddress bindAddress = null; + InetAddress bindAddress; if (bindAddr.equals(WILDCARD_ADDRESS)) { - bindAddress = new InetSocketAddress(0).getAddress(); + bindAddress = null; } else { - bindAddress = InetAddress.getByAddress(parseIP(bindAddr)); + bindAddress = InetAddress.getByName(bindAddr); } final AmqpProtocolVersion defaultSupportedProtocolReply = serverConfig.getDefaultSupportedProtocolReply(); @@ -193,18 +217,22 @@ public class Broker { for(int port : ports) { + final InetSocketAddress inetSocketAddress = new InetSocketAddress(bindAddress, port); + final Set supported = - getSupportedVersions(port, exclude_0_10, exclude_0_9_1, exclude_0_9, exclude_0_8, serverConfig); + getSupportedVersions(port, exclude_1_0, exclude_0_10, exclude_0_9_1, exclude_0_9, + exclude_0_8, serverConfig); final NetworkTransportConfiguration settings = - new ServerNetworkTransportConfiguration(serverConfig, port, bindAddress.getHostName(), Transport.TCP); + new ServerNetworkTransportConfiguration(serverConfig, inetSocketAddress, Transport.TCP); final IncomingNetworkTransport transport = Transport.getIncomingTransportInstance(); final MultiVersionProtocolEngineFactory protocolEngineFactory = new MultiVersionProtocolEngineFactory(supported, defaultSupportedProtocolReply); transport.accept(settings, protocolEngineFactory, null); - ApplicationRegistry.getInstance().addAcceptor(new InetSocketAddress(bindAddress, port), + + ApplicationRegistry.getInstance().addAcceptor(inetSocketAddress, new QpidAcceptor(transport,"TCP")); CurrentActor.get().message(BrokerMessages.LISTENING("TCP", port)); } @@ -219,17 +247,21 @@ public class Broker for(int sslPort : sslPorts) { + final InetSocketAddress inetSocketAddress = new InetSocketAddress(bindAddress, sslPort); + final Set supported = - getSupportedVersions(sslPort, exclude_0_10, exclude_0_9_1, exclude_0_9, exclude_0_8, serverConfig); + getSupportedVersions(sslPort, exclude_1_0, exclude_0_10, exclude_0_9_1, + exclude_0_9, exclude_0_8, serverConfig); final NetworkTransportConfiguration settings = - new ServerNetworkTransportConfiguration(serverConfig, sslPort, bindAddress.getHostName(), Transport.TCP); + new ServerNetworkTransportConfiguration(serverConfig, inetSocketAddress, Transport.TCP); final IncomingNetworkTransport transport = Transport.getIncomingTransportInstance(); final MultiVersionProtocolEngineFactory protocolEngineFactory = new MultiVersionProtocolEngineFactory(supported, defaultSupportedProtocolReply); transport.accept(settings, protocolEngineFactory, sslContext); - ApplicationRegistry.getInstance().addAcceptor(new InetSocketAddress(bindAddress, sslPort), + + ApplicationRegistry.getInstance().addAcceptor(inetSocketAddress, new QpidAcceptor(transport,"TCP")); CurrentActor.get().message(BrokerMessages.LISTENING("TCP/SSL", sslPort)); } @@ -244,13 +276,20 @@ public class Broker } } - private static Set getSupportedVersions(final int port, final Set exclude_0_10, - final Set exclude_0_9_1, final Set exclude_0_9, - final Set exclude_0_8, - final ServerConfiguration serverConfig) + private static Set getSupportedVersions(final int port, + final Set exclude_1_0, + final Set exclude_0_10, + final Set exclude_0_9_1, + final Set exclude_0_9, + final Set exclude_0_8, + final ServerConfiguration serverConfig) { final EnumSet supported = EnumSet.allOf(AmqpProtocolVersion.class); + if(exclude_1_0.contains(port) || !serverConfig.isAmqp10enabled()) + { + supported.remove(AmqpProtocolVersion.v1_0_0); + } if(exclude_0_10.contains(port) || !serverConfig.isAmqp010enabled()) { supported.remove(AmqpProtocolVersion.v0_10); @@ -349,34 +388,6 @@ public class Broker } } - private byte[] parseIP(String address) throws Exception - { - char[] literalBuffer = address.toCharArray(); - int byteCount = 0; - int currByte = 0; - byte[] ip = new byte[IPV4_ADDRESS_LENGTH]; - for (int i = 0; i < literalBuffer.length; i++) - { - char currChar = literalBuffer[i]; - if ((currChar >= '0') && (currChar <= '9')) - { - currByte = (currByte * 10) + (Character.digit(currChar, 10) & 0xFF); - } - - if (currChar == IPV4_LITERAL_SEPARATOR || (i + 1 == literalBuffer.length)) - { - ip[byteCount++] = (byte) currByte; - currByte = 0; - } - } - - if (byteCount != 4) - { - throw new Exception("Invalid IP address: " + address); - } - return ip; - } - private void configureLogging(File logConfigFile, long logWatchTime) throws InitException, IOException { if (logConfigFile.exists() && logConfigFile.canRead()) @@ -441,4 +452,56 @@ public class Broker blm.register(); } + + private void addShutdownHook() + { + Thread shutdownHookThread = new Thread(new ShutdownService()); + shutdownHookThread.setName("QpidBrokerShutdownHook"); + + Runtime.getRuntime().addShutdownHook(shutdownHookThread); + _shutdownHookThread = shutdownHookThread; + + LOGGER.debug("Added shutdown hook"); + } + + private void removeShutdownHook() + { + Thread shutdownThread = _shutdownHookThread; + + //if there is a shutdown thread and we aren't it, we should remove it + if(shutdownThread != null && !(Thread.currentThread() == shutdownThread)) + { + LOGGER.debug("Removing shutdown hook"); + + _shutdownHookThread = null; + + boolean removed = false; + try + { + removed = Runtime.getRuntime().removeShutdownHook(shutdownThread); + } + catch(IllegalStateException ise) + { + //ignore, means the JVM is already shutting down + } + + if(LOGGER.isDebugEnabled()) + { + LOGGER.debug("Removed shutdown hook: " + removed); + } + } + else + { + LOGGER.debug("Skipping shutdown hook removal as there either isnt one, or we are it."); + } + } + + private class ShutdownService implements Runnable + { + public void run() + { + LOGGER.debug("Shutdown hook running"); + Broker.this.shutdown(); + } + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java b/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java index 1f96a24701..d871c724fd 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java +++ b/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java @@ -47,7 +47,6 @@ public class BrokerOptions private Integer _logWatchFrequency = 0; - public void addPort(final int port) { _ports.add(port); @@ -107,7 +106,6 @@ public class BrokerOptions { _jmxPortConnectorServer = jmxPortConnectorServer; } - public String getQpidHome() { return System.getProperty(QPID_HOME); @@ -163,5 +161,4 @@ public class BrokerOptions { _bundleContext = bundleContext; } - } \ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/Main.java b/java/broker/src/main/java/org/apache/qpid/server/Main.java index 5fcd8a7b52..70fa414e3c 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/Main.java +++ b/java/broker/src/main/java/org/apache/qpid/server/Main.java @@ -59,6 +59,12 @@ public class Main .withDescription("SSL port. Overrides any value in the config file") .withLongOpt("sslport").create("s"); + + private static final Option OPTION_EXCLUDE_1_0 = + OptionBuilder.withArgName("port").hasArg() + .withDescription("when listening on the specified port do not accept AMQP1-0 connections. The specified port must be one specified on the command line") + .withLongOpt("exclude-1-0").create(); + private static final Option OPTION_EXCLUDE_0_10 = OptionBuilder.withArgName("port").hasArg() .withDescription("when listening on the specified port do not accept AMQP0-10 connections. The specified port must be one specified on the command line") @@ -116,6 +122,7 @@ public class Main OPTIONS.addOption(OPTION_LOG_WATCH); OPTIONS.addOption(OPTION_PORT); OPTIONS.addOption(OPTION_SSLPORT); + OPTIONS.addOption(OPTION_EXCLUDE_1_0); OPTIONS.addOption(OPTION_EXCLUDE_0_10); OPTIONS.addOption(OPTION_EXCLUDE_0_9_1); OPTIONS.addOption(OPTION_EXCLUDE_0_9); diff --git a/java/broker/src/main/java/org/apache/qpid/server/ProtocolExclusion.java b/java/broker/src/main/java/org/apache/qpid/server/ProtocolExclusion.java index 22d97d36dd..fe6e32173f 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/ProtocolExclusion.java +++ b/java/broker/src/main/java/org/apache/qpid/server/ProtocolExclusion.java @@ -28,7 +28,8 @@ public enum ProtocolExclusion v0_8("exclude-0-8","--exclude-0-8"), v0_9("exclude-0-9", "--exclude-0-9"), v0_9_1("exclude-0-9-1", "--exclude-0-9-1"), - v0_10("exclude-0-10", "--exclude-0-10"); + v0_10("exclude-0-10", "--exclude-0-10"), + v1_0("exclude-1-0", "--exclude-1-0"); private static final Map MAP = new HashMap(); diff --git a/java/broker/src/main/java/org/apache/qpid/server/binding/Binding.java b/java/broker/src/main/java/org/apache/qpid/server/binding/Binding.java index 8e44da095a..2efd4cee26 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/binding/Binding.java +++ b/java/broker/src/main/java/org/apache/qpid/server/binding/Binding.java @@ -117,7 +117,7 @@ public class Binding public String toString() { - return "Binding{bindingKey="+_bindingKey+", exchange="+_exchange+", queue="+_queue+"}"; + return "Binding{bindingKey="+_bindingKey+", exchange="+_exchange+", queue="+_queue+", id= " + _id + " }"; } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java b/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java index fe66a6d341..abf252c733 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/binding/BindingFactory.java @@ -24,7 +24,6 @@ import org.apache.qpid.AMQException; import org.apache.qpid.AMQInternalException; import org.apache.qpid.AMQSecurityException; import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.configuration.BindingConfig; import org.apache.qpid.server.configuration.BindingConfigType; import org.apache.qpid.server.configuration.ConfigStore; @@ -33,58 +32,35 @@ import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.messages.BindingMessages; import org.apache.qpid.server.logging.subjects.BindingLogSubject; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.queue.AMQQueue; -import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Collections; import java.util.Map; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; public class BindingFactory { private final VirtualHost _virtualHost; - private final DurableConfigurationStore.Source _configSource; - private final Exchange _defaultExchange; private final ConcurrentHashMap _bindings = new ConcurrentHashMap(); - public BindingFactory(final VirtualHost vhost) { - this(vhost, vhost.getExchangeRegistry().getDefaultExchange()); + _virtualHost = vhost; } - public BindingFactory(final DurableConfigurationStore.Source configSource, final Exchange defaultExchange) - { - _configSource = configSource; - _defaultExchange = defaultExchange; - if (configSource instanceof VirtualHost) - { - _virtualHost = (VirtualHost) configSource; - } - else - { - _virtualHost = null; - } - } - - public VirtualHost getVirtualHost() - { - return _virtualHost; - } - - - private final class BindingImpl extends Binding implements AMQQueue.Task, Exchange.Task, BindingConfig { private final BindingLogSubject _logSubject; //TODO : persist creation time private long _createTime = System.currentTimeMillis(); - private BindingImpl(String bindingKey, final AMQQueue queue, final Exchange exchange, final Map arguments) + private BindingImpl(UUID id, String bindingKey, final AMQQueue queue, final Exchange exchange, final Map arguments) { - super(queue.getVirtualHost().getConfigStore().createId(), bindingKey, queue, exchange, arguments); + super(id, bindingKey, queue, exchange, arguments); _logSubject = new BindingLogSubject(bindingKey,exchange,queue); } @@ -141,48 +117,59 @@ public class BindingFactory public boolean addBinding(String bindingKey, AMQQueue queue, Exchange exchange, Map arguments) throws AMQSecurityException, AMQInternalException { - return makeBinding(bindingKey, queue, exchange, arguments, false, false); + return makeBinding(null, bindingKey, queue, exchange, arguments, false, false); } - public boolean replaceBinding(final String bindingKey, + public boolean replaceBinding(final UUID id, final String bindingKey, final AMQQueue queue, final Exchange exchange, final Map arguments) throws AMQSecurityException, AMQInternalException { - return makeBinding(bindingKey, queue, exchange, arguments, false, true); + return makeBinding(id, bindingKey, queue, exchange, arguments, false, true); } - private boolean makeBinding(String bindingKey, AMQQueue queue, Exchange exchange, Map arguments, boolean restore, boolean force) throws AMQSecurityException, AMQInternalException + private boolean makeBinding(UUID id, String bindingKey, AMQQueue queue, Exchange exchange, Map arguments, boolean restore, boolean force) throws AMQSecurityException, AMQInternalException { assert queue != null; + final Exchange defaultExchange = _virtualHost.getExchangeRegistry().getDefaultExchange(); + if (bindingKey == null) { bindingKey = ""; } if (exchange == null) { - exchange = _defaultExchange; + exchange = defaultExchange; } if (arguments == null) { arguments = Collections.emptyMap(); } + if (exchange == null) + { + throw new IllegalArgumentException("exchange cannot be null"); + } + // The default exchange bindings must reflect the existence of queues, allow // all operations on it to succeed. It is up to the broker to prevent illegal // attempts at binding to this exchange, not the ACLs. - if(exchange != _defaultExchange) + if(exchange != defaultExchange) { //Perform ACLs - if (!getVirtualHost().getSecurityManager().authoriseBind(exchange, queue, new AMQShortString(bindingKey))) + if (!_virtualHost.getSecurityManager().authoriseBind(exchange, queue, new AMQShortString(bindingKey))) { throw new AMQSecurityException("Permission denied: binding " + bindingKey); } } - - BindingImpl b = new BindingImpl(bindingKey,queue,exchange,arguments); - BindingImpl existingMapping = _bindings.putIfAbsent(b,b); + + if (id == null) + { + id = UUIDGenerator.generateUUID(); + } + BindingImpl b = new BindingImpl(id, bindingKey, queue, exchange, arguments); + BindingImpl existingMapping = _bindings.putIfAbsent(b, b); if (existingMapping == null || force) { if (existingMapping != null) @@ -192,7 +179,7 @@ public class BindingFactory if (b.isDurable() && !restore) { - _configSource.getDurableConfigurationStore().bindQueue(exchange,new AMQShortString(bindingKey),queue,FieldTable.convertToFieldTable(arguments)); + _virtualHost.getMessageStore().bindQueue(b); } queue.addQueueDeleteTask(b); @@ -212,12 +199,12 @@ public class BindingFactory private ConfigStore getConfigStore() { - return getVirtualHost().getConfigStore(); + return _virtualHost.getConfigStore(); } - public void restoreBinding(final String bindingKey, final AMQQueue queue, final Exchange exchange, final Map argumentMap) throws AMQSecurityException, AMQInternalException + public void restoreBinding(final UUID id, final String bindingKey, final AMQQueue queue, final Exchange exchange, final Map argumentMap) throws AMQSecurityException, AMQInternalException { - makeBinding(bindingKey,queue,exchange,argumentMap,true, false); + makeBinding(id, bindingKey,queue,exchange,argumentMap,true, false); } public void removeBinding(final Binding b) throws AMQSecurityException, AMQInternalException @@ -229,13 +216,15 @@ public class BindingFactory public Binding removeBinding(String bindingKey, AMQQueue queue, Exchange exchange, Map arguments) throws AMQSecurityException, AMQInternalException { assert queue != null; + final Exchange defaultExchange = _virtualHost.getExchangeRegistry().getDefaultExchange(); + if (bindingKey == null) { bindingKey = ""; } if (exchange == null) { - exchange = _defaultExchange; + exchange = defaultExchange; } if (arguments == null) { @@ -245,16 +234,16 @@ public class BindingFactory // The default exchange bindings must reflect the existence of queues, allow // all operations on it to succeed. It is up to the broker to prevent illegal // attempts at binding to this exchange, not the ACLs. - if(exchange != _defaultExchange) + if(exchange != defaultExchange) { // Check access - if (!getVirtualHost().getSecurityManager().authoriseUnbind(exchange, new AMQShortString(bindingKey), queue)) + if (!_virtualHost.getSecurityManager().authoriseUnbind(exchange, new AMQShortString(bindingKey), queue)) { throw new AMQSecurityException("Permission denied: unbinding " + bindingKey); } } - BindingImpl b = _bindings.remove(new BindingImpl(bindingKey,queue,exchange,arguments)); + BindingImpl b = _bindings.remove(new BindingImpl(null, bindingKey,queue,exchange,arguments)); if (b != null) { @@ -265,10 +254,7 @@ public class BindingFactory if (b.isDurable()) { - _configSource.getDurableConfigurationStore().unbindQueue(exchange, - new AMQShortString(bindingKey), - queue, - FieldTable.convertToFieldTable(arguments)); + _virtualHost.getMessageStore().unbindQueue(b); } b.logDestruction(); getConfigStore().removeConfiguredObject(b); @@ -280,20 +266,22 @@ public class BindingFactory public Binding getBinding(String bindingKey, AMQQueue queue, Exchange exchange, Map arguments) { assert queue != null; + final Exchange defaultExchange = _virtualHost.getExchangeRegistry().getDefaultExchange(); + if(bindingKey == null) { bindingKey = ""; } if(exchange == null) { - exchange = _defaultExchange; + exchange = defaultExchange; } if(arguments == null) { arguments = Collections.emptyMap(); } - BindingImpl b = new BindingImpl(bindingKey,queue,exchange,arguments); + BindingImpl b = new BindingImpl(null, bindingKey,queue,exchange,arguments); return _bindings.get(b); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java b/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java index 259902a938..46027d02c6 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java +++ b/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java @@ -88,6 +88,7 @@ public class ServerConfiguration extends ConfigurationPlugin public static final String MGMT_JMXPORT_CONNECTORSERVER = "management.jmxport.connectorServer"; public static final String STATUS_UPDATES = "status-updates"; public static final String ADVANCED_LOCALE = "advanced.locale"; + public static final String CONNECTOR_AMQP10ENABLED = "connector.amqp10enabled"; public static final String CONNECTOR_AMQP010ENABLED = "connector.amqp010enabled"; public static final String CONNECTOR_AMQP091ENABLED = "connector.amqp091enabled"; public static final String CONNECTOR_AMQP09ENABLED = "connector.amqp09enabled"; @@ -667,6 +668,11 @@ public class ServerConfiguration extends ConfigurationPlugin return getListValue("connector.port", Collections.singletonList(DEFAULT_PORT)); } + public List getPortExclude10() + { + return getListValue("connector.non10port"); + } + public List getPortExclude010() { return getListValue("connector.non010port"); @@ -737,7 +743,7 @@ public class ServerConfiguration extends ConfigurationPlugin public String getConnectorKeyManagerFactoryAlgorithm() { final String systemFallback = KeyManagerFactory.getDefaultAlgorithm(); - // deprecated, pre-0.15 brokers supported this name. + // deprecated, pre-0.17 brokers supported this name. final String fallback = getStringValue("connector.ssl.certType", systemFallback); return getStringValue("connector.ssl.keyManagerFactoryAlgorithm", fallback); } @@ -843,6 +849,11 @@ public class ServerConfiguration extends ConfigurationPlugin return getConfig().getString("deadLetterQueueSuffix", AMQQueueFactory.DEFAULT_DLQ_NAME_SUFFIX); } + public boolean isAmqp10enabled() + { + return getConfig().getBoolean(CONNECTOR_AMQP10ENABLED, true); + } + public boolean isAmqp010enabled() { return getConfig().getBoolean(CONNECTOR_AMQP010ENABLED, true); diff --git a/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerNetworkTransportConfiguration.java b/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerNetworkTransportConfiguration.java index 81dfcb4465..f6fe47b996 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerNetworkTransportConfiguration.java +++ b/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerNetworkTransportConfiguration.java @@ -19,22 +19,21 @@ */ package org.apache.qpid.server.configuration; +import java.net.InetSocketAddress; import org.apache.qpid.transport.NetworkTransportConfiguration; public class ServerNetworkTransportConfiguration implements NetworkTransportConfiguration { private final ServerConfiguration _serverConfig; - private final int _port; - private final String _host; private final String _transport; + private InetSocketAddress _address; public ServerNetworkTransportConfiguration(final ServerConfiguration serverConfig, - final int port, final String host, + final InetSocketAddress address, final String transport) { _serverConfig = serverConfig; - _port = port; - _host = host; + _address = address; _transport = transport; } @@ -55,12 +54,12 @@ public class ServerNetworkTransportConfiguration implements NetworkTransportConf public Integer getPort() { - return _port; + return _address.getPort(); } public String getHost() { - return _host; + return _address.getHostName(); } public String getTransport() @@ -72,4 +71,9 @@ public class ServerNetworkTransportConfiguration implements NetworkTransportConf { return _serverConfig.getConnectorProcessors(); } + + public InetSocketAddress getAddress() + { + return _address; + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfig.java b/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfig.java index 9256724c56..b96ddc56c6 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfig.java +++ b/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfig.java @@ -29,6 +29,4 @@ public interface VirtualHostConfig extends ConfiguredObject _queues = new HashMap(); - private Map _exchanges = new HashMap(); + private final String _name; + private final Map _queues = new HashMap(); + private final Map _exchanges = new HashMap(); public VirtualHostConfiguration(String name, Configuration config) throws ConfigurationException { @@ -92,11 +93,6 @@ public class VirtualHostConfiguration extends ConfigurationPlugin return getLongValue("housekeeping.checkPeriod", ApplicationRegistry.getInstance().getConfiguration().getHousekeepingCheckPeriod()); } - public String getAuthenticationDatabase() - { - return getStringValue("security.authentication.name"); - } - public List getCustomExchanges() { return getListValue("custom-exchanges.class-name"); @@ -107,14 +103,14 @@ public class VirtualHostConfiguration extends ConfigurationPlugin return getConfig().subset("store"); } - public String getMessageStoreClass() + public String getMessageStoreFactoryClass() { - return getStringValue("store.class", MemoryMessageStore.class.getName()); + return getStringValue("store.factoryclass", MemoryMessageStoreFactory.class.getName()); } - public void setMessageStoreClass(String storeClass) + public void setMessageStoreFactoryClass(String storeFactoryClass) { - getConfig().setProperty("store.class", storeClass); + getConfig().setProperty("store.factoryclass", storeFactoryClass); } public List getExchanges() @@ -253,16 +249,6 @@ public class VirtualHostConfiguration extends ConfigurationPlugin return queueConfig; } - public long getMemoryUsageMaximum() - { - return getLongValue("queues.maximumMemoryUsage"); - } - - public long getMemoryUsageMinimum() - { - return getLongValue("queues.minimumMemoryUsage"); - } - public int getMaximumMessageAge() { return getIntValue("queues.maximumMessageAge"); diff --git a/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java index 9159489299..4a58314f51 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java @@ -46,11 +46,19 @@ public class ConnectionRegistry implements IConnectionRegistry, Closeable /** Close all of the currently open connections. */ public void close() { - _logger.debug("Closing connection registry :" + _registry.size() + " connections."); + close(IConnectionRegistry.BROKER_SHUTDOWN_REPLY_TEXT); + } + + public void close(final String replyText) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Closing connection registry :" + _registry.size() + " connections."); + } while (!_registry.isEmpty()) { AMQConnectionModel connection = _registry.get(0); - closeConnection(connection, AMQConstant.CONNECTION_FORCED, "Broker is shutting down"); + closeConnection(connection, AMQConstant.CONNECTION_FORCED, replyText); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java index 89582e5748..954c448b72 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java @@ -28,12 +28,17 @@ import java.util.List; public interface IConnectionRegistry { + public static final String BROKER_SHUTDOWN_REPLY_TEXT = "Broker is shutting down"; + public static final String VHOST_PASSIVATE_REPLY_TEXT = "Virtual host is being passivated"; + public void initialise(); public void close() throws AMQException; - + + public void close(String replyText) throws AMQException; + public void closeConnection(AMQConnectionModel connection, AMQConstant cause, String message); - + public List getConnections(); public void registerConnection(AMQConnectionModel connnection); diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java index cae07046fa..9493f400f2 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java @@ -20,8 +20,6 @@ */ package org.apache.qpid.server.exchange; -import org.apache.log4j.Logger; - import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.binding.Binding; @@ -116,7 +114,7 @@ public abstract class AbstractExchange implements Exchange, Managable */ protected abstract AbstractExchangeMBean createMBean() throws JMException; - public void initialise(VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) + public void initialise(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException { _virtualHost = host; @@ -125,10 +123,18 @@ public abstract class AbstractExchange implements Exchange, Managable _autoDelete = autoDelete; _ticket = ticket; - // TODO - fix - _id = getConfigStore().createId(); + _id = id; getConfigStore().addConfiguredObject(this); + createAndRegisterMBean(); + _logSubject = new ExchangeLogSubject(this, this.getVirtualHost()); + + // Log Exchange creation + CurrentActor.get().message(ExchangeMessages.CREATED(String.valueOf(getTypeShortString()), String.valueOf(name), durable)); + } + + private void createAndRegisterMBean() + { try { _exchangeMbean = createMBean(); @@ -136,12 +142,8 @@ public abstract class AbstractExchange implements Exchange, Managable } catch (JMException e) { - getLogger().error(e); + throw new RuntimeException("Failed to register mbean",e); } - _logSubject = new ExchangeLogSubject(this, this.getVirtualHost()); - - // Log Exchange creation - CurrentActor.get().message(ExchangeMessages.CREATED(String.valueOf(getTypeShortString()), String.valueOf(name), durable)); } public ConfigStore getConfigStore() @@ -149,8 +151,6 @@ public abstract class AbstractExchange implements Exchange, Managable return getVirtualHost().getConfigStore(); } - public abstract Logger getLogger(); - public boolean isDurable() { return _durable; @@ -324,8 +324,7 @@ public abstract class AbstractExchange implements Exchange, Managable public Map getArguments() { - // TODO - Fix - return Collections.EMPTY_MAP; + return Collections.emptyMap(); } public UUID getId() diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java index 153419de1b..5058f91995 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java @@ -25,9 +25,11 @@ import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.AMQSecurityException; import org.apache.qpid.AMQUnknownExchangeType; +import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.qmf.ManagementExchange; import org.apache.qpid.server.configuration.VirtualHostConfiguration; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -35,6 +37,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import java.util.UUID; public class DefaultExchangeFactory implements ExchangeFactory { @@ -76,17 +79,29 @@ public class DefaultExchangeFactory implements ExchangeFactory return publicTypes; } - - public Exchange createExchange(String exchange, String type, boolean durable, boolean autoDelete) - throws AMQException + throws AMQException { return createExchange(new AMQShortString(exchange), new AMQShortString(type), durable, autoDelete, 0); } - public Exchange createExchange(AMQShortString exchange, AMQShortString type, boolean durable, boolean autoDelete, - int ticket) + public Exchange createExchange(UUID id, String exchange, String type, boolean durable, boolean autoDelete) + throws AMQException + { + return createExchange(id, new AMQShortString(exchange), new AMQShortString(type), durable, autoDelete, 0); + } + + public Exchange createExchange(AMQShortString exchange, AMQShortString type, boolean durable, + boolean autoDelete, int ticket) + throws AMQException + { + UUID id = UUIDGenerator.generateExchangeUUID(exchange.asString(), _host.getName()); + return createExchange(id, exchange, type, durable, autoDelete, ticket); + } + + public Exchange createExchange(UUID id, AMQShortString exchange, AMQShortString type, boolean durable, + boolean autoDelete, int ticket) throws AMQException { // Check access @@ -102,7 +117,7 @@ public class DefaultExchangeFactory implements ExchangeFactory throw new AMQUnknownExchangeType("Unknown exchange type: " + type,null); } - Exchange e = exchType.newInstance(_host, exchange, durable, ticket, autoDelete); + Exchange e = exchType.newInstance(id, _host, exchange, durable, ticket, autoDelete); return e; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java index a5fa9f014e..bf4184bf0b 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java @@ -21,7 +21,6 @@ package org.apache.qpid.server.exchange; import org.apache.log4j.Logger; - import org.apache.qpid.AMQException; import org.apache.qpid.AMQSecurityException; import org.apache.qpid.exchange.ExchangeDefaults; @@ -31,12 +30,13 @@ import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Collection; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; public class DefaultExchangeRegistry implements ExchangeRegistry { - private static final Logger _log = Logger.getLogger(DefaultExchangeRegistry.class); + private static final Logger LOGGER = Logger.getLogger(DefaultExchangeRegistry.class); /** * Maps from exchange name to exchange instance @@ -59,11 +59,9 @@ public class DefaultExchangeRegistry implements ExchangeRegistry new ExchangeInitialiser().initialise(_host.getExchangeFactory(), this, getDurableConfigurationStore()); } - - public DurableConfigurationStore getDurableConfigurationStore() { - return _host.getDurableConfigurationStore(); + return _host.getMessageStore(); } public void registerExchange(Exchange exchange) throws AMQException @@ -153,4 +151,49 @@ public class DefaultExchangeRegistry implements ExchangeRegistry } } + @Override + public void clearAndUnregisterMbeans() + { + for (final AMQShortString exchangeName : getExchangeNames()) + { + final Exchange exchange = getExchange(exchangeName); + + if (exchange instanceof AbstractExchange) + { + AbstractExchange abstractExchange = (AbstractExchange) exchange; + try + { + abstractExchange.getManagedObject().unregister(); + } + catch (AMQException e) + { + LOGGER.warn("Failed to unregister mbean", e); + } + } + } + _exchangeMap.clear(); + _exchangeMapStr.clear(); + } + + @Override + public synchronized Exchange getExchange(UUID exchangeId) + { + if (exchangeId == null) + { + return getDefaultExchange(); + } + else + { + Collection exchanges = _exchangeMap.values(); + for (Exchange exchange : exchanges) + { + if (exchange.getId().equals(exchangeId)) + { + return exchange; + } + } + return null; + } + } + } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java index 8c0a5001db..af9322764a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java @@ -36,6 +36,7 @@ import javax.management.JMException; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; @@ -103,14 +104,14 @@ public class DirectExchange extends AbstractExchange return DirectExchange.class; } - public DirectExchange newInstance(VirtualHost host, + public DirectExchange newInstance(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException { DirectExchange exch = new DirectExchange(); - exch.initialise(host,name,durable,ticket,autoDelete); + exch.initialise(id, host,name,durable,ticket,autoDelete); return exch; } @@ -131,12 +132,6 @@ public class DirectExchange extends AbstractExchange return new DirectExchangeMBean(this); } - public Logger getLogger() - { - return _logger; - } - - public List doRoute(InboundMessage payload) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java index c3ae15f09c..289cb1a923 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java @@ -35,6 +35,8 @@ import org.apache.qpid.server.virtualhost.VirtualHost; import javax.management.JMException; import java.util.Collection; import java.util.List; +import java.util.Map; +import java.util.UUID; public interface Exchange extends ExchangeReferrer, ExchangeConfig { @@ -49,7 +51,7 @@ public interface Exchange extends ExchangeReferrer, ExchangeConfig AMQShortString getTypeShortString(); - void initialise(VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) + void initialise(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException, JMException; boolean isDurable(); @@ -111,6 +113,8 @@ public interface Exchange extends ExchangeReferrer, ExchangeConfig boolean isBound(String bindingKey, AMQQueue queue); + public boolean isBound(String bindingKey, Map arguments, AMQQueue queue); + boolean isBound(String bindingKey); void addCloseTask(Task task); diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java index 577da79028..aae4ae89bb 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeFactory.java @@ -25,6 +25,7 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.configuration.VirtualHostConfiguration; import java.util.Collection; +import java.util.UUID; public interface ExchangeFactory @@ -40,4 +41,10 @@ public interface ExchangeFactory Collection> getPublicCreatableTypes(); Exchange createExchange(String exchange, String type, boolean durable, boolean autoDelete) throws AMQException; + + Exchange createExchange(UUID id, String exchange, String type, boolean durable, boolean autoDelete) throws AMQException; + + Exchange createExchange(UUID id, AMQShortString exchange, AMQShortString type, boolean durable, + boolean autoDelete, int ticket) + throws AMQException; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeInitialiser.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeInitialiser.java index 4dfcce7bbe..ba4f57a8e0 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeInitialiser.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeInitialiser.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.exchange; + import org.apache.qpid.AMQException; import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; @@ -27,7 +28,8 @@ import org.apache.qpid.server.store.DurableConfigurationStore; public class ExchangeInitialiser { - public void initialise(ExchangeFactory factory, ExchangeRegistry registry, DurableConfigurationStore store) throws AMQException{ + public void initialise(ExchangeFactory factory, ExchangeRegistry registry, DurableConfigurationStore store) throws AMQException + { for (ExchangeType type : factory.getRegisteredTypes()) { define (registry, factory, type.getDefaultExchangeName(), type.getName(), store); @@ -44,7 +46,6 @@ public class ExchangeInitialiser { Exchange exchange = f.createExchange(name, type, true, false, 0); r.registerExchange(exchange); - if(exchange.isDurable()) { store.createExchange(exchange); diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java index 18eb37e037..795ae2e140 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeRegistry.java @@ -24,6 +24,7 @@ import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import java.util.Collection; +import java.util.UUID; public interface ExchangeRegistry @@ -51,5 +52,9 @@ public interface ExchangeRegistry Exchange getExchange(String exchangeName); - void unregisterExchange(String exchange, boolean ifUnused) throws ExchangeInUseException, AMQException;; + void unregisterExchange(String exchange, boolean ifUnused) throws ExchangeInUseException, AMQException; + + void clearAndUnregisterMbeans(); + + Exchange getExchange(UUID exchangeId); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeType.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeType.java index ce339c4e29..a01e41f039 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeType.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/ExchangeType.java @@ -20,6 +20,8 @@ */ package org.apache.qpid.server.exchange; +import java.util.UUID; + import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -29,7 +31,7 @@ public interface ExchangeType { public AMQShortString getName(); public Class getExchangeClass(); - public T newInstance(VirtualHost host, AMQShortString name, + public T newInstance(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException; public AMQShortString getDefaultExchangeName(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java index 76f86ea1b4..5ebcfd095f 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java @@ -34,6 +34,7 @@ import org.apache.qpid.server.virtualhost.VirtualHost; import javax.management.JMException; import java.util.ArrayList; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; public class FanoutExchange extends AbstractExchange @@ -52,11 +53,6 @@ public class FanoutExchange extends AbstractExchange return new FanoutExchangeMBean(this); } - public Logger getLogger() - { - return _logger; - } - public static final ExchangeType TYPE = new ExchangeType() { @@ -70,14 +66,14 @@ public class FanoutExchange extends AbstractExchange return FanoutExchange.class; } - public FanoutExchange newInstance(VirtualHost host, + public FanoutExchange newInstance(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException { FanoutExchange exch = new FanoutExchange(); - exch.initialise(host, name, durable, ticket, autoDelete); + exch.initialise(id, host, name, durable, ticket, autoDelete); return exch; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java index 0886ae2ae0..b6f5f973f4 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersBinding.java @@ -39,7 +39,7 @@ class HeadersBinding { private static final Logger _logger = Logger.getLogger(HeadersBinding.class); - private final FieldTable _mappings; + private final Map _mappings; private final Binding _binding; private final Set required = new HashSet(); private final Map matches = new HashMap(); @@ -58,7 +58,7 @@ class HeadersBinding _binding = binding; if(_binding !=null) { - _mappings = FieldTable.convertToFieldTable(_binding.getArguments()); + _mappings = _binding.getArguments(); initMappings(); } else @@ -69,37 +69,23 @@ class HeadersBinding private void initMappings() { - _mappings.processOverElements(new FieldTable.FieldTableElementProcessor() + for(Map.Entry entry : _mappings.entrySet()) { - - public boolean processElement(String propertyName, AMQTypedValue value) + String propertyName = entry.getKey(); + Object value = entry.getValue(); + if (isSpecial(propertyName)) { - if (isSpecial(propertyName)) - { - processSpecial(propertyName, value.getValue()); - } - else if (value.getValue() == null || value.getValue().equals("")) - { - required.add(propertyName); - } - else - { - matches.put(propertyName,value.getValue()); - } - - return true; + processSpecial(propertyName, value); } - - public Object getResult() + else if (value == null || value.equals("")) { - return null; + required.add(propertyName); } - }); - } - - protected FieldTable getMappings() - { - return _mappings; + else + { + matches.put(propertyName,value); + } + } } public Binding getBinding() diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java index de12da74b4..16ba3c0431 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java @@ -37,6 +37,7 @@ import javax.management.JMException; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.Map; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; @@ -93,12 +94,12 @@ public class HeadersExchange extends AbstractExchange return HeadersExchange.class; } - public HeadersExchange newInstance(VirtualHost host, AMQShortString name, boolean durable, int ticket, + public HeadersExchange newInstance(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException { HeadersExchange exch = new HeadersExchange(); - exch.initialise(host, name, durable, ticket, autoDelete); + exch.initialise(id, host, name, durable, ticket, autoDelete); return exch; } @@ -146,6 +147,33 @@ public class HeadersExchange extends AbstractExchange return new ArrayList(queues); } + + public boolean isBound(String bindingKey, Map arguments, AMQQueue queue) + { + CopyOnWriteArraySet bindings; + if(bindingKey == null) + { + bindings = new CopyOnWriteArraySet(getBindings()); + } + else + { + bindings = _bindingsByKey.get(bindingKey); + } + + if(bindings != null) + { + for(Binding binding : bindings) + { + if(queue == null || binding.getQueue().equals(queue)) + { + return arguments == null ? binding.getArguments() == null : binding.getArguments().equals(arguments); + } + } + } + + return false; + } + public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue) { //fixme isBound here should take the arguements in to consideration. @@ -204,11 +232,6 @@ public class HeadersExchange extends AbstractExchange return new HeadersExchangeMBean(this); } - public Logger getLogger() - { - return _logger; - } - protected void onBind(final Binding binding) { String bindingKey = binding.getBindingKey(); @@ -251,10 +274,11 @@ public class HeadersExchange extends AbstractExchange { bindings.remove(binding); } - + + boolean removedBinding = _bindingHeaderMatchers.remove(new HeadersBinding(binding)); if(_logger.isDebugEnabled()) { - _logger.debug("Removing Binding: " + _bindingHeaderMatchers.remove(new HeadersBinding(binding))); + _logger.debug("Removing Binding: " + removedBinding); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java index 6d7242a78d..7ea7a41826 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java +++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java @@ -20,13 +20,24 @@ */ package org.apache.qpid.server.exchange; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.UUID; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import javax.management.JMException; import org.apache.log4j.Logger; - import org.apache.qpid.AMQException; import org.apache.qpid.AMQInvalidArgumentException; import org.apache.qpid.common.AMQPFilterTypes; import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.filter.SelectorParsingException; +import org.apache.qpid.filter.selector.ParseException; import org.apache.qpid.filter.selector.TokenMgrError; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; @@ -36,23 +47,14 @@ import org.apache.qpid.server.exchange.topic.TopicMatcherResult; import org.apache.qpid.server.exchange.topic.TopicNormalizer; import org.apache.qpid.server.exchange.topic.TopicParser; import org.apache.qpid.server.filter.JMSSelectorFilter; -import org.apache.qpid.filter.selector.ParseException; +import org.apache.qpid.server.filter.MessageFilter; import org.apache.qpid.server.message.InboundMessage; +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.Filterable; import org.apache.qpid.server.virtualhost.VirtualHost; -import javax.management.JMException; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.WeakHashMap; -import java.util.concurrent.ConcurrentHashMap; - public class TopicExchange extends AbstractExchange { @@ -69,14 +71,14 @@ public class TopicExchange extends AbstractExchange return TopicExchange.class; } - public TopicExchange newInstance(VirtualHost host, + public TopicExchange newInstance(UUID id, VirtualHost host, AMQShortString name, boolean durable, int ticket, boolean autoDelete) throws AMQException { TopicExchange exch = new TopicExchange(); - exch.initialise(host, name, durable, ticket, autoDelete); + exch.initialise(id, host, name, durable, ticket, autoDelete); return exch; } @@ -122,24 +124,26 @@ public class TopicExchange extends AbstractExchange FieldTable oldArgs = _bindings.get(binding); TopicExchangeResult result = _topicExchangeResults.get(routingKey); - if(argumentsContainSelector(args)) + if(argumentsContainFilter(args)) { - if(argumentsContainSelector(oldArgs)) + if(argumentsContainFilter(oldArgs)) { - result.replaceQueueFilter(queue,createSelectorFilter(oldArgs), createSelectorFilter(args)); + result.replaceQueueFilter(queue, + createMessageFilter(oldArgs, queue), + createMessageFilter(args, queue)); } else { - result.addFilteredQueue(queue,createSelectorFilter(args)); + result.addFilteredQueue(queue, createMessageFilter(args, queue)); result.removeUnfilteredQueue(queue); } } else { - if(argumentsContainSelector(oldArgs)) + if(argumentsContainFilter(oldArgs)) { result.addUnfilteredQueue(queue); - result.removeFilteredQueue(queue, createSelectorFilter(oldArgs)); + result.removeFilteredQueue(queue, createMessageFilter(oldArgs, queue)); } else { @@ -158,9 +162,9 @@ public class TopicExchange extends AbstractExchange if(result == null) { result = new TopicExchangeResult(); - if(argumentsContainSelector(args)) + if(argumentsContainFilter(args)) { - result.addFilteredQueue(queue, createSelectorFilter(args)); + result.addFilteredQueue(queue, createMessageFilter(args, queue)); } else { @@ -171,9 +175,9 @@ public class TopicExchange extends AbstractExchange } else { - if(argumentsContainSelector(args)) + if(argumentsContainFilter(args)) { - result.addFilteredQueue(queue, createSelectorFilter(args)); + result.addFilteredQueue(queue, createMessageFilter(args, queue)); } else { @@ -187,9 +191,28 @@ public class TopicExchange extends AbstractExchange } - private JMSSelectorFilter createSelectorFilter(final FieldTable args) throws AMQInvalidArgumentException + private MessageFilter createMessageFilter(final FieldTable args, AMQQueue queue) throws AMQInvalidArgumentException { + if(argumentsContainNoLocal(args)) + { + MessageFilter filter = new NoLocalFilter(queue); + + if(argumentsContainJMSSelector(args)) + { + filter = new CompoundFilter(filter, createJMSSelectorFilter(args)); + } + return filter; + } + else + { + return createJMSSelectorFilter(args); + } + + } + + private MessageFilter createJMSSelectorFilter(FieldTable args) throws AMQInvalidArgumentException + { final String selectorString = args.getString(AMQPFilterTypes.JMS_SELECTOR.getValue()); WeakReference selectorRef = _selectorCache.get(selectorString); JMSSelectorFilter selector = null; @@ -217,11 +240,25 @@ public class TopicExchange extends AbstractExchange return selector; } - private static boolean argumentsContainSelector(final FieldTable args) + private static boolean argumentsContainFilter(final FieldTable args) { - return args != null && args.containsKey(AMQPFilterTypes.JMS_SELECTOR.getValue()) && args.getString(AMQPFilterTypes.JMS_SELECTOR.getValue()).trim().length() != 0; + return argumentsContainNoLocal(args) || argumentsContainJMSSelector(args); } + private static boolean argumentsContainNoLocal(final FieldTable args) + { + return args != null + && args.containsKey(AMQPFilterTypes.NO_LOCAL.getValue()) + && Boolean.TRUE.equals(args.get(AMQPFilterTypes.NO_LOCAL.getValue())); + } + + private static boolean argumentsContainJMSSelector(final FieldTable args) + { + return args != null && (args.containsKey(AMQPFilterTypes.JMS_SELECTOR.getValue()) + && args.getString(AMQPFilterTypes.JMS_SELECTOR.getValue()).trim().length() != 0); + } + + public ArrayList doRoute(InboundMessage payload) { @@ -275,6 +312,28 @@ public class TopicExchange extends AbstractExchange } } + public boolean isBound(String bindingKey, Map arguments, AMQQueue queue) + { + Binding binding = new Binding(null, bindingKey, queue, this, arguments); + if (arguments == null) + { + return _bindings.containsKey(binding); + } + else + { + FieldTable o = _bindings.get(binding); + if (o != null) + { + return arguments.equals(FieldTable.convertToMap(o)); + } + else + { + return false; + } + } + + } + public boolean isBound(AMQShortString routingKey, AMQQueue queue) { return isBound(routingKey, null, queue); @@ -321,11 +380,11 @@ public class TopicExchange extends AbstractExchange result.removeBinding(binding); - if(argumentsContainSelector(bindingArgs)) + if(argumentsContainFilter(bindingArgs)) { try { - result.removeFilteredQueue(binding.getQueue(), createSelectorFilter(bindingArgs)); + result.removeFilteredQueue(binding.getQueue(), createMessageFilter(bindingArgs, binding.getQueue())); } catch (AMQInvalidArgumentException e) { @@ -349,11 +408,6 @@ public class TopicExchange extends AbstractExchange return new TopicExchangeMBean(this); } - public Logger getLogger() - { - return _logger; - } - private Collection getMatchedQueues(InboundMessage message, AMQShortString routingKey) { @@ -402,4 +456,96 @@ public class TopicExchange extends AbstractExchange deregisterQueue(binding); } + private static final class NoLocalFilter implements MessageFilter + { + private final AMQQueue _queue; + + public NoLocalFilter(AMQQueue queue) + { + _queue = queue; + } + + public boolean matches(Filterable message) + { + InboundMessage inbound = (InboundMessage) message; + final AMQSessionModel exclusiveOwningSession = _queue.getExclusiveOwningSession(); + return exclusiveOwningSession == null || !exclusiveOwningSession.onSameConnection(inbound); + + } + + @Override + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + + if (o == null || getClass() != o.getClass()) + { + return false; + } + + NoLocalFilter that = (NoLocalFilter) o; + + return _queue == null ? that._queue == null : _queue.equals(that._queue); + } + + @Override + public int hashCode() + { + return _queue != null ? _queue.hashCode() : 0; + } + } + + private static final class CompoundFilter implements MessageFilter + { + private MessageFilter _noLocalFilter; + private MessageFilter _jmsSelectorFilter; + + public CompoundFilter(MessageFilter filter, MessageFilter jmsSelectorFilter) + { + _noLocalFilter = filter; + _jmsSelectorFilter = jmsSelectorFilter; + } + + public boolean matches(Filterable message) + { + return _noLocalFilter.matches(message) && _jmsSelectorFilter.matches(message); + } + + @Override + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + if (o == null || getClass() != o.getClass()) + { + return false; + } + + CompoundFilter that = (CompoundFilter) o; + + if (_jmsSelectorFilter != null ? !_jmsSelectorFilter.equals(that._jmsSelectorFilter) : that._jmsSelectorFilter != null) + { + return false; + } + if (_noLocalFilter != null ? !_noLocalFilter.equals(that._noLocalFilter) : that._noLocalFilter != null) + { + return false; + } + + return true; + } + + @Override + public int hashCode() + { + int result = _noLocalFilter != null ? _noLocalFilter.hashCode() : 0; + result = 31 * result + (_jmsSelectorFilter != null ? _jmsSelectorFilter.hashCode() : 0); + return result; + } + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/federation/Bridge.java b/java/broker/src/main/java/org/apache/qpid/server/federation/Bridge.java index b58802e1ff..c7046f8e53 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/federation/Bridge.java +++ b/java/broker/src/main/java/org/apache/qpid/server/federation/Bridge.java @@ -132,7 +132,7 @@ public class Bridge implements BridgeConfig { try { - brokerLink.getVirtualHost().getDurableConfigurationStore().createBridge(this); + brokerLink.getVirtualHost().getMessageStore().createBridge(this); } catch (AMQStoreException e) { @@ -220,7 +220,7 @@ public class Bridge implements BridgeConfig { try { - brokerLink.getVirtualHost().getDurableConfigurationStore().createBridge(this); + brokerLink.getVirtualHost().getMessageStore().createBridge(this); } catch (AMQStoreException e) { @@ -767,13 +767,13 @@ public class Bridge implements BridgeConfig try { - _queue = AMQQueueFactory.createAMQQueueImpl(_tmpQueueName, + _queue = AMQQueueFactory.createAMQQueueImpl(null, + _tmpQueueName, isDurable(), _link.getFederationTag(), false, false, - getVirtualHost(), - options); + getVirtualHost(), options); } catch (AMQException e) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/federation/BrokerLink.java b/java/broker/src/main/java/org/apache/qpid/server/federation/BrokerLink.java index 032df8bb0d..4bcc0d6136 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/federation/BrokerLink.java +++ b/java/broker/src/main/java/org/apache/qpid/server/federation/BrokerLink.java @@ -250,7 +250,7 @@ public class BrokerLink implements LinkConfig, ConnectionListener { try { - _virtualHost.getDurableConfigurationStore().createBrokerLink(this); + _virtualHost.getMessageStore().createBrokerLink(this); } catch (AMQStoreException e) { @@ -295,7 +295,7 @@ public class BrokerLink implements LinkConfig, ConnectionListener { try { - _virtualHost.getDurableConfigurationStore().createBrokerLink(this); + _virtualHost.getMessageStore().createBrokerLink(this); } catch (AMQStoreException e) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java b/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java index 18a0e4c8bf..6c158de8b5 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java @@ -39,6 +39,12 @@ public class SimpleFilterManager implements FilterManager _filters = new ConcurrentLinkedQueue(); } + public SimpleFilterManager(JMSSelectorFilter messageFilter) + { + this(); + add(messageFilter); + } + public void add(MessageFilter filter) { _filters.add(filter); diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java index 79fcfb6d76..eab28ac9d4 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java @@ -32,6 +32,7 @@ import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.state.AMQState; import org.apache.qpid.server.state.AMQStateManager; import org.apache.qpid.server.state.StateAwareMethodListener; +import org.apache.qpid.server.virtualhost.State; import org.apache.qpid.server.virtualhost.VirtualHost; public class ConnectionOpenMethodHandler implements StateAwareMethodListener @@ -82,6 +83,10 @@ public class ConnectionOpenMethodHandler implements StateAwareMethodListener Map oldArgs = oldBinding.getArguments(); if((oldArgs == null && !arguments.isEmpty()) || (oldArgs != null && !oldArgs.equals(arguments))) { - virtualHost.getBindingFactory().replaceBinding(bindingKey, queue, exch, arguments); + virtualHost.getBindingFactory().replaceBinding(oldBinding.getId(), bindingKey, queue, exch, arguments); } } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java index f57f7eb9e6..396829df91 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java @@ -24,6 +24,7 @@ import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.MethodRegistry; import org.apache.qpid.framing.QueueDeclareBody; import org.apache.qpid.framing.QueueDeclareOkBody; @@ -31,6 +32,7 @@ import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.server.AMQChannel; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.queue.AMQQueue; @@ -43,6 +45,7 @@ import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Collections; +import java.util.Map; import java.util.UUID; public class QueueDeclareHandler implements StateAwareMethodListener @@ -65,7 +68,7 @@ public class QueueDeclareHandler implements StateAwareMethodListener arguments = FieldTable.convertToMap(body.getArguments()); + final AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(UUIDGenerator.generateUUID(), AMQShortString.toString(queueName), body.getDurable(), owner, body.getAutoDelete(), + body.getExclusive(),virtualHost, arguments); if (body.getExclusive() && !body.getDurable()) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java index cc37259d54..762f090b83 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java @@ -62,7 +62,7 @@ public class QueueDeleteHandler implements StateAwareMethodListener FACTORY = new MetaDataFactory(); private volatile ByteBuffer _encoded; + private Object _connectionReference; public MessageMetaData_0_10(MessageTransfer xfr) @@ -219,6 +220,16 @@ public class MessageMetaData_0_10 implements StorableMessageMetaData, InboundMes return _header; } + public void setConnectionReference(Object connectionReference) + { + _connectionReference = connectionReference; + } + + public Object getConnectionReference() + { + return _connectionReference; + } + private static class MetaDataFactory implements MessageMetaDataType.Factory { public MessageMetaData_0_10 createMetaData(ByteBuffer buf) diff --git a/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData_1_0.java b/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData_1_0.java new file mode 100755 index 0000000000..7d030fe711 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/message/MessageMetaData_1_0.java @@ -0,0 +1,522 @@ +/* +* +* 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 java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.apache.qpid.amqp_1_0.codec.ValueHandler; +import org.apache.qpid.amqp_1_0.messaging.SectionDecoder; +import org.apache.qpid.amqp_1_0.type.AmqpErrorException; +import org.apache.qpid.amqp_1_0.type.Section; +import org.apache.qpid.amqp_1_0.type.Symbol; +import org.apache.qpid.amqp_1_0.type.codec.AMQPDescribedTypeRegistry; +import org.apache.qpid.amqp_1_0.type.messaging.AmqpSequence; +import org.apache.qpid.amqp_1_0.type.messaging.AmqpValue; +import org.apache.qpid.amqp_1_0.type.messaging.ApplicationProperties; +import org.apache.qpid.amqp_1_0.type.messaging.Data; +import org.apache.qpid.amqp_1_0.type.messaging.DeliveryAnnotations; +import org.apache.qpid.amqp_1_0.type.messaging.Footer; +import org.apache.qpid.amqp_1_0.type.messaging.Header; +import org.apache.qpid.amqp_1_0.type.messaging.MessageAnnotations; +import org.apache.qpid.amqp_1_0.type.messaging.Properties; +import org.apache.qpid.server.store.MessageMetaDataType; +import org.apache.qpid.server.store.StorableMessageMetaData; + +public class MessageMetaData_1_0 implements StorableMessageMetaData +{ + // TODO move to somewhere more useful + public static final Symbol JMS_TYPE = Symbol.valueOf("x-opt-jms-type"); + + + private Header _header; + private Properties _properties; + private Map _deliveryAnnotations; + private Map _messageAnnotations; + private Map _appProperties; + private Map _footer; + + private List _encodedSections = new ArrayList(3); + + private volatile ByteBuffer _encoded; + private MessageHeader_1_0 _messageHeader; + + + + public MessageMetaData_1_0(ByteBuffer[] fragments, SectionDecoder decoder) + { + this(fragments, decoder, new ArrayList(3)); + } + + public MessageMetaData_1_0(ByteBuffer[] fragments, SectionDecoder decoder, List immuatableSections) + { + this(constructSections(fragments, decoder,immuatableSections), immuatableSections); + } + + private MessageMetaData_1_0(List
sections, List encodedSections) + { + _encodedSections = encodedSections; + + Iterator
sectIter = sections.iterator(); + + Section section = sectIter.hasNext() ? sectIter.next() : null; + if(section instanceof Header) + { + _header = (Header) section; + section = sectIter.hasNext() ? sectIter.next() : null; + } + + if(section instanceof DeliveryAnnotations) + { + _deliveryAnnotations = ((DeliveryAnnotations) section).getValue(); + section = sectIter.hasNext() ? sectIter.next() : null; + } + + if(section instanceof MessageAnnotations) + { + _messageAnnotations = ((MessageAnnotations) section).getValue(); + section = sectIter.hasNext() ? sectIter.next() : null; + } + + if(section instanceof Properties) + { + _properties = (Properties) section; + section = sectIter.hasNext() ? sectIter.next() : null; + } + + if(section instanceof ApplicationProperties) + { + _appProperties = ((ApplicationProperties) section).getValue(); + section = sectIter.hasNext() ? sectIter.next() : null; + } + + if(section instanceof Footer) + { + _footer = ((Footer) section).getValue(); + section = sectIter.hasNext() ? sectIter.next() : null; + } + + _messageHeader = new MessageHeader_1_0(); + + } + + private static List
constructSections(final ByteBuffer[] fragments, final SectionDecoder decoder, List encodedSections) + { + List
sections = new ArrayList
(3); + + ByteBuffer src; + if(fragments.length == 1) + { + src = fragments[0].duplicate(); + } + else + { + int size = 0; + for(ByteBuffer buf : fragments) + { + size += buf.remaining(); + } + src = ByteBuffer.allocate(size); + for(ByteBuffer buf : fragments) + { + src.put(buf.duplicate()); + } + src.flip(); + + } + + try + { + int startBarePos = -1; + int lastPos = src.position(); + Section s = decoder.readSection(src); + + + + if(s instanceof Header) + { + sections.add(s); + lastPos = src.position(); + s = src.hasRemaining() ? decoder.readSection(src) : null; + } + + if(s instanceof DeliveryAnnotations) + { + sections.add(s); + lastPos = src.position(); + s = src.hasRemaining() ? decoder.readSection(src) : null; + } + + if(s instanceof MessageAnnotations) + { + sections.add(s); + lastPos = src.position(); + s = src.hasRemaining() ? decoder.readSection(src) : null; + } + + if(s instanceof Properties) + { + sections.add(s); + if(startBarePos == -1) + { + startBarePos = lastPos; + } + s = src.hasRemaining() ? decoder.readSection(src) : null; + } + + if(s instanceof ApplicationProperties) + { + sections.add(s); + if(startBarePos == -1) + { + startBarePos = lastPos; + } + s = src.hasRemaining() ? decoder.readSection(src) : null; + } + + if(s instanceof AmqpValue) + { + if(startBarePos == -1) + { + startBarePos = lastPos; + } + s = src.hasRemaining() ? decoder.readSection(src) : null; + } + else if(s instanceof Data) + { + if(startBarePos == -1) + { + startBarePos = lastPos; + } + do + { + s = src.hasRemaining() ? decoder.readSection(src) : null; + } while(s instanceof Data); + } + else if(s instanceof AmqpSequence) + { + if(startBarePos == -1) + { + startBarePos = lastPos; + } + do + { + s = src.hasRemaining() ? decoder.readSection(src) : null; + } + while(s instanceof AmqpSequence); + } + + if(s instanceof Footer) + { + sections.add(s); + } + + + int pos = 0; + for(ByteBuffer buf : fragments) + { +/* + if(pos < startBarePos) + { + if(pos + buf.remaining() > startBarePos) + { + ByteBuffer dup = buf.duplicate(); + dup.position(dup.position()+startBarePos-pos); + dup.slice(); + encodedSections.add(dup); + } + } + else +*/ + { + encodedSections.add(buf.duplicate()); + } + pos += buf.remaining(); + } + + return sections; + } + catch (AmqpErrorException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + throw new IllegalArgumentException(e); + } + } + + + public MessageMetaDataType getType() + { + return MessageMetaDataType.META_DATA_1_0; + } + + + public int getStorableSize() + { + int size = 0; + + for(ByteBuffer bin : _encodedSections) + { + size += bin.limit(); + } + + return size; + } + + private ByteBuffer encodeAsBuffer() + { + ByteBuffer buf = ByteBuffer.allocate(getStorableSize()); + + for(ByteBuffer bin : _encodedSections) + { + buf.put(bin.duplicate()); + } + + return buf; + } + + public int writeToBuffer(int offsetInMetaData, ByteBuffer dest) + { + ByteBuffer buf = _encoded; + + if(buf == null) + { + buf = encodeAsBuffer(); + _encoded = buf; + } + + buf = buf.duplicate(); + + buf.position(offsetInMetaData); + + if(dest.remaining() < buf.limit()) + { + buf.limit(dest.remaining()); + } + dest.put(buf); + return buf.limit(); + } + + public int getContentSize() + { + ByteBuffer buf = _encoded; + + if(buf == null) + { + buf = encodeAsBuffer(); + _encoded = buf; + } + return buf.remaining(); + } + + public boolean isPersistent() + { + return _header != null && Boolean.TRUE.equals(_header.getDurable()); + } + + public MessageHeader_1_0 getMessageHeader() + { + return _messageHeader; + } + + public static final MessageMetaDataType.Factory FACTORY = new MetaDataFactory(); + + + private static class MetaDataFactory implements MessageMetaDataType.Factory + { + private final AMQPDescribedTypeRegistry _typeRegistry = AMQPDescribedTypeRegistry.newInstance(); + + public MessageMetaData_1_0 createMetaData(ByteBuffer buf) + { + ValueHandler valueHandler = new ValueHandler(_typeRegistry); + + ArrayList
sections = new ArrayList
(3); + ArrayList encodedSections = new ArrayList(3); + + while(buf.hasRemaining()) + { + try + { + ByteBuffer encodedBuf = buf.duplicate(); + sections.add((Section) valueHandler.parse(buf)); + encodedBuf.limit(buf.position()); + encodedSections.add(encodedBuf); + + } + catch (AmqpErrorException e) + { + //TODO + throw new RuntimeException(e); + } + + } + + return new MessageMetaData_1_0(sections,encodedSections); + + } + } + + public class MessageHeader_1_0 implements AMQMessageHeader + { + + public String getCorrelationId() + { + if(_properties == null || _properties.getCorrelationId() == null) + { + return null; + } + else + { + return _properties.getMessageId().toString(); + } + } + + public long getExpiration() + { + return 0; //TODO + } + + public String getMessageId() + { + if(_properties == null || _properties.getCorrelationId() == null) + { + return null; + } + else + { + return _properties.getCorrelationId().toString(); + } + } + + public String getMimeType() + { + + if(_properties == null || _properties.getContentType() == null) + { + return null; + } + else + { + return _properties.getContentType().toString(); + } + } + + public String getEncoding() + { + return null; //TODO + } + + public byte getPriority() + { + if(_header == null || _header.getPriority() == null) + { + return 4; //javax.jms.Message.DEFAULT_PRIORITY; + } + else + { + return _header.getPriority().byteValue(); + } + } + + public long getTimestamp() + { + if(_properties == null || _properties.getCreationTime() == null) + { + return 0L; + } + else + { + return _properties.getCreationTime().getTime(); + } + + } + + public String getType() + { + + if(_messageAnnotations == null || _messageAnnotations.get(JMS_TYPE) == null) + { + return null; + } + else + { + return _messageAnnotations.get(JMS_TYPE).toString(); + } + } + + public String getReplyTo() + { + if(_properties == null || _properties.getReplyTo() == null) + { + return null; + } + else + { + return _properties.getReplyTo().toString(); + } + } + + public String getReplyToExchange() + { + return null; //TODO + } + + public String getReplyToRoutingKey() + { + return null; //TODO + } + + public Object getHeader(final String name) + { + return _appProperties == null ? null : _appProperties.get(name); + } + + public boolean containsHeaders(final Set names) + { + if(_appProperties == null) + { + return false; + } + + for(String key : names) + { + if(!_appProperties.containsKey(key)) + { + return false; + } + } + return true; + } + + public boolean containsHeader(final String name) + { + return _appProperties != null && _appProperties.containsKey(name); + } + + public String getSubject() + { + return _properties == null ? null : _properties.getSubject(); + } + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/Binding.java b/java/broker/src/main/java/org/apache/qpid/server/model/Binding.java new file mode 100644 index 0000000000..fdb009386c --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/Binding.java @@ -0,0 +1,75 @@ +/* + * + * 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.model; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +public interface Binding extends ConfiguredObject +{ + + public String MATCHED_BYTES = "matchedBytes"; + public String MATCHED_MESSAGES = "matchedMessages"; + public String STATE_CHANGED = "stateChanged"; + + public static final Collection AVAILABLE_STATISTICS = + Collections.unmodifiableCollection( + Arrays.asList( + MATCHED_BYTES, + MATCHED_MESSAGES, + STATE_CHANGED)); + + + public String ARGUMENTS = "arguments"; + public String CREATED = "created"; + public String DURABLE = "durable"; + public String ID = "id"; + public String LIFETIME_POLICY = "lifetimePolicy"; + public String NAME = "name"; + public String STATE = "state"; + public String TIME_TO_LIVE = "timeToLive"; + public String UPDATED = "updated"; + public String QUEUE = "queue"; + public String EXCHANGE = "exchange"; + + public static final Collection AVAILABLE_ATTRIBUTES = + Collections.unmodifiableCollection( + Arrays.asList(ID, + NAME, + STATE, + DURABLE, + LIFETIME_POLICY, + TIME_TO_LIVE, + CREATED, + UPDATED, + EXCHANGE, + QUEUE, + ARGUMENTS) + ); + + + + Map getArguments(); + + void delete(); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/ConfigurationChangeListener.java b/java/broker/src/main/java/org/apache/qpid/server/model/ConfigurationChangeListener.java new file mode 100644 index 0000000000..6477633a9b --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/ConfigurationChangeListener.java @@ -0,0 +1,38 @@ +/* + * + * 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.model; + +public interface ConfigurationChangeListener +{ + /** + * Inform the listener that the passed object has changed state + * + * @param object the object whose state has changed + * @param oldState the state prior to the change + * @param newState the state after the change + */ + void stateChanged(ConfiguredObject object, State oldState, State newState); + + void childAdded(ConfiguredObject object, ConfiguredObject child); + + void childRemoved(ConfiguredObject object, ConfiguredObject child); + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java b/java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java new file mode 100644 index 0000000000..fb47a54d0a --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/ConfiguredObject.java @@ -0,0 +1,229 @@ +/* + * + * 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.model; + +import java.security.AccessControlException; +import java.util.Collection; +import java.util.UUID; + +public interface ConfiguredObject +{ + + /** + * Get the universally unique identifier for the object + * + * @return the objects id + */ + UUID getId(); + + /** + * Get the name of the object + * + * @return the name of the object + */ + String getName(); + + + /** + * Attempt to change the name of the object + * + * Request a change to the name of the object. The caller must pass in the name it believes the object currently + * has. If the current name differes from this expected value, then no name change will occur + * + * @param currentName the name the caller believes the object to have + * @param desiredName the name the caller would like the object to have + * @return the new name for the object + * @throws IllegalStateException if the name of the object may not be changed in in the current state + * @throws AccessControlException if the current context does not have permission to change the name + * @throws IllegalArgumentException if the provided name is not legal + * @throws NullPointerException if the desired name is null + */ + String setName(String currentName, String desiredName) throws IllegalStateException, + AccessControlException; + + + /** + * Get the desired state of the object. + * + * This is the state set at the object itself, however the object + * may not be able attain this state if one of its ancestors is in a different state (in particular a descendant + * object may not be ACTIVE if all of its ancestors are not also ACTIVE). + * + * @return the desired state of the object + */ + State getDesiredState(); + + /** + * Change the desired state of the object + * + * Request a change to the current state. The caller must pass in the state it believe the object to be in, if + * this differs from the current desired state when the object evalues the request, then no state change will occur. + * + * @param currentState the state the caller believes the object to be in + * @param desiredState the state the caller wishes the object to attain + * @return the new current state + * @throws IllegalStateTransitionException the requested state tranisition is invalid + * @throws AccessControlException the current context does not have sufficeint permissions to change the state + */ + State setDesiredState(State currentState, State desiredState) throws IllegalStateTransitionException, + AccessControlException; + + /** + * Get the actual state of the object. + * + * This state is derived fromt the desired state of the object itself and + * the actual state of its parents. If an object "desires" to be ACTIVE, but one of its parents is STOPPED, then + * the actual state of the object will be STOPPED + * + * @return the actual state of the object + */ + State getActualState(); + + + /** + * Add a listener which will be informed of all changes to this configuration object + * + * @param listener the listener to add + */ + void addChangeListener(ConfigurationChangeListener listener); + + /** + * Remove a change listener + * + * + * @param listener the listener to remove + * @return true iff a listener was removed + */ + boolean removeChangeListener(ConfigurationChangeListener listener); + + /** + * Get the parent of the given type for this object + * + * @param clazz the class of parent being asked for + * @return the objects parent + */ + T getParent(Class clazz); + + + /** + * Returns whether the the object configuration is durably stored + * + * @return the durablity + */ + boolean isDurable(); + + /** + * Sets the durability of the object + * + * @param durable true iff the caller wishes the object to store its configuration durably + * + * @throws IllegalStateException if the durability cannot be changed in the current state + * @throws AccessControlException if the current context does not have sufficient permission to change the durability + * @throws IllegalArgumentException if the object does not support the requested durability + */ + void setDurable(boolean durable) throws IllegalStateException, + AccessControlException, + IllegalArgumentException; + + /** + * Return the lifetime policy for the object + * + * @return the lifetime policy + */ + LifetimePolicy getLifetimePolicy(); + + /** + * Set the lifetime policy of the object + * + * @param expected The lifetime policy the caller believes the object currently has + * @param desired The lifetime policy the caller desires the object to have + * @return the new lifetime policy + * @throws IllegalStateException if the lifetime policy cannot be changed in the current state + * @throws AccessControlException if the caller does not have permission to change the lifetime policy + * @throws IllegalArgumentException if the object does not support the requested lifetime policy + */ + LifetimePolicy setLifetimePolicy(LifetimePolicy expected, LifetimePolicy desired) throws IllegalStateException, + AccessControlException, + IllegalArgumentException; + + /** + * Get the time the object will live once the lifetime policy conditions are no longer fulfilled + * + * @return the time to live + */ + long getTimeToLive(); + + /** + * Set the ttl value + * + * @param expected the ttl the caller believes the object currently has + * @param desired the ttl value the caller + * @return the new ttl value + * @throws IllegalStateException if the ttl cannot be set in the current state + * @throws AccessControlException if the caller does not have permission to change the ttl + * @throws IllegalArgumentException if the object does not support the requested ttl value + */ + long setTimeToLive(long expected, long desired) throws IllegalStateException, + AccessControlException, + IllegalArgumentException; + + /** + * Get the names of attributes that are set on this object + * + * Not that the returned collection is correct at the time the method is called, but will not reflect future + * additions or removals when they occur + * + * @return the collection of attribute names + */ + Collection getAttributeNames(); + + + /** + * Return the value for the given attribute + * + * @param name the name of the attribute + * @return the value of the attribute at the object (or null if the attribute is not set + */ + Object getAttribute(String name); + + /** + * Set the value of an attribute + * + * @param name the name of the attribute to be set + * @param expected the value the caller believes the attribute currently has (or null if it is expected to be unset) + * @param desired the desired value for the attribute (or null to unset the attribute) + * @return the new value for the given attribute + * @throws IllegalStateException if the attribute cannot be set while the object is in its current state + * @throws AccessControlException if the caller does not have permission to alter the value of the attribute + * @throws IllegalArgumentException if the provided value is not valid for the given argument + */ + Object setAttribute(String name, Object expected, Object desired) throws IllegalStateException, + AccessControlException, + IllegalArgumentException; + + + /** + * Return the Statistics holder for the ConfiguredObject + * + * @return the Statistics holder for the ConfiguredObject (or null if none exists) + */ + Statistics getStatistics(); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/Consumer.java b/java/broker/src/main/java/org/apache/qpid/server/model/Consumer.java new file mode 100644 index 0000000000..958177e713 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/Consumer.java @@ -0,0 +1,73 @@ +/* + * + * 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.model; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +public interface Consumer extends ConfiguredObject +{ + public String DISTRIBUTION_MODE = "distributionMode"; + public String EXCLUSIVE = "exclusive"; + public String NO_LOCAL = "noLocal"; + public String SELECTOR = "selector"; + public String SETTLEMENT_MODE = "settlementMode"; + public String CREATED = "created"; + public String DURABLE = "durable"; + public String ID = "id"; + public String LIFETIME_POLICY = "lifetimePolicy"; + public String NAME = "name"; + public String STATE = "state"; + public String TIME_TO_LIVE = "timeToLive"; + public String UPDATED = "updated"; + + public Collection AVAILABLE_ATTRIBUTES = + Collections.unmodifiableCollection( + Arrays.asList(ID, + NAME, + STATE, + DURABLE, + LIFETIME_POLICY, + TIME_TO_LIVE, + CREATED, + UPDATED, + DISTRIBUTION_MODE, + SETTLEMENT_MODE, + EXCLUSIVE, + NO_LOCAL, + SELECTOR)); + + public String BYTES_OUT = "bytesOut"; + public String MESSAGES_OUT = "messagesOut"; + public String STATE_CHANGED = "stateChanged"; + public String UNACKNOWLEDGED_BYTES = "unacknowledgedBytes"; + public String UNACKNOWLEDGED_MESSAGES = "unacknowledgedMessages"; + + public Collection AVAILABLE_STATISTICS = + Collections.unmodifiableCollection( + Arrays.asList(BYTES_OUT, + MESSAGES_OUT, + STATE_CHANGED, + UNACKNOWLEDGED_BYTES, + UNACKNOWLEDGED_MESSAGES) + ); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/Exchange.java b/java/broker/src/main/java/org/apache/qpid/server/model/Exchange.java new file mode 100644 index 0000000000..e872273d05 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/Exchange.java @@ -0,0 +1,91 @@ +/* + * + * 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.model; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +public interface Exchange extends ConfiguredObject +{ + String BINDING_COUNT = "bindingCount"; + String BYTES_DROPPED = "bytesDropped"; + String BYTES_IN = "bytesIn"; + String MESSAGES_DROPPED = "messagesDropped"; + String MESSAGES_IN = "messagesIn"; + String PRODUCER_COUNT = "producerCount"; + String STATE_CHANGED = "stateChanged"; + + public static final Collection AVAILABLE_STATISTICS = + Collections.unmodifiableList( + Arrays.asList(BINDING_COUNT, + BYTES_DROPPED, + BYTES_IN, + MESSAGES_DROPPED, + MESSAGES_IN, + PRODUCER_COUNT, + STATE_CHANGED)); + + String CREATED = "created"; + String DURABLE = "durable"; + String ID = "id"; + String LIFETIME_POLICY = "lifetimePolicy"; + String NAME = "name"; + String STATE = "state"; + String TIME_TO_LIVE = "timeToLive"; + String UPDATED = "updated"; + String ALTERNATE_EXCHANGE = "alternateExchange"; + String TYPE = "type"; + + // Attributes + public static final Collection AVAILABLE_ATTRIBUTES = + Collections.unmodifiableList( + Arrays.asList( + ID, + NAME, + STATE, + DURABLE, + LIFETIME_POLICY, + TIME_TO_LIVE, + CREATED, + UPDATED, + ALTERNATE_EXCHANGE, + TYPE + )); + + String getExchangeType(); + + //children + Collection getBindings(); + Collection getPublishers(); + + //operations + Binding createBinding(String bindingKey, + Queue queue, + Map bindingArguments, + Map attributes); + + + // Statistics + + void delete(); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/IllegalStateTransitionException.java b/java/broker/src/main/java/org/apache/qpid/server/model/IllegalStateTransitionException.java new file mode 100644 index 0000000000..9cab5e2103 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/IllegalStateTransitionException.java @@ -0,0 +1,43 @@ +/* + * + * 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.model; + +public class IllegalStateTransitionException extends RuntimeException +{ + public IllegalStateTransitionException() + { + } + + public IllegalStateTransitionException(final String message) + { + super(message); + } + + public IllegalStateTransitionException(final String message, final Throwable cause) + { + super(message, cause); + } + + public IllegalStateTransitionException(final Throwable cause) + { + super(cause); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/LifetimePolicy.java b/java/broker/src/main/java/org/apache/qpid/server/model/LifetimePolicy.java new file mode 100644 index 0000000000..c9006f4e71 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/LifetimePolicy.java @@ -0,0 +1,27 @@ +/* + * + * 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.model; + +public enum LifetimePolicy +{ + PERMANENT, + AUTO_DELETE +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/Publisher.java b/java/broker/src/main/java/org/apache/qpid/server/model/Publisher.java new file mode 100644 index 0000000000..cdb85d8023 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/Publisher.java @@ -0,0 +1,25 @@ +/* + * + * 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.model; + +public interface Publisher extends ConfiguredObject +{ +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/Queue.java b/java/broker/src/main/java/org/apache/qpid/server/model/Queue.java new file mode 100644 index 0000000000..7c4f0de22b --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/Queue.java @@ -0,0 +1,146 @@ +/* + * + * 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.model; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import org.apache.qpid.server.queue.QueueEntryVisitor; + + +public interface Queue extends ConfiguredObject +{ + public static final String BINDING_COUNT = "bindingCount"; + public static final String CONSUMER_COUNT = "consumerCount"; + public static final String CONSUMER_COUNT_WITH_CREDIT = "consumerCountWithCredit"; + public static final String DISCARDS_TTL_BYTES = "discardsTtlBytes"; + public static final String DISCARDS_TTL_MESSAGES = "discardsTtlMessages"; + public static final String PERSISTENT_DEQUEUED_BYTES = "persistentDequeuedBytes"; + public static final String PERSISTENT_DEQUEUED_MESSAGES = "persistentDequeuedMessages"; + public static final String PERSISTENT_ENQUEUED_BYTES = "persistentEnqueuedBytes"; + public static final String PERSISTENT_ENQUEUED_MESSAGES = "persistentEnqueuedMessages"; + public static final String QUEUE_DEPTH_BYTES = "queueDepthBytes"; + public static final String QUEUE_DEPTH_MESSAGES = "queueDepthMessages"; + public static final String STATE_CHANGED = "stateChanged"; + public static final String TOTAL_DEQUEUED_BYTES = "totalDequeuedBytes"; + public static final String TOTAL_DEQUEUED_MESSAGES = "totalDequeuedMessages"; + public static final String TOTAL_ENQUEUED_BYTES = "totalEnqueuedBytes"; + public static final String TOTAL_ENQUEUED_MESSAGES = "totalEnqueuedMessages"; + public static final String UNACKNOWLEDGED_BYTES = "unacknowledgedBytes"; + public static final String UNACKNOWLEDGED_MESSAGES = "unacknowledgedMessages"; + + public static final Collection AVAILABLE_STATISTICS = + Collections.unmodifiableList( + Arrays.asList(BINDING_COUNT, + CONSUMER_COUNT, + CONSUMER_COUNT_WITH_CREDIT, + DISCARDS_TTL_BYTES, + DISCARDS_TTL_MESSAGES, + PERSISTENT_DEQUEUED_BYTES, + PERSISTENT_DEQUEUED_MESSAGES, + PERSISTENT_ENQUEUED_BYTES, + PERSISTENT_ENQUEUED_MESSAGES, + QUEUE_DEPTH_BYTES, + QUEUE_DEPTH_MESSAGES, + STATE_CHANGED, + TOTAL_DEQUEUED_BYTES, + TOTAL_DEQUEUED_MESSAGES, + TOTAL_ENQUEUED_BYTES, + TOTAL_ENQUEUED_MESSAGES, + UNACKNOWLEDGED_BYTES, + UNACKNOWLEDGED_MESSAGES)); + + + + public static final String ID = "id"; + public static final String NAME = "name"; + public static final String STATE = "state"; + public static final String DURABLE = "durable"; + public static final String LIFETIME_POLICY = "lifetimePolicy"; + public static final String TIME_TO_LIVE = "timeToLive"; + public static final String CREATED = "created"; + public static final String UPDATED = "updated"; + + public static final String ALERT_REPEAT_GAP = "alertRepeatGap"; + public static final String ALERT_THRESHOLD_MESSAGE_AGE = "alertThresholdMessageAge"; + public static final String ALERT_THRESHOLD_MESSAGE_SIZE = "alertThresholdMessageSize"; + public static final String ALERT_THRESHOLD_QUEUE_DEPTH_BYTES = "alertThresholdQueueDepthBytes"; + public static final String ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES = "alertThresholdQueueDepthMessages"; + public static final String ALTERNATE_EXCHANGE = "alternateExchange"; + public static final String EXCLUSIVE = "exclusive"; + public static final String MESSAGE_GROUP_KEY = "messageGroupKey"; + public static final String MESSAGE_GROUP_DEFAULT_GROUP = "messageGroupDefaultGroup"; + public static final String MESSAGE_GROUP_SHARED_GROUPS = "messageGroupSharedGroups"; + public static final String LVQ_KEY = "lvqKey"; + public static final String MAXIMUM_DELIVERY_ATTEMPTS = "maximumDeliveryAttempts"; + public static final String NO_LOCAL = "noLocal"; + public static final String OWNER = "owner"; + public static final String QUEUE_FLOW_CONTROL_SIZE_BYTES = "queueFlowControlSizeBytes"; + public static final String QUEUE_FLOW_RESUME_SIZE_BYTES = "queueFlowResumeSizeBytes"; + public static final String QUEUE_FLOW_STOPPED = "queueFlowStopped"; + public static final String SORT_KEY = "sortKey"; + public static final String TYPE = "type"; + + + + + public static final Collection AVAILABLE_ATTRIBUTES = + Collections.unmodifiableList( + Arrays.asList(ID, + NAME, + STATE, + DURABLE, + LIFETIME_POLICY, + TIME_TO_LIVE, + CREATED, + UPDATED, + TYPE, + ALTERNATE_EXCHANGE, + EXCLUSIVE, + OWNER, + NO_LOCAL, + LVQ_KEY, + SORT_KEY, + MESSAGE_GROUP_KEY, + MESSAGE_GROUP_DEFAULT_GROUP, + MESSAGE_GROUP_SHARED_GROUPS, + MAXIMUM_DELIVERY_ATTEMPTS, + QUEUE_FLOW_CONTROL_SIZE_BYTES, + QUEUE_FLOW_RESUME_SIZE_BYTES, + QUEUE_FLOW_STOPPED, + ALERT_THRESHOLD_MESSAGE_AGE, + ALERT_THRESHOLD_MESSAGE_SIZE, + ALERT_THRESHOLD_QUEUE_DEPTH_BYTES, + ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, + ALERT_REPEAT_GAP + )); + + //children + Collection getBindings(); + Collection getConsumers(); + + + //operations + + void visit(QueueEntryVisitor visitor); + + void delete(); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/State.java b/java/broker/src/main/java/org/apache/qpid/server/model/State.java new file mode 100644 index 0000000000..a73b2c9d3e --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/State.java @@ -0,0 +1,30 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.model; + +public enum State +{ + INITIALISING, + QUIESCED, + STOPPED, + ACTIVE, + DELETED +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/Statistics.java b/java/broker/src/main/java/org/apache/qpid/server/model/Statistics.java new file mode 100644 index 0000000000..2cb81eae82 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/Statistics.java @@ -0,0 +1,25 @@ +package org.apache.qpid.server.model; + +import java.util.Collection; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public interface Statistics +{ + Collection getStatisticNames(); + public Object getStatistic(String name); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java b/java/broker/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java new file mode 100644 index 0000000000..d8493c6df4 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/model/UUIDGenerator.java @@ -0,0 +1,54 @@ +/* + * + * 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.model; + +import java.util.UUID; + +import org.apache.qpid.exchange.ExchangeDefaults; + + +public class UUIDGenerator +{ + + public static UUID generateUUID() + { + return UUID.randomUUID(); + } + + public static UUID generateUUID(String objectName, String virtualHostName) + { + StringBuilder sb = new StringBuilder(); + sb.append(virtualHostName).append(objectName); + return UUID.nameUUIDFromBytes(sb.toString().getBytes()); + } + + public static UUID generateExchangeUUID(String echangeName, String virtualHostName) + { + if(ExchangeDefaults.DEFAULT_EXCHANGE_NAME.asString().equals(echangeName) || echangeName.startsWith("amq.") || echangeName.startsWith("qpid.")) + { + return generateUUID(echangeName, virtualHostName); + } + else + { + return generateUUID(); + } + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java index f6980be525..b750b29952 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java @@ -58,6 +58,7 @@ import org.apache.qpid.server.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.subscription.SubscriptionImpl; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; import org.apache.qpid.transport.Sender; @@ -1315,7 +1316,8 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr public void closeSession(AMQSessionModel session, AMQConstant cause, String message) throws AMQException { - closeChannel((Integer)session.getID()); + int channelId = ((AMQChannel)session).getChannelId(); + closeChannel(channelId); MethodRegistry methodRegistry = getMethodRegistry(); ChannelCloseBody responseBody = @@ -1324,7 +1326,7 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr new AMQShortString(message), 0,0); - writeFrame(responseBody.generateFrame((Integer)session.getID())); + writeFrame(responseBody.generateFrame(channelId)); } public void close(AMQConstant cause, String message) throws AMQException @@ -1454,7 +1456,7 @@ public class AMQProtocolEngine implements ServerProtocolEngine, Managable, AMQPr throws AMQException { registerMessageDelivered(entry.getMessage().getSize()); - _protocolOutputConverter.writeDeliver(entry, _channelId, deliveryTag, sub.getConsumerTag()); + _protocolOutputConverter.writeDeliver(entry, _channelId, deliveryTag, ((SubscriptionImpl)sub).getConsumerTag()); entry.incrementDeliveryCount(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java index a80eb46cfa..fa171815ca 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java @@ -20,10 +20,12 @@ */ package org.apache.qpid.server.protocol; +import java.util.UUID; import java.util.concurrent.ConcurrentSkipListSet; import org.apache.qpid.AMQException; import org.apache.qpid.server.logging.LogSubject; +import org.apache.qpid.server.message.InboundMessage; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.SimpleAMQQueue; @@ -34,7 +36,8 @@ import org.apache.qpid.server.queue.SimpleAMQQueue; */ public interface AMQSessionModel extends Comparable { - public Object getID(); + /** Unique session ID across entire broker*/ + public UUID getId(); public AMQConnectionModel getConnectionModel(); @@ -64,4 +67,7 @@ public interface AMQSessionModel extends Comparable void block(AMQQueue queue); void unblock(AMQQueue queue); + + + boolean onSameConnection(InboundMessage inbound); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AmqpProtocolVersion.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AmqpProtocolVersion.java index e925d7a1ec..0a71fe257a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AmqpProtocolVersion.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AmqpProtocolVersion.java @@ -20,4 +20,4 @@ */ package org.apache.qpid.server.protocol; -public enum AmqpProtocolVersion { v0_8, v0_9, v0_9_1, v0_10 } \ No newline at end of file +public enum AmqpProtocolVersion { v0_8, v0_9, v0_9_1, v0_10, v1_0_0 } diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngine.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngine.java index 3b26f05f84..652ffee004 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngine.java +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngine.java @@ -175,6 +175,28 @@ public class MultiVersionProtocolEngine implements ServerProtocolEngine (byte) 10 }; + private static final byte[] AMQP_1_0_0_HEADER = + new byte[] { (byte) 'A', + (byte) 'M', + (byte) 'Q', + (byte) 'P', + (byte) 0, + (byte) 1, + (byte) 0, + (byte) 0 + }; + + private static final byte[] AMQP_SASL_1_0_0_HEADER = + new byte[] { (byte) 'A', + (byte) 'M', + (byte) 'Q', + (byte) 'P', + (byte) 3, + (byte) 1, + (byte) 0, + (byte) 0 + }; + public void setNetworkConnection(NetworkConnection networkConnection) { setNetworkConnection(networkConnection, networkConnection.getSender()); @@ -289,8 +311,48 @@ public class MultiVersionProtocolEngine implements ServerProtocolEngine } }; + private DelegateCreator creator_1_0_0 = new DelegateCreator() + { + + public AmqpProtocolVersion getVersion() + { + return AmqpProtocolVersion.v1_0_0; + } + + + public byte[] getHeaderIdentifier() + { + return AMQP_1_0_0_HEADER; + } + + public ServerProtocolEngine getProtocolEngine() + { + return new ProtocolEngine_1_0_0(_appRegistry,_id); + } + }; + + private DelegateCreator creator_1_0_0_SASL = new DelegateCreator() + { + + public AmqpProtocolVersion getVersion() + { + return AmqpProtocolVersion.v1_0_0; + } + + + public byte[] getHeaderIdentifier() + { + return AMQP_SASL_1_0_0_HEADER; + } + + public ServerProtocolEngine getProtocolEngine() + { + return new ProtocolEngine_1_0_0_SASL(_network, _appRegistry, _id); + } + }; + private final DelegateCreator[] _creators = - new DelegateCreator[] { creator_0_8, creator_0_9, creator_0_9_1, creator_0_10 }; + new DelegateCreator[] { creator_0_8, creator_0_9, creator_0_9_1, creator_0_10, creator_1_0_0_SASL, creator_1_0_0 }; private class ClosedDelegateProtocolEngine implements ServerProtocolEngine diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0.java new file mode 100755 index 0000000000..f31ad5052b --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0.java @@ -0,0 +1,394 @@ +/* + * + * 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; + +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.security.auth.callback.CallbackHandler; +import org.apache.qpid.amqp_1_0.codec.FrameWriter; +import org.apache.qpid.amqp_1_0.framing.AMQFrame; +import org.apache.qpid.amqp_1_0.framing.FrameHandler; +import org.apache.qpid.amqp_1_0.framing.OversizeFrameException; +import org.apache.qpid.amqp_1_0.transport.CallbackHandlerSource; +import org.apache.qpid.amqp_1_0.transport.ConnectionEndpoint; +import org.apache.qpid.amqp_1_0.transport.Container; +import org.apache.qpid.amqp_1_0.transport.FrameOutputHandler; +import org.apache.qpid.amqp_1_0.type.Binary; +import org.apache.qpid.amqp_1_0.type.FrameBody; +import org.apache.qpid.protocol.ServerProtocolEngine; +import org.apache.qpid.server.configuration.ConfigStore; +import org.apache.qpid.server.configuration.ConnectionConfigType; +import org.apache.qpid.server.protocol.v1_0.Connection_1_0; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.transport.Sender; +import org.apache.qpid.transport.network.NetworkConnection; + +public class ProtocolEngine_1_0_0 implements ServerProtocolEngine, FrameOutputHandler +{ + static final AtomicLong _connectionIdSource = new AtomicLong(0L); + + //private NetworkConnection _networkDriver; + private long _readBytes; + private long _writtenBytes; + private final UUID _id; + private final IApplicationRegistry _appRegistry; + private long _createTime = System.currentTimeMillis(); + private ConnectionEndpoint _conn; + private final long _connectionId; + + private static final ByteBuffer HEADER = + ByteBuffer.wrap(new byte[] + { + (byte)'A', + (byte)'M', + (byte)'Q', + (byte)'P', + (byte) 0, + (byte) 1, + (byte) 0, + (byte) 0 + }); + + private FrameWriter _frameWriter; + private FrameHandler _frameHandler; + private Object _sendLock = new Object(); + private byte _major; + private byte _minor; + private byte _revision; + private NetworkConnection _network; + private Sender _sender; + + + static enum State { + A, + M, + Q, + P, + PROTOCOL, + MAJOR, + MINOR, + REVISION, + FRAME + } + + private State _state = State.A; + + + + public ProtocolEngine_1_0_0(final IApplicationRegistry appRegistry, long id) + { + _id = appRegistry.getConfigStore().createId(); + _appRegistry = appRegistry; + _connectionId = id; + } + + + public SocketAddress getRemoteAddress() + { + return _network.getRemoteAddress(); + } + + public SocketAddress getLocalAddress() + { + return _network.getLocalAddress(); + } + + public long getReadBytes() + { + return _readBytes; + } + + public long getWrittenBytes() + { + return _writtenBytes; + } + + public void writerIdle() + { + //Todo + } + + public void readerIdle() + { + //Todo + } + + public void setNetworkConnection(final NetworkConnection network, final Sender sender) + { + _network = network; + _sender = sender; + + Container container = new Container(_appRegistry.getBrokerId().toString()); + + _conn = new ConnectionEndpoint(container,asCallbackHandlerSource(_appRegistry.getAuthenticationManager())); + _conn.setConnectionEventListener(new Connection_1_0(_appRegistry)); + _conn.setFrameOutputHandler(this); + _conn.setRemoteAddress(_network.getRemoteAddress()); + + _frameWriter = new FrameWriter(_conn.getDescribedTypeRegistry()); + _frameHandler = new FrameHandler(_conn); + + _sender.send(HEADER.duplicate()); + _sender.flush(); + } + + private CallbackHandlerSource asCallbackHandlerSource(final AuthenticationManager authenticationManager) + { + return new CallbackHandlerSource() + { + @Override + public CallbackHandler getHandler(String mechanism) + { + return authenticationManager.getHandler(mechanism); + } + }; + } + + public String getAddress() + { + return getRemoteAddress().toString(); + } + + + public ConfigStore getConfigStore() + { + return _appRegistry.getConfigStore(); + } + + public UUID getId() + { + return _id; + } + + public ConnectionConfigType getConfigType() + { + return ConnectionConfigType.getInstance(); + } + + public boolean isDurable() + { + return false; + } + + public synchronized void received(ByteBuffer msg) + { + if(RAW_LOGGER.isLoggable(Level.FINE)) + { + ByteBuffer dup = msg.duplicate(); + byte[] data = new byte[dup.remaining()]; + dup.get(data); + Binary bin = new Binary(data); + RAW_LOGGER.fine("RECV[" + getRemoteAddress() + "] : " + bin.toString()); + } + _readBytes += msg.remaining(); + switch(_state) + { + case A: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + break; + } + case M: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + _state = State.M; + break; + } + + case Q: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + _state = State.Q; + break; + } + case P: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + _state = State.P; + break; + } + case PROTOCOL: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + _state = State.PROTOCOL; + break; + } + case MAJOR: + if(msg.hasRemaining()) + { + _major = msg.get(); + } + else + { + _state = State.MAJOR; + break; + } + case MINOR: + if(msg.hasRemaining()) + { + _minor = msg.get(); + } + else + { + _state = State.MINOR; + break; + } + case REVISION: + if(msg.hasRemaining()) + { + _revision = msg.get(); + + _state = State.FRAME; + } + else + { + _state = State.REVISION; + break; + } + case FRAME: + if(msg.hasRemaining()) + { + _frameHandler.parse(msg); + } + } + + } + + public void exception(Throwable t) + { + t.printStackTrace(); + } + + public void closed() + { + _conn.inputClosed(); + if(_conn != null && _conn.getConnectionEventListener() != null) + { + ((Connection_1_0)_conn.getConnectionEventListener()).closed(); + } + + } + + public long getCreateTime() + { + return _createTime; + } + + + public boolean canSend() + { + return true; + } + + public void send(final AMQFrame amqFrame) + { + send(amqFrame, null); + } + + private final Logger FRAME_LOGGER = Logger.getLogger("FRM"); + private final Logger RAW_LOGGER = Logger.getLogger("RAW"); + + + public void send(final AMQFrame amqFrame, ByteBuffer buf) + { + synchronized(_sendLock) + { + + if(FRAME_LOGGER.isLoggable(Level.FINE)) + { + FRAME_LOGGER.fine("SEND[" + getRemoteAddress() + "|" + amqFrame.getChannel() + "] : " + amqFrame.getFrameBody()); + } + + + _frameWriter.setValue(amqFrame); + + + + ByteBuffer dup = ByteBuffer.allocate(_conn.getMaxFrameSize()); + + int size = _frameWriter.writeToBuffer(dup); + if(size > _conn.getMaxFrameSize()) + { + throw new OversizeFrameException(amqFrame,size); + } + + dup.flip(); + _writtenBytes += dup.limit(); + + if(RAW_LOGGER.isLoggable(Level.FINE)) + { + ByteBuffer dup2 = dup.duplicate(); + byte[] data = new byte[dup2.remaining()]; + dup2.get(data); + Binary bin = new Binary(data); + RAW_LOGGER.fine("SEND[" + getRemoteAddress() + "] : " + bin.toString()); + } + + + _sender.send(dup); + _sender.flush(); + + } + } + + public void send(short channel, FrameBody body) + { + AMQFrame frame = AMQFrame.createAMQFrame(channel, body); + send(frame); + + } + + public void close() + { + //TODO + } + + public long getConnectionId() + { + return _connectionId; + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0_SASL.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0_SASL.java new file mode 100644 index 0000000000..ffd5e750b4 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/ProtocolEngine_1_0_0_SASL.java @@ -0,0 +1,449 @@ +/* + * + * 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; + +import java.io.PrintWriter; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.security.auth.callback.CallbackHandler; +import org.apache.qpid.amqp_1_0.codec.FrameWriter; +import org.apache.qpid.amqp_1_0.codec.ProtocolHandler; +import org.apache.qpid.amqp_1_0.framing.AMQFrame; +import org.apache.qpid.amqp_1_0.framing.OversizeFrameException; +import org.apache.qpid.amqp_1_0.framing.SASLFrameHandler; +import org.apache.qpid.amqp_1_0.transport.CallbackHandlerSource; +import org.apache.qpid.amqp_1_0.transport.ConnectionEndpoint; +import org.apache.qpid.amqp_1_0.transport.Container; +import org.apache.qpid.amqp_1_0.transport.FrameOutputHandler; +import org.apache.qpid.amqp_1_0.type.Binary; +import org.apache.qpid.amqp_1_0.type.FrameBody; +import org.apache.qpid.protocol.ServerProtocolEngine; +import org.apache.qpid.server.configuration.ConfigStore; +import org.apache.qpid.server.configuration.ConnectionConfigType; +import org.apache.qpid.server.protocol.v1_0.Connection_1_0; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.transport.Sender; +import org.apache.qpid.transport.network.NetworkConnection; + +public class ProtocolEngine_1_0_0_SASL implements ServerProtocolEngine, FrameOutputHandler +{ + private long _readBytes; + private long _writtenBytes; + private final UUID _id; + private final IApplicationRegistry _appRegistry; + private long _createTime = System.currentTimeMillis(); + private ConnectionEndpoint _conn; + private long _connectionId; + + private static final ByteBuffer HEADER = + ByteBuffer.wrap(new byte[] + { + (byte)'A', + (byte)'M', + (byte)'Q', + (byte)'P', + (byte) 3, + (byte) 1, + (byte) 0, + (byte) 0 + }); + + private static final ByteBuffer PROTOCOL_HEADER = + ByteBuffer.wrap(new byte[] + { + (byte)'A', + (byte)'M', + (byte)'Q', + (byte)'P', + (byte) 0, + (byte) 1, + (byte) 0, + (byte) 0 + }); + + + private FrameWriter _frameWriter; + private ProtocolHandler _frameHandler; + private ByteBuffer _buf = ByteBuffer.allocate(1024 * 1024); + private Object _sendLock = new Object(); + private byte _major; + private byte _minor; + private byte _revision; + private PrintWriter _out; + private NetworkConnection _network; + private Sender _sender; + + + static enum State { + A, + M, + Q, + P, + PROTOCOL, + MAJOR, + MINOR, + REVISION, + FRAME + } + + private State _state = State.A; + + + public ProtocolEngine_1_0_0_SASL(final NetworkConnection networkDriver, final IApplicationRegistry appRegistry, + long id) + { + _id = appRegistry.getConfigStore().createId(); + _connectionId = id; + _appRegistry = appRegistry; + + if(networkDriver != null) + { + setNetworkConnection(networkDriver, networkDriver.getSender()); + } + } + + + public SocketAddress getRemoteAddress() + { + return _network.getRemoteAddress(); + } + + public SocketAddress getLocalAddress() + { + return _network.getLocalAddress(); + } + + public long getReadBytes() + { + return _readBytes; + } + + public long getWrittenBytes() + { + return _writtenBytes; + } + + public void writerIdle() + { + //Todo + } + + public void readerIdle() + { + //Todo + } + + public void setNetworkConnection(final NetworkConnection network, final Sender sender) + { + _network = network; + _sender = sender; + + Container container = new Container(_appRegistry.getBrokerId().toString()); + + _conn = new ConnectionEndpoint(container, asCallbackHandlerSource(ApplicationRegistry.getInstance() + .getAuthenticationManager())); + _conn.setConnectionEventListener(new Connection_1_0(_appRegistry)); + _conn.setRemoteAddress(getRemoteAddress()); + + + _conn.setFrameOutputHandler(this); + _conn.setSaslFrameOutput(this); + + _conn.setOnSaslComplete(new Runnable() + { + + + public void run() + { + if(_conn.isAuthenticated()) + { + _sender.send(PROTOCOL_HEADER.duplicate()); + _sender.flush(); + } + else + { + _network.close(); + } + } + }); + _frameWriter = new FrameWriter(_conn.getDescribedTypeRegistry()); + _frameHandler = new SASLFrameHandler(_conn); + + _sender.send(HEADER.duplicate()); + _sender.flush(); + + _conn.initiateSASL(); + + + } + + private CallbackHandlerSource asCallbackHandlerSource(final AuthenticationManager authenticationManager) + { + return new CallbackHandlerSource() + { + @Override + public CallbackHandler getHandler(String mechanism) + { + return authenticationManager.getHandler(mechanism); + } + }; + } + + public String getAddress() + { + return getRemoteAddress().toString(); + } + + + public ConfigStore getConfigStore() + { + return _appRegistry.getConfigStore(); + } + + public UUID getId() + { + return _id; + } + + public ConnectionConfigType getConfigType() + { + return ConnectionConfigType.getInstance(); + } + + public boolean isDurable() + { + return false; + } + + private final Logger RAW_LOGGER = Logger.getLogger("RAW"); + + + public synchronized void received(ByteBuffer msg) + { + if(RAW_LOGGER.isLoggable(Level.FINE)) + { + ByteBuffer dup = msg.duplicate(); + byte[] data = new byte[dup.remaining()]; + dup.get(data); + Binary bin = new Binary(data); + RAW_LOGGER.fine("RECV[" + getRemoteAddress() + "] : " + bin.toString()); + } + _readBytes += msg.remaining(); + switch(_state) + { + case A: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + break; + } + case M: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + _state = State.M; + break; + } + + case Q: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + _state = State.Q; + break; + } + case P: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + _state = State.P; + break; + } + case PROTOCOL: + if(msg.hasRemaining()) + { + msg.get(); + } + else + { + _state = State.PROTOCOL; + break; + } + case MAJOR: + if(msg.hasRemaining()) + { + _major = msg.get(); + } + else + { + _state = State.MAJOR; + break; + } + case MINOR: + if(msg.hasRemaining()) + { + _minor = msg.get(); + } + else + { + _state = State.MINOR; + break; + } + case REVISION: + if(msg.hasRemaining()) + { + _revision = msg.get(); + + _state = State.FRAME; + } + else + { + _state = State.REVISION; + break; + } + case FRAME: + if(msg.hasRemaining()) + { + _frameHandler = _frameHandler.parse(msg); + } + } + + } + + public void exception(Throwable t) + { + t.printStackTrace(); + } + + public void closed() + { + // todo + _conn.inputClosed(); + if(_conn != null && _conn.getConnectionEventListener() != null) + { + ((Connection_1_0)_conn.getConnectionEventListener()).closed(); + } + + } + + public long getCreateTime() + { + return _createTime; + } + + + public boolean canSend() + { + return true; + } + + public void send(final AMQFrame amqFrame) + { + send(amqFrame, null); + } + + private static final Logger FRAME_LOGGER = Logger.getLogger("FRM"); + + + public void send(final AMQFrame amqFrame, ByteBuffer buf) + { + + synchronized(_sendLock) + { + + if(FRAME_LOGGER.isLoggable(Level.FINE)) + { + FRAME_LOGGER.fine("SEND[" + getRemoteAddress() + "|" + amqFrame.getChannel() + "] : " + amqFrame.getFrameBody()); + } + + + _frameWriter.setValue(amqFrame); + + + + ByteBuffer dup = ByteBuffer.allocate(_conn.getMaxFrameSize()); + + int size = _frameWriter.writeToBuffer(dup); + if(size > _conn.getMaxFrameSize()) + { + throw new OversizeFrameException(amqFrame,size); + } + + dup.flip(); + _writtenBytes += dup.limit(); + + if(RAW_LOGGER.isLoggable(Level.FINE)) + { + ByteBuffer dup2 = dup.duplicate(); + byte[] data = new byte[dup2.remaining()]; + dup2.get(data); + Binary bin = new Binary(data); + RAW_LOGGER.fine("SEND[" + getRemoteAddress() + "] : " + bin.toString()); + } + + + _sender.send(dup); + _sender.flush(); + + + } + } + + public void send(short channel, FrameBody body) + { + AMQFrame frame = AMQFrame.createAMQFrame(channel, body); + send(frame); + + } + + public void close() + { + _sender.close(); + } + + public void setLogOutput(final PrintWriter out) + { + _out = out; + } + + public long getConnectionId() + { + return _connectionId; + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Connection_1_0.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Connection_1_0.java new file mode 100644 index 0000000000..318a240b27 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Connection_1_0.java @@ -0,0 +1,98 @@ +/* + * + * 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.transport.ConnectionEventListener; +import org.apache.qpid.amqp_1_0.transport.SessionEndpoint; + +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Connection_1_0 implements ConnectionEventListener +{ + + private IApplicationRegistry _appRegistry; + private VirtualHost _vhost; + + + public static interface Task + { + public void doTask(Connection_1_0 connection); + } + + + private List _closeTasks = + Collections.synchronizedList(new ArrayList()); + + + + public Connection_1_0(IApplicationRegistry appRegistry) + { + _appRegistry = appRegistry; + _vhost = _appRegistry.getVirtualHostRegistry().getDefaultVirtualHost(); + } + + public void remoteSessionCreation(SessionEndpoint endpoint) + { + Session_1_0 session = new Session_1_0(_vhost, _appRegistry, this); + endpoint.setSessionEventListener(session); + } + + + void removeConnectionCloseTask(final Task task) + { + _closeTasks.remove( task ); + } + + void addConnectionCloseTask(final Task task) + { + _closeTasks.add( task ); + } + + public void closeReceived() + { + List taskCopy; + synchronized (_closeTasks) + { + taskCopy = new ArrayList(_closeTasks); + } + for(Task task : taskCopy) + { + task.doTask(this); + } + synchronized (_closeTasks) + { + _closeTasks.clear(); + } + + } + + public void closed() + { + closeReceived(); + } + + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Destination.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Destination.java new file mode 100644 index 0000000000..d45758391c --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Destination.java @@ -0,0 +1,28 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.protocol.v1_0; + + +public interface Destination +{ + + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ExchangeDestination.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ExchangeDestination.java new file mode 100644 index 0000000000..ba1a1ca45c --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ExchangeDestination.java @@ -0,0 +1,108 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.server.protocol.v1_0; + +import java.util.List; +import org.apache.qpid.AMQException; +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.TerminusDurability; +import org.apache.qpid.amqp_1_0.type.messaging.TerminusExpiryPolicy; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.queue.BaseQueue; +import org.apache.qpid.server.txn.ServerTransaction; + +public class ExchangeDestination implements ReceivingDestination, SendingDestination +{ + private static final Accepted ACCEPTED = new Accepted(); + private static final Outcome[] OUTCOMES = { ACCEPTED }; + + private Exchange _exchange; + private TerminusDurability _durability; + private TerminusExpiryPolicy _expiryPolicy; + + public ExchangeDestination(Exchange 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 List queues = _exchange.route(message); + + txn.enqueue(queues,message, new ServerTransaction.Action() + { + + BaseQueue[] _queues = queues.toArray(new BaseQueue[queues.size()]); + + public void postCommit() + { + for(int i = 0; i < _queues.length; i++) + { + try + { + _queues[i].enqueue(message); + } + catch (AMQException e) + { + // TODO + throw new RuntimeException(e); + } + } + } + + public void onRollback() + { + // NO-OP + } + }, System.currentTimeMillis()); + + return ACCEPTED; + } + + TerminusDurability getDurability() + { + return _durability; + } + + TerminusExpiryPolicy getExpiryPolicy() + { + return _expiryPolicy; + } + + public int getCredit() + { + // TODO - fix + return 20000; + } + + public Exchange getExchange() + { + return _exchange; + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/LinkRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/LinkRegistry.java new file mode 100644 index 0000000000..42eea05d37 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/LinkRegistry.java @@ -0,0 +1,59 @@ +package org.apache.qpid.server.protocol.v1_0; + +import java.util.HashMap; +import java.util.Map; + +public class LinkRegistry +{ + private final Map _sendingLinks = new HashMap(); + private final Map _receivingLinks = new HashMap(); + + public synchronized SendingLink_1_0 getDurableSendingLink(String name) + { + return _sendingLinks.get(name); + } + + public synchronized boolean registerSendingLink(String name, SendingLink_1_0 link) + { + if(_sendingLinks.containsKey(name)) + { + return false; + } + else + { + _sendingLinks.put(name, link); + return true; + } + } + + public synchronized boolean unregisterSendingLink(String name) + { + if(!_sendingLinks.containsKey(name)) + { + return false; + } + else + { + _sendingLinks.remove(name); + return true; + } + } + + public synchronized ReceivingLink_1_0 getDurableReceivingLink(String name) + { + return _receivingLinks.get(name); + } + + public synchronized boolean registerReceivingLink(String name, ReceivingLink_1_0 link) + { + if(_receivingLinks.containsKey(name)) + { + return false; + } + else + { + _receivingLinks.put(name, link); + return true; + } + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Link_1_0.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Link_1_0.java new file mode 100644 index 0000000000..db81d3b205 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Link_1_0.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.v1_0; + +public interface Link_1_0 +{ + void start(); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Message_1_0.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Message_1_0.java new file mode 100644 index 0000000000..140a815f57 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Message_1_0.java @@ -0,0 +1,172 @@ +/* + * + * 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.lang.ref.WeakReference; +import java.nio.ByteBuffer; +import java.util.List; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.configuration.SessionConfig; +import org.apache.qpid.server.message.InboundMessage; +import org.apache.qpid.server.message.MessageMetaData_1_0; +import org.apache.qpid.server.message.MessageReference; +import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.store.StoredMessage; + +public class Message_1_0 implements ServerMessage, InboundMessage +{ + private final StoredMessage _storedMessage; + private List _fragments; + private WeakReference _session; + + + public Message_1_0(final StoredMessage storedMessage, + final List fragments, + final Session_1_0 session) + { + _storedMessage = storedMessage; + _fragments = fragments; + _session = new WeakReference(session); + } + + public String getRoutingKey() + { + Object routingKey = getMessageHeader().getHeader("routing-key"); + if(routingKey != null) + { + return routingKey.toString(); + } + else + { + return getMessageHeader().getSubject(); + } + } + + public AMQShortString getRoutingKeyShortString() + { + return AMQShortString.valueOf(getRoutingKey()); + } + + private MessageMetaData_1_0 getMessageMetaData() + { + return _storedMessage.getMetaData(); + } + + public MessageMetaData_1_0.MessageHeader_1_0 getMessageHeader() + { + return getMessageMetaData().getMessageHeader(); + } + + public StoredMessage getStoredMessage() + { + return _storedMessage; + } + + public boolean isPersistent() + { + return getMessageMetaData().isPersistent(); + } + + public boolean isRedelivered() + { + // TODO + return false; + } + + public long getSize() + { + // TODO + return 0l; + } + + public boolean isImmediate() + { + return false; + } + + public long getExpiration() + { + return getMessageHeader().getExpiration(); + } + + public MessageReference newReference() + { + return new Reference(this); + } + + public long getMessageNumber() + { + return _storedMessage.getMessageNumber(); + } + + public long getArrivalTime() + { + return 0; //TODO + } + + public int getContent(final ByteBuffer buf, final int offset) + { + return _storedMessage.getContent(offset, buf); + } + + public ByteBuffer getContent(int offset, int size) + { + ByteBuffer buf = ByteBuffer.allocate(size); + buf.limit(getContent(buf, offset)); + + return buf; + } + + public SessionConfig getSessionConfig() + { + return null; //TODO + } + + public List getFragments() + { + return _fragments; + } + + public Session_1_0 getSession() + { + return _session.get(); + } + + public static class Reference extends MessageReference + { + public Reference(Message_1_0 message) + { + super(message); + } + + protected void onReference(Message_1_0 message) + { + + } + + protected void onRelease(Message_1_0 message) + { + + } + +} +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/QueueDestination.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/QueueDestination.java new file mode 100644 index 0000000000..af3f0b7872 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/QueueDestination.java @@ -0,0 +1,100 @@ +/* + * + * 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.AMQException; +import org.apache.qpid.server.queue.AMQQueue; + +import org.apache.qpid.server.txn.ServerTransaction; + +import java.util.Arrays; + +public class QueueDestination implements SendingDestination, ReceivingDestination +{ + private static final Accepted ACCEPTED = new Accepted(); + private static final Outcome[] OUTCOMES = new Outcome[] { ACCEPTED }; + + + private AMQQueue _queue; + + public QueueDestination(AMQQueue queue) + { + _queue = queue; + } + + public Outcome[] getOutcomes() + { + return OUTCOMES; + } + + public Outcome send(final Message_1_0 message, ServerTransaction txn) + { + + try + { + txn.enqueue(_queue,message, new ServerTransaction.Action() + { + + + public void postCommit() + { + try + { + + _queue.enqueue(message); + } + catch (Exception e) + { + // TODO + throw new RuntimeException(e); + } + + } + + public void onRollback() + { + // NO-OP + } + }); + } + catch(Exception e) + { + e.printStackTrace(); + throw new RuntimeException(e); + } + return ACCEPTED; + } + + public int getCredit() + { + // TODO - fix + return 100; + } + + public AMQQueue getQueue() + { + return _queue; + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingDestination.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingDestination.java new file mode 100644 index 0000000000..4ae0596e25 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingDestination.java @@ -0,0 +1,35 @@ +/* + * + * 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.server.txn.ServerTransaction; + +public interface ReceivingDestination extends Destination +{ + + Outcome[] getOutcomes(); + + Outcome send(Message_1_0 message, ServerTransaction txn); + + int getCredit(); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingLinkAttachment.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingLinkAttachment.java new file mode 100644 index 0000000000..6da5081185 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingLinkAttachment.java @@ -0,0 +1,51 @@ +package org.apache.qpid.server.protocol.v1_0; + +import org.apache.qpid.amqp_1_0.transport.DeliveryStateHandler; +import org.apache.qpid.amqp_1_0.transport.ReceivingLinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.SendingLinkEndpoint; +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.Source; +import org.apache.qpid.amqp_1_0.type.Target; + +public class ReceivingLinkAttachment +{ + private final Session_1_0 _session; + private final ReceivingLinkEndpoint _endpoint; + + public ReceivingLinkAttachment(final Session_1_0 session, final ReceivingLinkEndpoint endpoint) + { + _session = session; + _endpoint = endpoint; + } + + public Session_1_0 getSession() + { + return _session; + } + + public ReceivingLinkEndpoint getEndpoint() + { + return _endpoint; + } + + public Source getSource() + { + return getEndpoint().getSource(); + } + + public void setDeliveryStateHandler(final DeliveryStateHandler handler) + { + getEndpoint().setDeliveryStateHandler(handler); + } + + public void updateDisposition(final Binary deliveryTag, final DeliveryState state, final boolean settled) + { + getEndpoint().updateDisposition(deliveryTag, state, settled); + } + + public Target getTarget() + { + return getEndpoint().getTarget(); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingLink_1_0.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingLink_1_0.java new file mode 100644 index 0000000000..e097dd5c83 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/ReceivingLink_1_0.java @@ -0,0 +1,305 @@ +/* + * + * 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.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.qpid.amqp_1_0.messaging.SectionDecoderImpl; +import org.apache.qpid.amqp_1_0.transport.DeliveryStateHandler; +import org.apache.qpid.amqp_1_0.transport.LinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.ReceivingLinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.ReceivingLinkListener; +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.messaging.Target; +import org.apache.qpid.amqp_1_0.type.messaging.TerminusDurability; +import org.apache.qpid.amqp_1_0.type.transaction.TransactionalState; +import org.apache.qpid.amqp_1_0.type.transport.Detach; +import org.apache.qpid.amqp_1_0.type.transport.ReceiverSettleMode; +import org.apache.qpid.amqp_1_0.type.transport.Transfer; +import org.apache.qpid.server.message.MessageMetaData_1_0; +import org.apache.qpid.server.store.StoredMessage; +import org.apache.qpid.server.txn.AutoCommitTransaction; +import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class ReceivingLink_1_0 implements ReceivingLinkListener, Link_1_0, DeliveryStateHandler +{ + private VirtualHost _vhost; + + private ReceivingDestination _destination; + private SectionDecoderImpl _sectionDecoder; + private volatile ReceivingLinkAttachment _attachment; + + + private ArrayList _incompleteMessage; + private TerminusDurability _durability; + + private Map _unsettledMap = Collections.synchronizedMap(new HashMap()); + private boolean _resumedMessage; + private Binary _messageDeliveryTag; + private ReceiverSettleMode _receivingSettlementMode; + + + public ReceivingLink_1_0(ReceivingLinkAttachment receivingLinkAttachment, VirtualHost vhost, + ReceivingDestination destination) + { + _vhost = vhost; + _destination = destination; + _attachment = receivingLinkAttachment; + receivingLinkAttachment.setDeliveryStateHandler(this); + + _durability = ((Target)receivingLinkAttachment.getTarget()).getDurable(); + + _sectionDecoder = new SectionDecoderImpl(receivingLinkAttachment.getEndpoint().getSession().getConnection().getDescribedTypeRegistry()); + + + } + + public void messageTransfer(Transfer xfr) + { + // TODO - cope with fragmented messages + + List fragments = null; + + + + if(Boolean.TRUE.equals(xfr.getMore()) && _incompleteMessage == null) + { + _incompleteMessage = new ArrayList(); + _incompleteMessage.add(xfr); + _resumedMessage = Boolean.TRUE.equals(xfr.getResume()); + _messageDeliveryTag = xfr.getDeliveryTag(); + return; + } + else if(_incompleteMessage != null) + { + _incompleteMessage.add(xfr); + + if(Boolean.TRUE.equals(xfr.getMore())) + { + return; + } + + fragments = new ArrayList(_incompleteMessage.size()); + for(Transfer t : _incompleteMessage) + { + fragments.add(t.getPayload()); + } + _incompleteMessage=null; + + } + else + { + _resumedMessage = Boolean.TRUE.equals(xfr.getResume()); + _messageDeliveryTag = xfr.getDeliveryTag(); + fragments = Collections.singletonList(xfr.getPayload()); + } + + if(_resumedMessage) + { + if(_unsettledMap.containsKey(_messageDeliveryTag)) + { + Outcome outcome = _unsettledMap.get(_messageDeliveryTag); + boolean settled = ReceiverSettleMode.FIRST.equals(getReceivingSettlementMode()); + getEndpoint().updateDisposition(_messageDeliveryTag, (DeliveryState) outcome, settled); + if(settled) + { + _unsettledMap.remove(_messageDeliveryTag); + } + } + else + { + System.err.println("UNEXPECTED!!"); + System.err.println("Delivery Tag: " + _messageDeliveryTag); + System.err.println("_unsettledMap: " + _unsettledMap); + + } + } + else + { + MessageMetaData_1_0 mmd = null; + List immutableSections = new ArrayList(3); + mmd = new MessageMetaData_1_0(fragments.toArray(new ByteBuffer[fragments.size()]), + _sectionDecoder, + immutableSections); + + StoredMessage storedMessage = _vhost.getMessageStore().addMessage(mmd); + + boolean skipping = true; + int offset = 0; + + for(ByteBuffer bareMessageBuf : immutableSections) + { + storedMessage.addContent(offset, bareMessageBuf.duplicate()); + offset += bareMessageBuf.remaining(); + } + + storedMessage.flushToStore(); + + Message_1_0 message = new Message_1_0(storedMessage, fragments, getSession()); + + + Binary transactionId = null; + org.apache.qpid.amqp_1_0.type.DeliveryState xfrState = xfr.getState(); + if(xfrState != null) + { + if(xfrState instanceof TransactionalState) + { + transactionId = ((TransactionalState)xfrState).getTxnId(); + } + } + + ServerTransaction transaction = null; + if(transactionId != null) + { + transaction = getSession().getTransaction(transactionId); + } + else + { + Session_1_0 session = getSession(); + transaction = session != null ? session.getTransaction(null) : new AutoCommitTransaction(_vhost.getMessageStore()); + } + + Outcome outcome = _destination.send(message, transaction); + + DeliveryState resultantState; + + if(transactionId == null) + { + resultantState = (DeliveryState) outcome; + } + else + { + TransactionalState transactionalState = new TransactionalState(); + transactionalState.setOutcome(outcome); + transactionalState.setTxnId(transactionId); + resultantState = transactionalState; + + } + + + boolean settled = transaction instanceof AutoCommitTransaction && ReceiverSettleMode.FIRST.equals(getReceivingSettlementMode()); + + final Binary deliveryTag = xfr.getDeliveryTag(); + + if(!settled) + { + _unsettledMap.put(deliveryTag, outcome); + } + + getEndpoint().updateDisposition(deliveryTag, resultantState, settled); + + if(!(transaction instanceof AutoCommitTransaction)) + { + ServerTransaction.Action a; + transaction.addPostTransactionAction(new ServerTransaction.Action() + { + public void postCommit() + { + getEndpoint().updateDisposition(deliveryTag, null, true); + } + + public void onRollback() + { + getEndpoint().updateDisposition(deliveryTag, null, true); + } + }); + } + } + } + + private ReceiverSettleMode getReceivingSettlementMode() + { + return _receivingSettlementMode; + } + + public void remoteDetached(LinkEndpoint endpoint, Detach detach) + { + //TODO + // if not durable or close + if(!TerminusDurability.UNSETTLED_STATE.equals(_durability) || + (detach != null && Boolean.TRUE.equals(detach.getClosed()))) + { + endpoint.close(); + } + else if(detach == null || detach.getError() != null) + { + _attachment = null; + } + } + + public void start() + { + getEndpoint().setLinkCredit(UnsignedInteger.valueOf(_destination.getCredit())); + getEndpoint().setCreditWindow(); + } + + public ReceivingLinkEndpoint getEndpoint() + { + return _attachment.getEndpoint(); + } + + + public Session_1_0 getSession() + { + ReceivingLinkAttachment attachment = _attachment; + return attachment == null ? null : attachment.getSession(); + } + + public void handle(Binary deliveryTag, DeliveryState state, Boolean settled) + { + if(Boolean.TRUE.equals(settled)) + { + _unsettledMap.remove(deliveryTag); + } + } + + public void setLinkAttachment(ReceivingLinkAttachment linkAttachment) + { + _attachment = linkAttachment; + _receivingSettlementMode = linkAttachment.getEndpoint().getReceivingSettlementMode(); + ReceivingLinkEndpoint endpoint = linkAttachment.getEndpoint(); + Map initialUnsettledMap = endpoint.getInitialUnsettledMap(); + + Map unsettledCopy = new HashMap(_unsettledMap); + for(Map.Entry entry : unsettledCopy.entrySet()) + { + Binary deliveryTag = entry.getKey(); + if(!initialUnsettledMap.containsKey(deliveryTag)) + { + _unsettledMap.remove(deliveryTag); + } + } + + } + + public Map getUnsettledOutcomeMap() + { + return _unsettledMap; + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingDestination.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingDestination.java new file mode 100644 index 0000000000..6d601c9dda --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingDestination.java @@ -0,0 +1,27 @@ +/* + * + * 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; + + +public interface SendingDestination extends Destination +{ + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLinkAttachment.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLinkAttachment.java new file mode 100644 index 0000000000..9d7af24135 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLinkAttachment.java @@ -0,0 +1,44 @@ +package org.apache.qpid.server.protocol.v1_0; + +import org.apache.qpid.amqp_1_0.transport.DeliveryStateHandler; +import org.apache.qpid.amqp_1_0.transport.SendingLinkEndpoint; +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.Source; + +public class SendingLinkAttachment +{ + private final Session_1_0 _session; + private final SendingLinkEndpoint _endpoint; + + public SendingLinkAttachment(final Session_1_0 session, final SendingLinkEndpoint endpoint) + { + _session = session; + _endpoint = endpoint; + } + + public Session_1_0 getSession() + { + return _session; + } + + public SendingLinkEndpoint getEndpoint() + { + return _endpoint; + } + + public Source getSource() + { + return getEndpoint().getSource(); + } + + public void setDeliveryStateHandler(final DeliveryStateHandler handler) + { + getEndpoint().setDeliveryStateHandler(handler); + } + + public void updateDisposition(final Binary deliveryTag, final DeliveryState state, final boolean settled) + { + getEndpoint().updateDisposition(deliveryTag, state, settled); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLink_1_0.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLink_1_0.java new file mode 100644 index 0000000000..b3e9a74d04 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/SendingLink_1_0.java @@ -0,0 +1,648 @@ +/* + * + * 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.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQInternalException; +import org.apache.qpid.AMQSecurityException; +import org.apache.qpid.amqp_1_0.transport.DeliveryStateHandler; +import org.apache.qpid.amqp_1_0.transport.LinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.SendingLinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.SendingLinkListener; +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.Symbol; +import org.apache.qpid.amqp_1_0.type.UnsignedInteger; +import org.apache.qpid.amqp_1_0.type.messaging.Accepted; +import org.apache.qpid.amqp_1_0.type.messaging.ExactSubjectFilter; +import org.apache.qpid.amqp_1_0.type.messaging.Filter; +import org.apache.qpid.amqp_1_0.type.messaging.MatchingSubjectFilter; +import org.apache.qpid.amqp_1_0.type.messaging.Modified; +import org.apache.qpid.amqp_1_0.type.messaging.NoLocalFilter; +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.messaging.TerminusDurability; +import org.apache.qpid.amqp_1_0.type.transport.AmqpError; +import org.apache.qpid.amqp_1_0.type.transport.Detach; +import org.apache.qpid.amqp_1_0.type.transport.Error; +import org.apache.qpid.amqp_1_0.type.transport.Transfer; +import org.apache.qpid.filter.SelectorParsingException; +import org.apache.qpid.filter.selector.ParseException; +import org.apache.qpid.server.binding.Binding; +import org.apache.qpid.server.exchange.DirectExchange; +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.model.UUIDGenerator; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.txn.AutoCommitTransaction; +import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class SendingLink_1_0 implements SendingLinkListener, Link_1_0, DeliveryStateHandler +{ + private VirtualHost _vhost; + private SendingDestination _destination; + + private Subscription_1_0 _subscription; + private boolean _draining; + 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 _resumeAcceptedTransfers = new ArrayList(); + private Runnable _closeAction; + + public SendingLink_1_0(final SendingLinkAttachment linkAttachment, + final VirtualHost vhost, + final SendingDestination destination) + throws AmqpErrorException + { + _vhost = vhost; + _destination = destination; + _linkAttachment = linkAttachment; + final Source source = (Source) linkAttachment.getSource(); + _durability = source.getDurable(); + linkAttachment.setDeliveryStateHandler(this); + QueueDestination qd = null; + AMQQueue queue = null; + + + + boolean noLocal = false; + JMSSelectorFilter messageFilter = null; + + if(destination instanceof QueueDestination) + { + queue = ((QueueDestination) _destination).getQueue(); + if(queue.getArguments() != null && queue.getArguments().containsKey("topic")) + { + source.setDistributionMode(StdDistMode.COPY); + } + qd = (QueueDestination) destination; + + Map filters = source.getFilter(); + + Map actualFilters = new HashMap(); + + if(filters != null) + { + for(Map.Entry entry : filters.entrySet()) + { + if(entry.getValue() instanceof NoLocalFilter) + { + actualFilters.put(entry.getKey(), entry.getValue()); + noLocal = true; + } + else if(messageFilter == null && entry.getValue() instanceof org.apache.qpid.amqp_1_0.type.messaging.JMSSelectorFilter) + { + + org.apache.qpid.amqp_1_0.type.messaging.JMSSelectorFilter selectorFilter = (org.apache.qpid.amqp_1_0.type.messaging.JMSSelectorFilter) entry.getValue(); + try + { + messageFilter = new JMSSelectorFilter(selectorFilter.getValue()); + + actualFilters.put(entry.getKey(), entry.getValue()); + } + catch (ParseException e) + { + Error error = new Error(); + error.setCondition(AmqpError.INVALID_FIELD); + error.setDescription("Invalid JMS Selector: " + selectorFilter.getValue()); + error.setInfo(Collections.singletonMap(Symbol.valueOf("field"), Symbol.valueOf("filter"))); + throw new AmqpErrorException(error); + } + catch (SelectorParsingException e) + { + Error error = new Error(); + error.setCondition(AmqpError.INVALID_FIELD); + error.setDescription("Invalid JMS Selector: " + selectorFilter.getValue()); + error.setInfo(Collections.singletonMap(Symbol.valueOf("field"), Symbol.valueOf("filter"))); + throw new AmqpErrorException(error); + } + + + } + } + } + source.setFilter(actualFilters.isEmpty() ? null : actualFilters); + + _subscription = new Subscription_1_0(this, qd); + } + else if(destination instanceof ExchangeDestination) + { + try + { + + ExchangeDestination exchangeDestination = (ExchangeDestination) destination; + + boolean isDurable = exchangeDestination.getDurability() == TerminusDurability.CONFIGURATION + || exchangeDestination.getDurability() == TerminusDurability.UNSETTLED_STATE; + String name; + if(isDurable) + { + String remoteContainerId = getEndpoint().getSession().getConnection().getRemoteContainerId(); + remoteContainerId = remoteContainerId.replace("_","__").replace(".", "_:"); + + String endpointName = linkAttachment.getEndpoint().getName(); + endpointName = endpointName + .replace("_", "__") + .replace(".", "_:") + .replace("(", "_O") + .replace(")", "_C") + .replace("<", "_L") + .replace(">", "_R"); + name = "qpid_/" + remoteContainerId + "_/" + endpointName; + } + else + { + name = UUID.randomUUID().toString(); + } + + queue = _vhost.getQueueRegistry().getQueue(name); + Exchange exchange = exchangeDestination.getExchange(); + + if(queue == null) + { + queue = AMQQueueFactory.createAMQQueueImpl( + UUIDGenerator.generateUUID(), + name, + isDurable, + null, + true, + true, + _vhost, + Collections.EMPTY_MAP); + } + else + { + List bindings = queue.getBindings(); + List bindingsToRemove = new ArrayList(); + for(Binding existingBinding : bindings) + { + if(existingBinding.getExchange() != _vhost.getExchangeRegistry().getDefaultExchange() + && existingBinding.getExchange() != exchange) + { + bindingsToRemove.add(existingBinding); + } + } + for(Binding existingBinding : bindingsToRemove) + { + existingBinding.getExchange().removeBinding(existingBinding); + } + } + + + String binding = ""; + + Map filters = source.getFilter(); + Map actualFilters = new HashMap(); + boolean hasBindingFilter = false; + if(filters != null && !filters.isEmpty()) + { + + for(Map.Entry entry : filters.entrySet()) + { + if(!hasBindingFilter + && entry.getValue() instanceof ExactSubjectFilter + && exchange.getType() == DirectExchange.TYPE) + { + ExactSubjectFilter filter = (ExactSubjectFilter) filters.values().iterator().next(); + source.setFilter(filters); + binding = filter.getValue(); + actualFilters.put(entry.getKey(), entry.getValue()); + hasBindingFilter = true; + } + else if(!hasBindingFilter + && entry.getValue() instanceof MatchingSubjectFilter + && exchange.getType() == TopicExchange.TYPE) + { + MatchingSubjectFilter filter = (MatchingSubjectFilter) filters.values().iterator().next(); + source.setFilter(filters); + binding = filter.getValue(); + actualFilters.put(entry.getKey(), entry.getValue()); + hasBindingFilter = true; + } + else if(entry.getValue() instanceof NoLocalFilter) + { + actualFilters.put(entry.getKey(), entry.getValue()); + noLocal = true; + } + else if(messageFilter == null && entry.getValue() instanceof org.apache.qpid.amqp_1_0.type.messaging.JMSSelectorFilter) + { + + org.apache.qpid.amqp_1_0.type.messaging.JMSSelectorFilter selectorFilter = (org.apache.qpid.amqp_1_0.type.messaging.JMSSelectorFilter) entry.getValue(); + try + { + messageFilter = new JMSSelectorFilter(selectorFilter.getValue()); + + actualFilters.put(entry.getKey(), entry.getValue()); + } + catch (ParseException e) + { + Error error = new Error(); + error.setCondition(AmqpError.INVALID_FIELD); + error.setDescription("Invalid JMS Selector: " + selectorFilter.getValue()); + error.setInfo(Collections.singletonMap(Symbol.valueOf("field"), Symbol.valueOf("filter"))); + throw new AmqpErrorException(error); + } + catch (SelectorParsingException e) + { + Error error = new Error(); + error.setCondition(AmqpError.INVALID_FIELD); + error.setDescription("Invalid JMS Selector: " + selectorFilter.getValue()); + error.setInfo(Collections.singletonMap(Symbol.valueOf("field"), Symbol.valueOf("filter"))); + throw new AmqpErrorException(error); + } + + + } + } + } + source.setFilter(actualFilters.isEmpty() ? null : actualFilters); + + vhost.getBindingFactory().addBinding(binding,queue,exchange,null); + source.setDistributionMode(StdDistMode.COPY); + + qd = new QueueDestination(queue); + } + catch (AMQSecurityException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + catch (AMQInternalException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } catch (AMQException e) + { + e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + } + _subscription = new Subscription_1_0(this, qd, true); + + } + + if(_subscription != null) + { + _subscription.setNoLocal(noLocal); + if(messageFilter!=null) + { + _subscription.setFilters(new SimpleFilterManager(messageFilter)); + } + + try + { + + queue.registerSubscription(_subscription, false); + } + catch (AMQException e) + { + e.printStackTrace(); //TODO + } + } + + } + + public void resume(SendingLinkAttachment linkAttachment) + { + _linkAttachment = linkAttachment; + + } + + public void remoteDetached(final LinkEndpoint endpoint, final Detach detach) + { + //TODO + // if not durable or close + if(!TerminusDurability.UNSETTLED_STATE.equals(_durability)) + { + AMQQueue queue = _subscription.getQueue(); + + try + { + + queue.unregisterSubscription(_subscription); + + } + catch (AMQException e) + { + e.printStackTrace(); //TODO + } + + Modified state = new Modified(); + state.setDeliveryFailed(true); + + for(UnsettledAction action : _unsettledActionMap.values()) + { + + action.process(state,Boolean.TRUE); + } + _unsettledActionMap.clear(); + + endpoint.close(); + + if(_destination instanceof ExchangeDestination + && (_durability == TerminusDurability.CONFIGURATION + || _durability == TerminusDurability.UNSETTLED_STATE)) + { + try + { + queue.delete(); + } + catch(AMQException e) + { + e.printStackTrace(); // TODO - Implement + } + } + + if(_closeAction != null) + { + _closeAction.run(); + } + } + else if(detach == null || detach.getError() != null) + { + _linkAttachment = null; + _subscription.flowStateChanged(); + } + else + { + endpoint.detach(); + } + } + + public void start() + { + //TODO + } + + public SendingLinkEndpoint getEndpoint() + { + return _linkAttachment == null ? null : _linkAttachment.getEndpoint() ; + } + + public Session_1_0 getSession() + { + return _linkAttachment == null ? null : _linkAttachment.getSession(); + } + + public void flowStateChanged() + { + if(Boolean.TRUE.equals(getEndpoint().getDrain()) + && hasCredit()) + { + _draining = true; + } + + while(!_resumeAcceptedTransfers.isEmpty() && getEndpoint().hasCreditToSend()) + { + Accepted accepted = new Accepted(); + synchronized(getLock()) + { + + Transfer xfr = new Transfer(); + Binary dt = _resumeAcceptedTransfers.remove(0); + xfr.setDeliveryTag(dt); + xfr.setState(accepted); + xfr.setResume(Boolean.TRUE); + getEndpoint().transfer(xfr); + } + + } + if(_resumeAcceptedTransfers.isEmpty()) + { + _subscription.flowStateChanged(); + } + + } + + boolean hasCredit() + { + return getEndpoint().getLinkCredit().compareTo(UnsignedInteger.ZERO) > 0; + } + + public boolean isDraining() + { + return false; //TODO + } + + public boolean drained() + { + if(getEndpoint() != null) + { + synchronized(getEndpoint().getLock()) + { + if(_draining) + { + //TODO + getEndpoint().drained(); + _draining = false; + return true; + } + else + { + return false; + } + } + } + else + { + return false; + } + } + + public void addUnsettled(Binary tag, UnsettledAction unsettledAction, QueueEntry queueEntry) + { + _unsettledActionMap.put(tag,unsettledAction); + if(getTransactionId() == null) + { + _unsettledMap.put(tag, queueEntry); + } + } + + public void removeUnsettled(Binary tag) + { + _unsettledActionMap.remove(tag); + } + + public void handle(Binary deliveryTag, DeliveryState state, Boolean settled) + { + UnsettledAction action = _unsettledActionMap.get(deliveryTag); + boolean localSettle = false; + if(action != null) + { + localSettle = action.process(state, settled); + if(localSettle && !Boolean.TRUE.equals(settled)) + { + _linkAttachment.updateDisposition(deliveryTag, state, true); + } + } + if(Boolean.TRUE.equals(settled) || localSettle) + { + _unsettledActionMap.remove(deliveryTag); + _unsettledMap.remove(deliveryTag); + } + } + + ServerTransaction getTransaction(Binary transactionId) + { + return _linkAttachment.getSession().getTransaction(transactionId); + } + + public Binary getTransactionId() + { + SendingLinkEndpoint endpoint = getEndpoint(); + return endpoint == null ? null : endpoint.getTransactionId(); + } + + public synchronized Object getLock() + { + return _linkAttachment == null ? this : getEndpoint().getLock(); + } + + public boolean isDetached() + { + return _linkAttachment == null || getEndpoint().isDetached(); + } + + public boolean isAttached() + { + return _linkAttachment != null && getEndpoint().isAttached(); + } + + public synchronized void setLinkAttachment(SendingLinkAttachment linkAttachment) + { + + if(_subscription.isActive()) + { + _subscription.suspend(); + } + + _linkAttachment = linkAttachment; + + SendingLinkEndpoint endpoint = linkAttachment.getEndpoint(); + endpoint.setDeliveryStateHandler(this); + Map initialUnsettledMap = endpoint.getInitialUnsettledMap(); + Map unsettledCopy = new HashMap(_unsettledMap); + _resumeAcceptedTransfers.clear(); + _resumeFullTransfers.clear(); + + for(Map.Entry entry : unsettledCopy.entrySet()) + { + Binary deliveryTag = entry.getKey(); + final QueueEntry queueEntry = entry.getValue(); + if(initialUnsettledMap == null || !initialUnsettledMap.containsKey(deliveryTag)) + { + queueEntry.setRedelivered(); + queueEntry.release(); + _unsettledMap.remove(deliveryTag); + } + else if(initialUnsettledMap != null && (initialUnsettledMap.get(deliveryTag) instanceof Outcome)) + { + Outcome outcome = (Outcome) initialUnsettledMap.get(deliveryTag); + + if(outcome instanceof Accepted) + { + AutoCommitTransaction txn = new AutoCommitTransaction(_vhost.getMessageStore()); + if(_subscription.acquires()) + { + txn.dequeue(Collections.singleton(queueEntry), + new ServerTransaction.Action() + { + public void postCommit() + { + queueEntry.discard(); + } + + public void onRollback() + { + //To change body of implemented methods use File | Settings | File Templates. + } + }); + } + } + else if(outcome instanceof Released) + { + AutoCommitTransaction txn = new AutoCommitTransaction(_vhost.getMessageStore()); + if(_subscription.acquires()) + { + txn.dequeue(Collections.singleton(queueEntry), + new ServerTransaction.Action() + { + public void postCommit() + { + queueEntry.release(); + } + + public void onRollback() + { + //To change body of implemented methods use File | Settings | File Templates. + } + }); + } + } + //_unsettledMap.remove(deliveryTag); + initialUnsettledMap.remove(deliveryTag); + _resumeAcceptedTransfers.add(deliveryTag); + } + else + { + _resumeFullTransfers.add(queueEntry); + // exists in receivers map, but not yet got an outcome ... should resend with resume = true + } + // TODO - else + } + + + } + + public Map getUnsettledOutcomeMap() + { + Map unsettled = new HashMap(_unsettledMap); + + for(Map.Entry entry : unsettled.entrySet()) + { + entry.setValue(null); + } + + return unsettled; + } + + public void setCloseAction(Runnable action) + { + _closeAction = action; + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Session_1_0.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Session_1_0.java new file mode 100644 index 0000000000..48a551e42a --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Session_1_0.java @@ -0,0 +1,446 @@ +/* + * + * 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.transport.LinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.ReceivingLinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.SendingLinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.SessionEventListener; +import org.apache.qpid.amqp_1_0.type.*; +import org.apache.qpid.amqp_1_0.type.messaging.*; +import org.apache.qpid.amqp_1_0.type.messaging.Source; +import org.apache.qpid.amqp_1_0.type.messaging.Target; +import org.apache.qpid.amqp_1_0.type.transaction.Coordinator; +import org.apache.qpid.amqp_1_0.type.transaction.TxnCapability; +import org.apache.qpid.amqp_1_0.type.transport.*; + +import org.apache.qpid.amqp_1_0.type.transport.Error; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQSecurityException; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.model.UUIDGenerator; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.txn.AutoCommitTransaction; +import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import java.util.*; + +public class Session_1_0 implements SessionEventListener +{ + private static final Symbol LIFETIME_POLICY = Symbol.valueOf("lifetime-policy"); + private IApplicationRegistry _appRegistry; + private VirtualHost _vhost; + private AutoCommitTransaction _transaction; + + private final LinkedHashMap _openTransactions = + new LinkedHashMap(); + private final Connection_1_0 _connection; + + + public Session_1_0(VirtualHost vhost, IApplicationRegistry appRegistry, final Connection_1_0 connection) + { + _appRegistry = appRegistry; + _vhost = vhost; + _transaction = new AutoCommitTransaction(vhost.getMessageStore()); + _connection = connection; + + } + + public void remoteLinkCreation(final LinkEndpoint endpoint) + { + + + Destination destination; + Link_1_0 link = null; + Error error = null; + + final LinkRegistry linkRegistry = _vhost.getLinkRegistry(endpoint.getSession().getConnection().getRemoteContainerId()); + + + if(endpoint.getRole() == Role.SENDER) + { + + SendingLink_1_0 previousLink = linkRegistry.getDurableSendingLink(endpoint.getName()); + + if(previousLink == null) + { + + Target target = (Target) endpoint.getTarget(); + Source source = (Source) endpoint.getSource(); + + + if(source != null) + { + if(Boolean.TRUE.equals(source.getDynamic())) + { + AMQQueue tempQueue = createTemporaryQueue(source.getDynamicNodeProperties()); + source.setAddress(tempQueue.getName()); + } + String addr = source.getAddress(); + AMQQueue queue = _vhost.getQueueRegistry().getQueue(addr); + if(queue != null) + { + + destination = new QueueDestination(queue); + + + + } + else + { + Exchange exchg = _vhost.getExchangeRegistry().getExchange(addr); + if(exchg != null) + { + destination = new ExchangeDestination(exchg, source.getDurable(), source.getExpiryPolicy()); + } + else + { + + endpoint.setSource(null); + destination = null; + } + } + + } + else + { + destination = null; + } + + if(destination != null) + { + final SendingLinkEndpoint sendingLinkEndpoint = (SendingLinkEndpoint) endpoint; + try + { + final SendingLink_1_0 sendingLink = new SendingLink_1_0(new SendingLinkAttachment(this, sendingLinkEndpoint), + _vhost, + (SendingDestination) destination + ); + sendingLinkEndpoint.setLinkEventListener(sendingLink); + link = sendingLink; + if(TerminusDurability.UNSETTLED_STATE.equals(source.getDurable())) + { + linkRegistry.registerSendingLink(endpoint.getName(), sendingLink); + } + } + catch(AmqpErrorException e) + { + e.printStackTrace(); + destination = null; + sendingLinkEndpoint.setSource(null); + error = e.getError(); + } + } + } + else + { + Source newSource = (Source) endpoint.getSource(); + + Source oldSource = (Source) previousLink.getEndpoint().getSource(); + final TerminusDurability newSourceDurable = newSource == null ? null : newSource.getDurable(); + if(newSourceDurable != null) + { + oldSource.setDurable(newSourceDurable); + if(newSourceDurable.equals(TerminusDurability.NONE)) + { + linkRegistry.unregisterSendingLink(endpoint.getName()); + } + } + endpoint.setSource(oldSource); + SendingLinkEndpoint sendingLinkEndpoint = (SendingLinkEndpoint) endpoint; + previousLink.setLinkAttachment(new SendingLinkAttachment(this, sendingLinkEndpoint)); + sendingLinkEndpoint.setLinkEventListener(previousLink); + link = previousLink; + endpoint.setLocalUnsettled(previousLink.getUnsettledOutcomeMap()); + } + } + else + { + if(endpoint.getTarget() instanceof Coordinator) + { + Coordinator coordinator = (Coordinator) endpoint.getTarget(); + TxnCapability[] capabilities = coordinator.getCapabilities(); + boolean localTxn = false; + boolean multiplePerSession = false; + if(capabilities != null) + { + for(TxnCapability capability : capabilities) + { + if(capability.equals(TxnCapability.LOCAL_TXN)) + { + localTxn = true; + } + else if(capability.equals(TxnCapability.MULTI_TXNS_PER_SSN)) + { + multiplePerSession = true; + } + else + { + error = new Error(); + error.setCondition(AmqpError.NOT_IMPLEMENTED); + error.setDescription("Unsupported capability: " + capability); + break; + } + } + } + + /* if(!localTxn) + { + capabilities.add(TxnCapabilities.LOCAL_TXN); + }*/ + + final ReceivingLinkEndpoint receivingLinkEndpoint = (ReceivingLinkEndpoint) endpoint; + final TxnCoordinatorLink_1_0 coordinatorLink = + new TxnCoordinatorLink_1_0(_vhost, this, receivingLinkEndpoint, _openTransactions); + receivingLinkEndpoint.setLinkEventListener(coordinatorLink); + link = coordinatorLink; + + + } + else + { + + ReceivingLink_1_0 previousLink = linkRegistry.getDurableReceivingLink(endpoint.getName()); + + if(previousLink == null) + { + + Target target = (Target) endpoint.getTarget(); + + if(target != null) + { + if(Boolean.TRUE.equals(target.getDynamic())) + { + + AMQQueue tempQueue = createTemporaryQueue(target.getDynamicNodeProperties()); + target.setAddress(tempQueue.getName()); + } + + String addr = target.getAddress(); + Exchange exchg = _vhost.getExchangeRegistry().getExchange(addr); + if(exchg != null) + { + destination = new ExchangeDestination(exchg, target.getDurable(), + target.getExpiryPolicy()); + } + else + { + AMQQueue queue = _vhost.getQueueRegistry().getQueue(addr); + if(queue != null) + { + + destination = new QueueDestination(queue); + } + else + { + endpoint.setTarget(null); + destination = null; + } + + } + + + } + else + { + destination = null; + } + if(destination != null) + { + final ReceivingLinkEndpoint receivingLinkEndpoint = (ReceivingLinkEndpoint) endpoint; + final ReceivingLink_1_0 receivingLink = new ReceivingLink_1_0(new ReceivingLinkAttachment(this, receivingLinkEndpoint), _vhost, + (ReceivingDestination) destination); + receivingLinkEndpoint.setLinkEventListener(receivingLink); + link = receivingLink; + if(TerminusDurability.UNSETTLED_STATE.equals(target.getDurable())) + { + linkRegistry.registerReceivingLink(endpoint.getName(), receivingLink); + } + } + } + else + { + ReceivingLinkEndpoint receivingLinkEndpoint = (ReceivingLinkEndpoint) endpoint; + previousLink.setLinkAttachment(new ReceivingLinkAttachment(this, receivingLinkEndpoint)); + receivingLinkEndpoint.setLinkEventListener(previousLink); + link = previousLink; + endpoint.setLocalUnsettled(previousLink.getUnsettledOutcomeMap()); + + } + } + } + + endpoint.attach(); + + if(link == null) + { + if(error == null) + { + error = new Error(); + error.setCondition(AmqpError.NOT_FOUND); + } + endpoint.detach(error); + } + else + { + link.start(); + } + } + + + private AMQQueue createTemporaryQueue(Map properties) + { + final String queueName = UUID.randomUUID().toString(); + AMQQueue queue = null; + try + { + LifetimePolicy lifetimePolicy = properties == null + ? null + : (LifetimePolicy) properties.get(LIFETIME_POLICY); + + final AMQQueue tempQueue = queue = AMQQueueFactory.createAMQQueueImpl( UUIDGenerator.generateUUID(), + queueName, + false, // durable + null, // owner + false, // autodelete + false, // exclusive + _vhost, + properties); + + + + if (lifetimePolicy == null || lifetimePolicy instanceof DeleteOnClose) + { + final Connection_1_0.Task deleteQueueTask = + new Connection_1_0.Task() + { + public void doTask(Connection_1_0 session) + { + if (_vhost.getQueueRegistry().getQueue(queueName) == tempQueue) + { + try + { + tempQueue.delete(); + } + catch (AMQException e) + { + e.printStackTrace(); //TODO. + } + } + } + }; + + _connection.addConnectionCloseTask(deleteQueueTask); + + queue.addQueueDeleteTask(new AMQQueue.Task() + { + public void doTask(AMQQueue queue) + { + _connection.removeConnectionCloseTask(deleteQueueTask); + } + + + }); + } + else if(lifetimePolicy instanceof DeleteOnNoLinks) + { + + } + else if(lifetimePolicy instanceof DeleteOnNoMessages) + { + + } + else if(lifetimePolicy instanceof DeleteOnNoLinksOrMessages) + { + + } + } + catch (AMQSecurityException e) + { + e.printStackTrace(); //TODO. + } catch (AMQException e) + { + e.printStackTrace(); //TODO + } + + return queue; + } + + public ServerTransaction getTransaction(Binary transactionId) + { + // TODO should treat invalid id differently to null + ServerTransaction transaction = _openTransactions.get(binaryToInteger(transactionId)); + return transaction == null ? _transaction : transaction; + } + + public void remoteEnd(End end) + { + Iterator> iter = _openTransactions.entrySet().iterator(); + + while(iter.hasNext()) + { + Map.Entry entry = iter.next(); + entry.getValue().rollback(); + iter.remove(); + } + + } + + Integer binaryToInteger(final Binary txnId) + { + if(txnId == null) + { + return null; + } + + if(txnId.getLength() > 4) + throw new IllegalArgumentException(); + + int id = 0; + byte[] data = txnId.getArray(); + for(int i = 0; i < txnId.getLength(); i++) + { + id <<= 8; + id += data[i+txnId.getArrayOffset()]; + } + + return id; + + } + + Binary integerToBinary(final int txnId) + { + byte[] data = new byte[4]; + data[3] = (byte) (txnId & 0xff); + data[2] = (byte) ((txnId & 0xff00) >> 8); + data[1] = (byte) ((txnId & 0xff0000) >> 16); + data[0] = (byte) ((txnId & 0xff000000) >> 24); + return new Binary(data); + + } + + public void forceEnd() + { + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java new file mode 100644 index 0000000000..08926d000d --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/Subscription_1_0.java @@ -0,0 +1,634 @@ +/* + * + * 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.filter.FilterManager; +import org.apache.qpid.server.logging.LogActor; +import org.apache.qpid.server.message.ServerMessage; +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 QueueEntry.SubscriptionAssignedState _assignedState = new QueueEntry.SubscriptionAssignedState(this); + private final long _id; + private final boolean _acquires; + private 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 QueueEntry.SubscriptionAssignedState getAssignedState() + { + return _assignedState; + } + + public void setQueue(final AMQQueue queue, final boolean exclusive) + { + //TODO + } + + public void setNoLocal(final boolean noLocal) + { + _noLocal = noLocal; + } + + public boolean isNoLocal() + { + return _noLocal; + } + + public long getSubscriptionID() + { + return _id; + } + + public boolean isSuspended() + { + return !isActive();// || !getEndpoint().hasCreditToSend(); + + } + + public boolean hasInterest(final QueueEntry entry) + { + return !(_noLocal && (entry.getMessage() instanceof Message_1_0) + && ((Message_1_0)entry.getMessage()).getSession() == getSession()) + && checkFilters(entry); + + } + + private boolean checkFilters(final QueueEntry entry) + { + return (_filters == null) || _filters.allAllow(entry); + } + + 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 + { + //TODO + ServerMessage serverMessage = queueEntry.getMessage(); + if(serverMessage instanceof Message_1_0) + { + Message_1_0 message = (Message_1_0) serverMessage; + 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); + + + } + } + }); + } + + } + + getEndpoint().transfer(transfer); + } + else + { + queueEntry.release(); + } + } + } + + } + + public void queueDeleted(final AMQQueue queue) + { + //TODO + getEndpoint().setSource(null); + getEndpoint().detach(); + } + + public synchronized boolean wouldSuspend(final QueueEntry msg) + { + final boolean hasCredit = _link.isAttached() && getEndpoint().hasCreditToSend(); + if(!hasCredit && getState() == State.ACTIVE) + { + suspend(); + } + + return !hasCredit; + } + + public boolean trySendLock() + { + return _stateChangeLock.tryLock(); + } + + public synchronized void suspend() + { + 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 synchronized void queueEmpty() + { + if(_link.drained()) + { + if(_state.compareAndSet(State.ACTIVE, State.SUSPENDED)) + { + _stateListener.stateChange(this, State.ACTIVE, State.SUSPENDED); + } + } + } + + public synchronized void flowStateChanged() + { + 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.discard(); + } + } + + 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; + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/TxnCoordinatorLink_1_0.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/TxnCoordinatorLink_1_0.java new file mode 100644 index 0000000000..a05d14816a --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/TxnCoordinatorLink_1_0.java @@ -0,0 +1,195 @@ +/* + * + * 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.messaging.SectionDecoder; +import org.apache.qpid.amqp_1_0.messaging.SectionDecoderImpl; +import org.apache.qpid.amqp_1_0.transport.LinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.ReceivingLinkEndpoint; +import org.apache.qpid.amqp_1_0.transport.ReceivingLinkListener; +import org.apache.qpid.amqp_1_0.type.*; +import org.apache.qpid.amqp_1_0.type.DeliveryState; +import org.apache.qpid.amqp_1_0.type.messaging.*; +import org.apache.qpid.amqp_1_0.type.transaction.Declare; +import org.apache.qpid.amqp_1_0.type.transaction.Declared; +import org.apache.qpid.amqp_1_0.type.transaction.Discharge; +import org.apache.qpid.amqp_1_0.type.transport.*; +import org.apache.qpid.amqp_1_0.type.transport.Error; +import org.apache.qpid.server.txn.LocalTransaction; +import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.virtualhost.VirtualHost; + +import java.nio.ByteBuffer; +import java.util.*; + +public class TxnCoordinatorLink_1_0 implements ReceivingLinkListener, Link_1_0 +{ + private VirtualHost _vhost; + private ReceivingLinkEndpoint _endpoint; + + private ArrayList _incompleteMessage; + private SectionDecoder _sectionDecoder; + private LinkedHashMap _openTransactions; + private Session_1_0 _session; + + + public TxnCoordinatorLink_1_0(VirtualHost vhost, + Session_1_0 session_1_0, ReceivingLinkEndpoint endpoint, + LinkedHashMap openTransactions) + { + _vhost = vhost; + _session = session_1_0; + _endpoint = endpoint; + _sectionDecoder = new SectionDecoderImpl(endpoint.getSession().getConnection().getDescribedTypeRegistry()); + _openTransactions = openTransactions; + } + + public void messageTransfer(Transfer xfr) + { + // TODO - cope with fragmented messages + + ByteBuffer payload = null; + + + if(Boolean.TRUE.equals(xfr.getMore()) && _incompleteMessage == null) + { + _incompleteMessage = new ArrayList(); + _incompleteMessage.add(xfr); + return; + } + else if(_incompleteMessage != null) + { + _incompleteMessage.add(xfr); + if(Boolean.TRUE.equals(xfr.getMore())) + { + return; + } + + int size = 0; + for(Transfer t : _incompleteMessage) + { + size += t.getPayload().limit(); + } + payload = ByteBuffer.allocate(size); + for(Transfer t : _incompleteMessage) + { + payload.put(t.getPayload().duplicate()); + } + payload.flip(); + _incompleteMessage=null; + + } + else + { + payload = xfr.getPayload(); + } + + + // Only interested int he amqp-value section that holds the message to the co-ordinator + try + { + List

sections = _sectionDecoder.parseAll(payload); + + for(Section section : sections) + { + if(section instanceof AmqpValue) + { + Object command = ((AmqpValue) section).getValue(); + + if(command instanceof Declare) + { + Integer txnId = Integer.valueOf(0); + Iterator existingTxn = _openTransactions.keySet().iterator(); + while(existingTxn.hasNext()) + { + txnId = existingTxn.next(); + } + txnId = Integer.valueOf(txnId.intValue() + 1); + + _openTransactions.put(txnId, new LocalTransaction(_vhost.getMessageStore())); + + Declared state = new Declared(); + + + + state.setTxnId(_session.integerToBinary(txnId)); + _endpoint.updateDisposition(xfr.getDeliveryTag(), state, true); + + } + else if(command instanceof Discharge) + { + Discharge discharge = (Discharge) command; + + DeliveryState state = xfr.getState(); + discharge(_session.binaryToInteger(discharge.getTxnId()), discharge.getFail()); + _endpoint.updateDisposition(xfr.getDeliveryTag(), new Accepted(), true); + + } + } + } + + } + catch (AmqpErrorException e) + { + e.printStackTrace(); //TODO. + } + + } + + public void remoteDetached(LinkEndpoint endpoint, Detach detach) + { + //TODO + endpoint.detach(); + } + + private Error discharge(Integer transactionId, boolean fail) + { + Error error = null; + ServerTransaction txn = _openTransactions.get(transactionId); + if(txn != null) + { + if(fail) + { + txn.rollback(); + } + else + { + txn.commit(); + } + _openTransactions.remove(transactionId); + } + else + { + error = new Error(); + error.setCondition(AmqpError.NOT_FOUND); + error.setDescription("Unkown transactionId" + transactionId); + } + return error; + } + + + + public void start() + { + _endpoint.setLinkCredit(UnsignedInteger.ONE); + _endpoint.setCreditWindow(); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/UnsettledAction.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/UnsettledAction.java new file mode 100644 index 0000000000..c497cc5146 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/v1_0/UnsettledAction.java @@ -0,0 +1,8 @@ +package org.apache.qpid.server.protocol.v1_0; + +import org.apache.qpid.amqp_1_0.type.DeliveryState; + +public interface UnsettledAction +{ + boolean process(DeliveryState state, Boolean settled); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java index f6bf6626a0..46c2a635b7 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java @@ -23,19 +23,20 @@ package org.apache.qpid.server.queue; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Map; +import java.util.UUID; public class AMQPriorityQueue extends OutOfOrderQueue { - protected AMQPriorityQueue(final String name, + protected AMQPriorityQueue(UUID id, + final String name, final boolean durable, final String owner, final boolean autoDelete, boolean exclusive, final VirtualHost virtualHost, - Map arguments, - int priorities) + Map arguments, int priorities) { - super(name, durable, owner, autoDelete, exclusive, virtualHost, new PriorityQueueList.Factory(priorities), arguments); + super(id, name, durable, owner, autoDelete, exclusive, virtualHost, new PriorityQueueList.Factory(priorities), arguments); } public int getPriorities() diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java index a3da598bdf..e643338c3d 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java @@ -34,7 +34,6 @@ 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.txn.ServerTransaction; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.List; @@ -141,10 +140,9 @@ public interface AMQQueue extends Managable, Comparable, ExchangeRefer public List getMessagesRangeOnTheQueue(final long fromPosition, final long toPosition); - void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, - ServerTransaction transaction); + void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName); - void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, ServerTransaction transaction); + void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName); void removeMessagesFromQueue(long fromMessageId, long toMessageId); diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java index aca5891d2e..f2b7d7c56b 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java @@ -20,6 +20,10 @@ */ package org.apache.qpid.server.queue; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + import org.apache.qpid.AMQException; import org.apache.qpid.AMQSecurityException; import org.apache.qpid.exchange.ExchangeDefaults; @@ -30,12 +34,10 @@ import org.apache.qpid.server.configuration.ServerConfiguration; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.exchange.ExchangeFactory; import org.apache.qpid.server.exchange.ExchangeRegistry; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; -import java.util.HashMap; -import java.util.Map; - public class AMQQueueFactory { public static final String X_QPID_PRIORITIES = "x-qpid-priorities"; @@ -166,8 +168,13 @@ public class AMQQueueFactory } }; - - /** @see #createAMQQueueImpl(String, boolean, String, boolean, boolean, VirtualHost, Map) */ + /** + * Creates a new queue with a random id. + * + * @see #createAMQQueueImpl(UUID, String, boolean, String, boolean, boolean, VirtualHost, Map) + * @deprecated because only called from unit tests + * */ + @Deprecated public static AMQQueue createAMQQueueImpl(AMQShortString name, boolean durable, AMQShortString owner, @@ -175,22 +182,28 @@ public class AMQQueueFactory boolean exclusive, VirtualHost virtualHost, final FieldTable arguments) throws AMQException { - return createAMQQueueImpl(name == null ? null : name.toString(), + return createAMQQueueImpl(UUIDGenerator.generateUUID(), + name == null ? null : name.toString(), durable, owner == null ? null : owner.toString(), autoDelete, - exclusive, - virtualHost, FieldTable.convertToMap(arguments)); + exclusive, virtualHost, FieldTable.convertToMap(arguments)); } - - public static AMQQueue createAMQQueueImpl(String queueName, + /** + * @param id the id to use. If default then one is generated from queueName. TODO check correctness of calls that pass a null value. + */ + public static AMQQueue createAMQQueueImpl(UUID id, + String queueName, boolean durable, String owner, boolean autoDelete, - boolean exclusive, - VirtualHost virtualHost, Map arguments) throws AMQSecurityException, AMQException + boolean exclusive, VirtualHost virtualHost, Map arguments) throws AMQSecurityException, AMQException { + if (id == null) + { + throw new IllegalArgumentException("Queue id must not be null"); + } if (queueName == null) { throw new IllegalArgumentException("Queue name must not be null"); @@ -241,19 +254,19 @@ public class AMQQueueFactory AMQQueue q; if(sortingKey != null) { - q = new SortedQueue(queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments, sortingKey); + q = new SortedQueue(id, queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments, sortingKey); } else if(conflationKey != null) { - q = new ConflationQueue(queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments, conflationKey); + q = new ConflationQueue(id, queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments, conflationKey); } else if(priorities > 1) { - q = new AMQPriorityQueue(queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments, priorities); + q = new AMQPriorityQueue(id, queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments, priorities); } else { - q = new SimpleAMQQueue(queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments); + q = new SimpleAMQQueue(id, queueName, durable, owner, autoDelete, exclusive, virtualHost, arguments); } //Register the new queue @@ -287,12 +300,12 @@ public class AMQQueueFactory if(dlExchange == null) { - dlExchange = exchangeFactory.createExchange(new AMQShortString(dlExchangeName), ExchangeDefaults.FANOUT_EXCHANGE_CLASS, true, false, 0); + dlExchange = exchangeFactory.createExchange(UUIDGenerator.generateUUID(dlExchangeName, virtualHost.getName()), new AMQShortString(dlExchangeName), ExchangeDefaults.FANOUT_EXCHANGE_CLASS, true, false, 0); exchangeRegistry.registerExchange(dlExchange); //enter the dle in the persistent store - virtualHost.getDurableConfigurationStore().createExchange(dlExchange); + virtualHost.getMessageStore().createExchange(dlExchange); } } @@ -309,10 +322,10 @@ public class AMQQueueFactory args.put(X_QPID_DLQ_ENABLED, false); args.put(X_QPID_MAXIMUM_DELIVERY_COUNT, 0); - dlQueue = createAMQQueueImpl(dlQueueName, true, owner, false, exclusive, virtualHost, args); + dlQueue = createAMQQueueImpl(UUIDGenerator.generateUUID(dlQueueName, virtualHost.getName()), dlQueueName, true, owner, false, exclusive, virtualHost, args); //enter the dlq in the persistent store - virtualHost.getDurableConfigurationStore().createQueue(dlQueue, FieldTable.convertToFieldTable(args)); + virtualHost.getMessageStore().createQueue(dlQueue, FieldTable.convertToFieldTable(args)); } } @@ -364,7 +377,10 @@ public class AMQQueueFactory arguments.put(X_QPID_DLQ_ENABLED, true); } - AMQQueue q = createAMQQueueImpl(queueName, durable, owner, autodelete, exclusive, host, arguments); + // we need queues that are defined in config to have deterministic ids. + UUID id = UUIDGenerator.generateUUID(queueName, host.getName()); + + AMQQueue q = createAMQQueueImpl(id, queueName, durable, owner, autodelete, exclusive, host, arguments); q.configure(config); return q; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java index 7c59097965..b0d4cb3486 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java @@ -36,8 +36,6 @@ import org.apache.qpid.server.message.AMQMessage; import org.apache.qpid.server.message.AMQMessageHeader; import org.apache.qpid.server.message.MessageTransferMessage; import org.apache.qpid.server.message.ServerMessage; -import org.apache.qpid.server.txn.LocalTransaction; -import org.apache.qpid.server.txn.ServerTransaction; import org.apache.qpid.transport.MessageProperties; import javax.management.JMException; @@ -613,9 +611,7 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que throw new OperationsException("\"From MessageId\" should be greater than 0 and less than \"To MessageId\""); } - ServerTransaction txn = new LocalTransaction(_queue.getVirtualHost().getMessageStore()); - _queue.moveMessagesToAnotherQueue(fromMessageId, toMessageId, toQueueName, txn); - txn.commit(); + _queue.moveMessagesToAnotherQueue(fromMessageId, toMessageId, toQueueName); } /** @@ -648,11 +644,7 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que throw new OperationsException("\"From MessageId\" should be greater than 0 and less than \"To MessageId\""); } - ServerTransaction txn = new LocalTransaction(_queue.getVirtualHost().getMessageStore()); - - _queue.copyMessagesToAnotherQueue(fromMessageId, toMessageId, toQueueName, txn); - - txn.commit(); + _queue.copyMessagesToAnotherQueue(fromMessageId, toMessageId, toQueueName); } /** diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/ConflationQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/ConflationQueue.java index 2c645cc555..c2813bb7a5 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/ConflationQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/ConflationQueue.java @@ -21,22 +21,23 @@ package org.apache.qpid.server.queue; -import org.apache.qpid.server.virtualhost.VirtualHost; - import java.util.Map; +import java.util.UUID; + +import org.apache.qpid.server.virtualhost.VirtualHost; public class ConflationQueue extends SimpleAMQQueue { - protected ConflationQueue(String name, + protected ConflationQueue(UUID id, + String name, boolean durable, String owner, boolean autoDelete, boolean exclusive, VirtualHost virtualHost, - Map args, - String conflationKey) + Map args, String conflationKey) { - super(name, durable, owner, autoDelete, exclusive, virtualHost, new ConflationQueueList.Factory(conflationKey), args); + super(id, name, durable, owner, autoDelete, exclusive, virtualHost, new ConflationQueueList.Factory(conflationKey), args); } public String getConflationKey() diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java index d76487073d..2493974d45 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/DefaultQueueRegistry.java @@ -20,15 +20,21 @@ */ package org.apache.qpid.server.queue; +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.server.exchange.DefaultExchangeRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Collection; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; public class DefaultQueueRegistry implements QueueRegistry { + private static final Logger LOGGER = Logger.getLogger(DefaultExchangeRegistry.class); + private ConcurrentMap _queueMap = new ConcurrentHashMap(); private final VirtualHost _virtualHost; @@ -72,4 +78,36 @@ public class DefaultQueueRegistry implements QueueRegistry { return getQueue(new AMQShortString(queue)); } + + @Override + public void stopAllAndUnregisterMBeans() + { + for (final AMQQueue queue : getQueues()) + { + queue.stop(); + try + { + queue.getManagedObject().unregister(); + } + catch (AMQException e) + { + LOGGER.warn("Failed to unregister mbean", e); + } + } + _queueMap.clear(); + } + + @Override + public synchronized AMQQueue getQueue(UUID queueId) + { + Collection queues = _queueMap.values(); + for (AMQQueue queue : queues) + { + if (queue.getId().equals(queueId)) + { + return queue; + } + } + return null; + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java b/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java index 0162f1b738..c5a610c7b6 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java @@ -70,8 +70,6 @@ public class IncomingMessage implements Filterable, InboundMessage, EnqueableMes private Exchange _exchange; - - private int _receivedChunkCount = 0; private List _contentChunks = new ArrayList(); // we keep both the original meta data object and the store reference to it just in case the @@ -80,13 +78,20 @@ public class IncomingMessage implements Filterable, InboundMessage, EnqueableMes private MessageMetaData _messageMetaData; private StoredMessage _storedMessageHandle; + private Object _connectionReference; public IncomingMessage( final MessagePublishInfo info ) + { + this(info, null); + } + + public IncomingMessage(MessagePublishInfo info, Object reference) { _messagePublishInfo = info; + _connectionReference = reference; } public void setContentHeaderBody(final ContentHeaderBody contentHeaderBody) throws AMQException @@ -125,12 +130,6 @@ public class IncomingMessage implements Filterable, InboundMessage, EnqueableMes } - public MessageMetaData headersReceived() - { - - return headersReceived(System.currentTimeMillis()); - } - public MessageMetaData headersReceived(long currentTime) { _messageMetaData = new MessageMetaData(_messagePublishInfo, _contentHeaderBody, 0, currentTime); @@ -143,16 +142,10 @@ public class IncomingMessage implements Filterable, InboundMessage, EnqueableMes return _destinationQueues; } - public int addContentBodyFrame(final ContentChunk contentChunk) - throws AMQException + public void addContentBodyFrame(final ContentChunk contentChunk) throws AMQException { - _storedMessageHandle.addContent((int)_bodyLengthReceived, ByteBuffer.wrap(contentChunk.getData())); _bodyLengthReceived += contentChunk.getSize(); _contentChunks.add(contentChunk); - - - - return _receivedChunkCount++; } public boolean allContentReceived() @@ -252,18 +245,12 @@ public class IncomingMessage implements Filterable, InboundMessage, EnqueableMes return _expiration; } - public int getReceivedChunkCount() - { - return _receivedChunkCount; - } - - public int getBodyCount() throws AMQException { return _contentChunks.size(); } - public ContentChunk getContentChunk(int index) throws IllegalArgumentException, AMQException + public ContentChunk getContentChunk(int index) { return _contentChunks.get(index); } @@ -318,4 +305,14 @@ public class IncomingMessage implements Filterable, InboundMessage, EnqueableMes { return _storedMessageHandle; } + + public Object getConnectionReference() + { + return _connectionReference; + } + + public MessageMetaData getMessageMetaData() + { + return _messageMetaData; + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/OutOfOrderQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/OutOfOrderQueue.java index 53121fc031..daa5db393a 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/OutOfOrderQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/OutOfOrderQueue.java @@ -1,3 +1,23 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ package org.apache.qpid.server.queue; import org.apache.qpid.server.subscription.Subscription; @@ -5,15 +25,16 @@ import org.apache.qpid.server.subscription.SubscriptionList; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Map; +import java.util.UUID; public abstract class OutOfOrderQueue extends SimpleAMQQueue { - protected OutOfOrderQueue(String name, boolean durable, String owner, - boolean autoDelete, boolean exclusive, VirtualHost virtualHost, - QueueEntryListFactory entryListFactory, Map arguments) + protected OutOfOrderQueue(UUID id, String name, boolean durable, + String owner, boolean autoDelete, boolean exclusive, + VirtualHost virtualHost, QueueEntryListFactory entryListFactory, Map arguments) { - super(name, durable, owner, autoDelete, exclusive, virtualHost, entryListFactory, arguments); + super(id, name, durable, owner, autoDelete, exclusive, virtualHost, entryListFactory, arguments); } @Override diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java b/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java index 05141a48a1..1d13ee66c0 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java @@ -25,19 +25,19 @@ import org.apache.qpid.server.message.ServerMessage; public class PriorityQueueList implements QueueEntryList { private final AMQQueue _queue; - private final SimpleQueueEntryList[] _priorityLists; + private final PriorityQueueEntrySubList[] _priorityLists; private final int _priorities; private final int _priorityOffset; public PriorityQueueList(AMQQueue queue, int priorities) { _queue = queue; - _priorityLists = new SimpleQueueEntryList[priorities]; + _priorityLists = new PriorityQueueEntrySubList[priorities]; _priorities = priorities; _priorityOffset = 5-((priorities + 1)/2); for(int i = 0; i < priorities; i++) { - _priorityLists[i] = new SimpleQueueEntryList(queue); + _priorityLists[i] = new PriorityQueueEntrySubList(queue); } } @@ -161,4 +161,48 @@ public class PriorityQueueList implements QueueEntryList return new PriorityQueueList(queue, _priorities); } } + + private static class PriorityQueueEntrySubList extends SimpleQueueEntryList + { + public PriorityQueueEntrySubList(AMQQueue queue) + { + super(queue); + } + + @Override + protected PriorityQueueEntryImpl createQueueEntry(ServerMessage message) + { + return new PriorityQueueEntryImpl(this, message); + } + } + + private static class PriorityQueueEntryImpl extends SimpleQueueEntryImpl + { + public PriorityQueueEntryImpl(PriorityQueueEntrySubList queueEntryList, ServerMessage message) + { + super(queueEntryList, message); + } + + @Override + public int compareTo(final QueueEntry o) + { + byte thisPriority = getMessageHeader().getPriority(); + byte otherPriority = o.getMessageHeader().getPriority(); + + if(thisPriority != otherPriority) + { + /* + * Different priorities, so answer can only be greater than or less than + * + * A message with higher priority (e.g. 5) is conceptually 'earlier' in the + * priority queue than one with a lower priority (e.g. 4). + */ + return thisPriority > otherPriority ? -1 : 1; + } + else + { + return super.compareTo(o); + } + } + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueContext.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueContext.java index c8f04c7b96..79279b44c7 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueContext.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueContext.java @@ -52,4 +52,13 @@ final class QueueContext implements AMQQueue.Context { return _releasedEntry; } + + @Override + public String toString() + { + return "QueueContext{" + + "_lastSeenEntry=" + _lastSeenEntry + + ", _releasedEntry=" + _releasedEntry + + '}'; + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java index fea5303e23..209553e8fa 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java @@ -227,9 +227,10 @@ public abstract class QueueEntryImpl implements QueueEntry public void release() { EntryState state = _state; - + if((state.getState() == State.ACQUIRED) &&_stateUpdater.compareAndSet(this, state, AVAILABLE_STATE)) { + if(state instanceof SubscriptionAcquiredState) { getQueue().decrementUnackedMsgCount(); @@ -254,6 +255,7 @@ public abstract class QueueEntryImpl implements QueueEntry routeToAlternate(); } } + } public boolean releaseButRetain() @@ -267,7 +269,6 @@ public abstract class QueueEntryImpl implements QueueEntry Subscription sub = ((SubscriptionAcquiredState) state).getSubscription(); if(_stateUpdater.compareAndSet(this, state, sub.getAssignedState())) { - System.err.println("Message released (and retained)" + getMessage().getMessageNumber()); getQueue().requeue(this); if(_stateChangeListeners != null) { @@ -417,11 +418,19 @@ public abstract class QueueEntryImpl implements QueueEntry if (alternateExchange != null) { - final List rerouteQueues = alternateExchange.route(new InboundMessageAdapter(this)); + InboundMessageAdapter inboundMessageAdapter = new InboundMessageAdapter(this); + List queues = alternateExchange.route(inboundMessageAdapter); final ServerMessage message = getMessage(); - if (rerouteQueues != null && rerouteQueues.size() != 0) + if ((queues == null || queues.size() == 0) && alternateExchange.getAlternateExchange() != null) { + queues = alternateExchange.getAlternateExchange().route(inboundMessageAdapter); + } + + + if (queues != null && queues.size() != 0) + { + final List rerouteQueues = queues; ServerTransaction txn = new LocalTransaction(getQueue().getVirtualHost().getMessageStore()); txn.enqueue(rerouteQueues, message, new ServerTransaction.Action() diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryVisitor.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryVisitor.java new file mode 100644 index 0000000000..1578d21321 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryVisitor.java @@ -0,0 +1,22 @@ +package org.apache.qpid.server.queue; + +/** +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +*

+* http://www.apache.org/licenses/LICENSE-2.0 +*

+* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +public interface QueueEntryVisitor +{ + boolean visit(QueueEntry entry); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java index 80f6bd1493..72a54c9889 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueRegistry.java @@ -24,6 +24,7 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.Collection; +import java.util.UUID; public interface QueueRegistry { @@ -40,4 +41,8 @@ public interface QueueRegistry Collection getQueues(); AMQQueue getQueue(String queue); + + void stopAllAndUnregisterMBeans(); + + AMQQueue getQueue(UUID queueId); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java index 891a492b7f..d7eb304c92 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java @@ -191,29 +191,29 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes private int _maximumDeliveryCount = ApplicationRegistry.getInstance().getConfiguration().getMaxDeliveryCount(); private final MessageGroupManager _messageGroupManager; - protected SimpleAMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, boolean exclusive, VirtualHost virtualHost, Map arguments) + protected SimpleAMQQueue(UUID id, AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, boolean exclusive, VirtualHost virtualHost, Map arguments) { - this(name, durable, owner, autoDelete, exclusive, virtualHost,new SimpleQueueEntryList.Factory(), arguments); + this(id, name, durable, owner, autoDelete, exclusive,virtualHost, new SimpleQueueEntryList.Factory(), arguments); } - public SimpleAMQQueue(String queueName, boolean durable, String owner, boolean autoDelete, boolean exclusive, VirtualHost virtualHost, Map arguments) + public SimpleAMQQueue(UUID id, String queueName, boolean durable, String owner, boolean autoDelete, boolean exclusive, VirtualHost virtualHost, Map arguments) { - this(queueName, durable, owner, autoDelete, exclusive, virtualHost, new SimpleQueueEntryList.Factory(), arguments); + this(id, queueName, durable, owner, autoDelete, exclusive, virtualHost, new SimpleQueueEntryList.Factory(), arguments); } - public SimpleAMQQueue(String queueName, boolean durable, String owner, boolean autoDelete, boolean exclusive, VirtualHost virtualHost, QueueEntryListFactory entryListFactory, Map arguments) + public SimpleAMQQueue(UUID id, String queueName, boolean durable, String owner, boolean autoDelete, boolean exclusive, VirtualHost virtualHost, QueueEntryListFactory entryListFactory, Map arguments) { - this(queueName == null ? null : new AMQShortString(queueName), durable, owner == null ? null : new AMQShortString(owner), autoDelete, exclusive, virtualHost, entryListFactory, arguments); + this(id, queueName == null ? null : new AMQShortString(queueName), durable, owner == null ? null : new AMQShortString(owner), autoDelete, exclusive, virtualHost, entryListFactory, arguments); } - protected SimpleAMQQueue(AMQShortString name, + protected SimpleAMQQueue(UUID id, + AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, boolean exclusive, VirtualHost virtualHost, - QueueEntryListFactory entryListFactory, - Map arguments) + QueueEntryListFactory entryListFactory, Map arguments) { if (name == null) @@ -236,7 +236,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes _entries = entryListFactory.createQueueEntryList(this); _arguments = arguments; - _id = virtualHost.getConfigStore().createId(); + _id = id; _asyncDelivery = ReferenceCountingExecutorService.getInstance().acquireExecutorService(); @@ -346,7 +346,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes if(isDurable()) { - getVirtualHost().getDurableConfigurationStore().updateQueue(this); + getVirtualHost().getMessageStore().updateQueue(this); } } @@ -862,7 +862,6 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes public void requeue(QueueEntry entry) { - SubscriptionList.SubscriptionNodeIterator subscriberIter = _subscriptionList.iterator(); // iterate over all the subscribers, and if they are in advance of this queue entry then move them backwards while (subscriberIter.advance() && entry.isAvailable()) @@ -1198,19 +1197,10 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes public void moveMessagesToAnotherQueue(final long fromMessageId, final long toMessageId, - String queueName, - ServerTransaction txn) throws IllegalArgumentException + String destinationQueueName) throws IllegalArgumentException { - final AMQQueue toQueue = getVirtualHost().getQueueRegistry().getQueue(new AMQShortString(queueName)); - if (toQueue == null) - { - throw new IllegalArgumentException("Queue '" + queueName + "' is not registered with the virtualhost."); - } - else if (toQueue == this) - { - throw new IllegalArgumentException("The destination queue cant be the same as the source queue"); - } + final AMQQueue toQueue = getValidatedDestinationQueue(destinationQueueName); List entries = getMessagesOnTheQueue(new QueueEntryFilter() { @@ -1230,65 +1220,68 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes }); - - // Move the messages in on the message store. - for (final QueueEntry entry : entries) + final ServerTransaction txn = new LocalTransaction(getVirtualHost().getMessageStore()); + boolean shouldRollback = true; + try { - final ServerMessage message = entry.getMessage(); - txn.enqueue(toQueue, message, - new ServerTransaction.Action() - { - - public void postCommit() + // Move the messages in on the message store. + for (final QueueEntry entry : entries) + { + final ServerMessage message = entry.getMessage(); + txn.enqueue(toQueue, message, + new ServerTransaction.Action() { - try + + public void postCommit() { - toQueue.enqueue(message); + try + { + toQueue.enqueue(message); + } + catch (AMQException e) + { + throw new RuntimeException(e); + } } - catch (AMQException e) + + public void onRollback() { - throw new RuntimeException(e); + entry.release(); } - } - - public void onRollback() - { - entry.release(); - } - }); - txn.dequeue(this, message, - new ServerTransaction.Action() - { - - public void postCommit() + }); + txn.dequeue(this, message, + new ServerTransaction.Action() { - entry.discard(); - } - public void onRollback() - { + public void postCommit() + { + entry.discard(); + } - } - }); + public void onRollback() + { + } + }); + } + txn.commit(); + shouldRollback = false; + } + finally + { + if (shouldRollback) + { + txn.rollback(); + } } } public void copyMessagesToAnotherQueue(final long fromMessageId, final long toMessageId, - String queueName, - final ServerTransaction txn) throws IllegalArgumentException + String destinationQueueName) throws IllegalArgumentException { - final AMQQueue toQueue = getVirtualHost().getQueueRegistry().getQueue(new AMQShortString(queueName)); - if (toQueue == null) - { - throw new IllegalArgumentException("Queue '" + queueName + "' is not registered with the virtualhost."); - } - else if (toQueue == this) - { - throw new IllegalArgumentException("The destination queue cant be the same as the source queue"); - } + final AMQQueue toQueue = getValidatedDestinationQueue(destinationQueueName); List entries = getMessagesOnTheQueue(new QueueEntryFilter() { @@ -1306,36 +1299,63 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } }); - - // Move the messages in on the message store. - for (QueueEntry entry : entries) + final ServerTransaction txn = new LocalTransaction(_virtualHost.getMessageStore()); + boolean shouldRollback = true; + try { - final ServerMessage message = entry.getMessage(); - - txn.enqueue(toQueue, message, new ServerTransaction.Action() + // Copy the messages in on the message store. + for (QueueEntry entry : entries) { - public void postCommit() + final ServerMessage message = entry.getMessage(); + + txn.enqueue(toQueue, message, new ServerTransaction.Action() { - try + public void postCommit() { - toQueue.enqueue(message); + try + { + toQueue.enqueue(message); + } + catch (AMQException e) + { + throw new RuntimeException(e); + } } - catch (AMQException e) + + public void onRollback() { - throw new RuntimeException(e); } - } + }); - public void onRollback() - { - - } - }); + } + txn.commit(); + shouldRollback = false; + } + finally + { + if (shouldRollback) + { + txn.rollback(); + } } } + private AMQQueue getValidatedDestinationQueue(String queueName) + { + final AMQQueue toQueue = getVirtualHost().getQueueRegistry().getQueue(new AMQShortString(queueName)); + if (toQueue == null) + { + throw new IllegalArgumentException("Queue '" + queueName + "' is not registered with the virtualhost."); + } + else if (toQueue == this) + { + throw new IllegalArgumentException("The destination queue can't be the same as the source queue"); + } + return toQueue; + } + public void removeMessagesFromQueue(long fromMessageId, long toMessageId) { @@ -1515,10 +1535,16 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes for(final QueueEntry entry : entries) { adapter.setEntry(entry); - final List rerouteQueues = _alternateExchange.route(adapter); + List queues = _alternateExchange.route(adapter); + if((queues == null || queues.size() == 0) && _alternateExchange.getAlternateExchange() != null) + { + queues = _alternateExchange.getAlternateExchange().route(adapter); + } + final ServerMessage message = entry.getMessage(); - if(rerouteQueues != null && rerouteQueues.size() != 0) + if(queues != null && queues.size() != 0) { + final List rerouteQueues = queues; txn.enqueue(rerouteQueues, entry.getMessage(), new ServerTransaction.Action() { @@ -1716,6 +1742,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes { boolean atTail = false; final boolean keepSendLockHeld = iterations <= SimpleAMQQueue.MAX_ASYNC_DELIVERIES; + boolean queueEmpty = false; try { @@ -1733,12 +1760,9 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes } atTail = attemptDelivery(sub, true); - if (atTail && !sub.isSuspended() && sub.isAutoClose()) + if (atTail && getNextAvailableEntry(sub) == null) { - unregisterSubscription(sub); - - sub.confirmAutoClose(); - + queueEmpty = true; } else if (!atTail) { @@ -1760,6 +1784,11 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes { sub.releaseSendLock(); } + if(queueEmpty) + { + sub.queueEmpty(); + } + sub.flushBatched(); } @@ -1895,7 +1924,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes if(context != null) { QueueEntry releasedNode = context.getReleasedEntry(); - return releasedNode == null || releasedNode.compareTo(entry) < 0; + return releasedNode != null && releasedNode.compareTo(entry) < 0; } else { @@ -1982,13 +2011,9 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes if (subscriptionDone) { sub.flushBatched(); - //close autoClose subscriptions if we are not currently intent on continuing - if (lastLoop && !sub.isSuspended() && sub.isAutoClose()) + if (lastLoop && !sub.isSuspended()) { - - unregisterSubscription(sub); - - sub.confirmAutoClose(); + sub.queueEmpty(); } break; } @@ -2064,9 +2089,13 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener, Mes // Only process nodes that are not currently deleted and not dequeued if (!node.isDispensed()) { - // If the node has exired then aquire it + // If the node has exired then acquire it if (node.expired() && node.acquire()) { + if (_logger.isDebugEnabled()) + { + _logger.debug("Dequeuing expired node " + node); + } // Then dequeue it. dequeueEntry(node); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java index e57390163c..b8d8ec19f4 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java +++ b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java @@ -104,7 +104,7 @@ public class SimpleQueueEntryList implements QueueEntryList message) { return new SimpleQueueEntryImpl(this, message); } @@ -116,7 +116,6 @@ public class SimpleQueueEntryList implements QueueEntryList { - private SimpleQueueEntryImpl _lastNode; QueueEntryIteratorImpl(SimpleQueueEntryImpl startNode) @@ -124,10 +123,9 @@ public class SimpleQueueEntryList implements QueueEntryList arguments, String sortedPropertyName) + protected SortedQueue(UUID id, final String name, + final boolean durable, final String owner, final boolean autoDelete, + final boolean exclusive, final VirtualHost virtualHost, Map arguments, String sortedPropertyName) { - super(name, durable, owner, autoDelete, exclusive, virtualHost, - new SortedQueueEntryListFactory(sortedPropertyName), arguments); + super(id, name, durable, owner, autoDelete, exclusive, + virtualHost, new SortedQueueEntryListFactory(sortedPropertyName), arguments); this._sortedPropertyName = sortedPropertyName; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java index 224d694932..4ed28b965d 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java @@ -186,19 +186,6 @@ public abstract class ApplicationRegistry implements IApplicationRegistry _qmfService = qmfService; } - static - { - Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownService())); - } - - private static class ShutdownService implements Runnable - { - public void run() - { - remove(); - } - } - public static void initialise(IApplicationRegistry instance) throws Exception { if(instance == null) @@ -273,7 +260,6 @@ public abstract class ApplicationRegistry implements IApplicationRegistry _logger.info("Shutting down ApplicationRegistry(" + instance + ")"); } instance.close(); - instance.getBroker().getSystem().removeBroker(instance.getBroker()); } } catch (Exception e) @@ -536,35 +522,49 @@ public abstract class ApplicationRegistry implements IApplicationRegistry } } - public void close() { if (_logger.isInfoEnabled()) { _logger.info("Shutting down ApplicationRegistry:" + this); } - - //Stop Statistics Reporting - if (_reportingTimer != null) + + //Set the Actor for Broker Shutdown + CurrentActor.set(new BrokerActor(getRootMessageLogger())); + try { - _reportingTimer.cancel(); - } + //Stop Statistics Reporting + if (_reportingTimer != null) + { + _reportingTimer.cancel(); + } + + //Stop incoming connections + unbind(); - //Stop incoming connections - unbind(); + //Shutdown virtualhosts + close(_virtualHostRegistry); - //Shutdown virtualhosts - close(_virtualHostRegistry); + close(_authenticationManager); - close(_authenticationManager); + close(_qmfService); - close(_qmfService); + close(_pluginManager); - close(_pluginManager); + close(_managedObjectRegistry); - close(_managedObjectRegistry); + BrokerConfig broker = getBroker(); + if(broker != null) + { + broker.getSystem().removeBroker(broker); + } - CurrentActor.get().message(BrokerMessages.STOPPED()); + CurrentActor.get().message(BrokerMessages.STOPPED()); + } + finally + { + CurrentActor.remove(); + } } private void unbind() @@ -664,7 +664,7 @@ public abstract class ApplicationRegistry implements IApplicationRegistry public VirtualHost createVirtualHost(final VirtualHostConfiguration vhostConfig) throws Exception { - VirtualHostImpl virtualHost = new VirtualHostImpl(this, vhostConfig, null); + VirtualHostImpl virtualHost = new VirtualHostImpl(this, vhostConfig); _virtualHostRegistry.registerVirtualHost(virtualHost); getBroker().addVirtualHost(virtualHost); return virtualHost; diff --git a/java/broker/src/main/java/org/apache/qpid/server/registry/BrokerConfigAdapter.java b/java/broker/src/main/java/org/apache/qpid/server/registry/BrokerConfigAdapter.java index 58fdc99dd3..db436b99e8 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/registry/BrokerConfigAdapter.java +++ b/java/broker/src/main/java/org/apache/qpid/server/registry/BrokerConfigAdapter.java @@ -114,7 +114,6 @@ public class BrokerConfigAdapter implements BrokerConfig public void addVirtualHost(final VirtualHostConfig virtualHost) { - virtualHost.setBroker(this); _vhosts.put(virtualHost.getId(), virtualHost); getConfigStore().addConfiguredObject(virtualHost); diff --git a/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java index e9ba2764e5..b28e3d6c89 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java +++ b/java/broker/src/main/java/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java @@ -25,8 +25,6 @@ import org.osgi.framework.BundleContext; import org.apache.qpid.AMQException; import org.apache.qpid.server.configuration.ServerConfiguration; -import org.apache.qpid.server.logging.actors.BrokerActor; -import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.management.JMXManagedObjectRegistry; import org.apache.qpid.server.management.NoopManagedObjectRegistry; @@ -44,22 +42,6 @@ public class ConfigurationFileApplicationRegistry extends ApplicationRegistry super(new ServerConfiguration(configurationURL), bundleContext); } - @Override - public void close() - { - //Set the Actor for Broker Shutdown - CurrentActor.set(new BrokerActor(getRootMessageLogger())); - try - { - super.close(); - } - finally - { - CurrentActor.remove(); - } - } - - @Override protected void initialiseManagedObjectRegistry() throws AMQException { diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java index 4ffa5a4bc2..6c1a917d5b 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/AuthenticationManager.java @@ -24,6 +24,7 @@ import org.apache.qpid.common.Closeable; import org.apache.qpid.server.plugins.Plugin; import org.apache.qpid.server.security.auth.AuthenticationResult; +import javax.security.auth.callback.CallbackHandler; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; @@ -88,4 +89,6 @@ public interface AuthenticationManager extends Closeable, Plugin * @return authentication result */ AuthenticationResult authenticate(String username, String password); + + CallbackHandler getHandler(String mechanism); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java index 3fa0de2af0..b5d70d9200 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java @@ -300,6 +300,11 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan } } + public CallbackHandler getHandler(String mechanism) + { + return _callbackHandlerMap.get(mechanism); + } + /** * @see org.apache.qpid.server.security.auth.manager.AuthenticationManager#authenticate(String, String) */ diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServerFactory.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServerFactory.java index 3664568b75..4650234972 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServerFactory.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/anonymous/AnonymousSaslServerFactory.java @@ -58,4 +58,4 @@ public class AnonymousSaslServerFactory implements SaslServerFactory return new String[]{AnonymousSaslServer.MECHANISM}; } } -} \ No newline at end of file +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.java b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.java index c1f306dce9..a811806c00 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.java +++ b/java/broker/src/main/java/org/apache/qpid/server/security/auth/sasl/plain/PlainSaslServer.java @@ -51,56 +51,65 @@ public class PlainSaslServer implements SaslServer public byte[] evaluateResponse(byte[] response) throws SaslException { - try + int authzidNullPosition = findNullPosition(response, 0); + if (authzidNullPosition < 0) { - int authzidNullPosition = findNullPosition(response, 0); - if (authzidNullPosition < 0) - { - throw new SaslException("Invalid PLAIN encoding, authzid null terminator not found"); - } - int authcidNullPosition = findNullPosition(response, authzidNullPosition + 1); - if (authcidNullPosition < 0) - { - throw new SaslException("Invalid PLAIN encoding, authcid null terminator not found"); - } + throw new SaslException("Invalid PLAIN encoding, authzid null terminator not found"); + } + int authcidNullPosition = findNullPosition(response, authzidNullPosition + 1); + if (authcidNullPosition < 0) + { + throw new SaslException("Invalid PLAIN encoding, authcid null terminator not found"); + } + + PlainPasswordCallback passwordCb; + AuthorizeCallback authzCb; + try + { // we do not currently support authcid in any meaningful way String authzid = new String(response, authzidNullPosition + 1, authcidNullPosition - authzidNullPosition - 1, "utf8"); // TODO: should not get pwd as a String but as a char array... int passwordLen = response.length - authcidNullPosition - 1; String pwd = new String(response, authcidNullPosition + 1, passwordLen, "utf8"); - + // we do not care about the prompt but it throws if null NameCallback nameCb = new NameCallback("prompt", authzid); - PlainPasswordCallback passwordCb = new PlainPasswordCallback("prompt", false, pwd); - AuthorizeCallback authzCb = new AuthorizeCallback(authzid, authzid); + passwordCb = new PlainPasswordCallback("prompt", false, pwd); + authzCb = new AuthorizeCallback(authzid, authzid); Callback[] callbacks = new Callback[]{nameCb, passwordCb, authzCb}; _cbh.handle(callbacks); - if (passwordCb.isAuthenticated()) - { - _complete = true; - } - if (authzCb.isAuthorized() && _complete) - { - _authorizationId = authzCb.getAuthenticationID(); - return null; - } - else - { - throw new SaslException("Authentication failed"); - } } catch (IOException e) { + if(e instanceof SaslException) + { + throw (SaslException) e; + } throw new SaslException("Error processing data: " + e, e); } catch (UnsupportedCallbackException e) { throw new SaslException("Unable to obtain data from callback handler: " + e, e); } + + if (passwordCb.isAuthenticated()) + { + _complete = true; + } + + if (authzCb.isAuthorized() && _complete) + { + _authorizationId = authzCb.getAuthenticationID(); + return null; + } + else + { + throw new SaslException("Authentication failed"); + } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/AbstractMessageStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/AbstractMessageStore.java deleted file mode 100644 index fc5d2a4e42..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/store/AbstractMessageStore.java +++ /dev/null @@ -1,43 +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.store; - -import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.messages.MessageStoreMessages; -import org.apache.qpid.server.logging.subjects.MessageStoreLogSubject; -import org.apache.qpid.server.virtualhost.VirtualHost; - -public abstract class AbstractMessageStore implements MessageStore -{ - private LogSubject _logSubject; - - public void configure(VirtualHost virtualHost) throws Exception - { - _logSubject = new MessageStoreLogSubject(virtualHost, this); - CurrentActor.get().message(_logSubject, MessageStoreMessages.CREATED(this.getClass().getName())); - } - - public void close() throws Exception - { - CurrentActor.get().message(_logSubject,MessageStoreMessages.CLOSED()); - } -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java b/java/broker/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java index fb67500da9..1307b1dbd4 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/ConfigurationRecoveryHandler.java @@ -32,19 +32,19 @@ public interface ConfigurationRecoveryHandler public static interface QueueRecoveryHandler { - void queue(String queueName, String owner, boolean exclusive, FieldTable arguments); + void queue(UUID id, String queueName, String owner, boolean exclusive, FieldTable arguments); ExchangeRecoveryHandler completeQueueRecovery(); } public static interface ExchangeRecoveryHandler { - void exchange(String exchangeName, String type, boolean autoDelete); + void exchange(UUID id, String exchangeName, String type, boolean autoDelete); BindingRecoveryHandler completeExchangeRecovery(); } public static interface BindingRecoveryHandler { - void binding(String exchangeName, String queueName, String bindingKey, ByteBuffer buf); + void binding(UUID bindingId, UUID exchangeId, UUID queueId, String bindingName, ByteBuffer buf); BrokerLinkRecoveryHandler completeBindingRecovery(); } @@ -60,13 +60,4 @@ public interface ConfigurationRecoveryHandler void completeBridgeRecoveryForLink(); } - public static interface QueueEntryRecoveryHandler - { - void complete(); - - void queueEntry(String queueName, long messageId); - } - - - } diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/ConfiguredObjectHelper.java b/java/broker/src/main/java/org/apache/qpid/server/store/ConfiguredObjectHelper.java new file mode 100644 index 0000000000..1a67fdf540 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/ConfiguredObjectHelper.java @@ -0,0 +1,183 @@ +/* + * + * 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.store; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.model.Binding; +import org.apache.qpid.server.model.Exchange; +import org.apache.qpid.server.model.LifetimePolicy; +import org.apache.qpid.server.model.Queue; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.store.ConfigurationRecoveryHandler.BindingRecoveryHandler; +import org.apache.qpid.server.store.ConfigurationRecoveryHandler.ExchangeRecoveryHandler; +import org.apache.qpid.server.store.ConfigurationRecoveryHandler.QueueRecoveryHandler; +import org.apache.qpid.server.util.MapJsonSerializer; + +public class ConfiguredObjectHelper +{ + /** + * Name of queue attribute to store queue creation arguments. + *

+ * This attribute is not defined yet on Queue configured object interface. + */ + private static final String QUEUE_ARGUMENTS = "ARGUMENTS"; + + private MapJsonSerializer _serializer = new MapJsonSerializer(); + + public void loadQueue(ConfiguredObjectRecord configuredObject, QueueRecoveryHandler qrh) + { + if (Queue.class.getName().equals(configuredObject.getType())) + { + Map attributeMap = _serializer.deserialize(configuredObject.getAttributes()); + String queueName = (String) attributeMap.get(Queue.NAME); + String owner = (String) attributeMap.get(Queue.OWNER); + boolean exclusive = (Boolean) attributeMap.get(Queue.EXCLUSIVE); + @SuppressWarnings("unchecked") + Map queueArgumentsMap = (Map) attributeMap.get(QUEUE_ARGUMENTS); + FieldTable arguments = null; + if (queueArgumentsMap != null) + { + arguments = FieldTable.convertToFieldTable(queueArgumentsMap); + } + qrh.queue(configuredObject.getId(), queueName, owner, exclusive, arguments); + } + } + + public ConfiguredObjectRecord updateQueueConfiguredObject(final AMQQueue queue, ConfiguredObjectRecord queueRecord) + { + Map attributesMap = _serializer.deserialize(queueRecord.getAttributes()); + attributesMap.put(Queue.NAME, queue.getName()); + attributesMap.put(Queue.EXCLUSIVE, queue.isExclusive()); + String newJson = _serializer.serialize(attributesMap); + ConfiguredObjectRecord newQueueRecord = new ConfiguredObjectRecord(queue.getId(), queueRecord.getType(), newJson); + return newQueueRecord; + } + + public ConfiguredObjectRecord createQueueConfiguredObject(AMQQueue queue, FieldTable arguments) + { + Map attributesMap = new HashMap(); + attributesMap.put(Queue.NAME, queue.getName()); + attributesMap.put(Queue.OWNER, AMQShortString.toString(queue.getOwner())); + attributesMap.put(Queue.EXCLUSIVE, queue.isExclusive()); + if (arguments != null) + { + attributesMap.put(QUEUE_ARGUMENTS, FieldTable.convertToMap(arguments)); + } + String json = _serializer.serialize(attributesMap); + ConfiguredObjectRecord configuredObject = new ConfiguredObjectRecord(queue.getId(), Queue.class.getName(), json); + return configuredObject; + } + + public void loadExchange(ConfiguredObjectRecord configuredObject, ExchangeRecoveryHandler erh) + { + if (Exchange.class.getName().equals(configuredObject.getType())) + { + Map attributeMap = _serializer.deserialize(configuredObject.getAttributes()); + String exchangeName = (String) attributeMap.get(Exchange.NAME); + String exchangeType = (String) attributeMap.get(Exchange.TYPE); + String lifeTimePolicy = (String) attributeMap.get(Exchange.LIFETIME_POLICY); + boolean autoDelete = lifeTimePolicy == null + || LifetimePolicy.valueOf(lifeTimePolicy) == LifetimePolicy.AUTO_DELETE; + erh.exchange(configuredObject.getId(), exchangeName, exchangeType, autoDelete); + } + } + + public ConfiguredObjectRecord createExchangeConfiguredObject(org.apache.qpid.server.exchange.Exchange exchange) + { + Map attributesMap = new HashMap(); + attributesMap.put(Exchange.NAME, exchange.getName()); + attributesMap.put(Exchange.TYPE, AMQShortString.toString(exchange.getTypeShortString())); + attributesMap.put(Exchange.LIFETIME_POLICY, exchange.isAutoDelete() ? LifetimePolicy.AUTO_DELETE.name() + : LifetimePolicy.PERMANENT.name()); + String json = _serializer.serialize(attributesMap); + ConfiguredObjectRecord configuredObject = new ConfiguredObjectRecord(exchange.getId(), Exchange.class.getName(), json); + return configuredObject; + } + + public void loadQueueBinding(ConfiguredObjectRecord configuredObject, BindingRecoveryHandler brh) + { + if (Binding.class.getName().equals(configuredObject.getType())) + { + Map attributeMap = _serializer.deserialize(configuredObject.getAttributes()); + UUID exchangeId = UUID.fromString((String)attributeMap.get(Binding.EXCHANGE)); + UUID queueId = UUID.fromString((String) attributeMap.get(Binding.QUEUE)); + String bindingName = (String) attributeMap.get(Binding.NAME); + + @SuppressWarnings("unchecked") + Map bindingArgumentsMap = (Map) attributeMap.get(Binding.ARGUMENTS); + FieldTable arguments = null; + if (bindingArgumentsMap != null) + { + arguments = FieldTable.convertToFieldTable(bindingArgumentsMap); + } + ByteBuffer argumentsBB = (arguments == null ? null : ByteBuffer.wrap(arguments.getDataAsBytes())); + + brh.binding(configuredObject.getId(), exchangeId, queueId, bindingName, argumentsBB); + } + } + + public ConfiguredObjectRecord createBindingConfiguredObject(org.apache.qpid.server.binding.Binding binding) + { + Map attributesMap = new HashMap(); + attributesMap.put(Binding.NAME, binding.getBindingKey()); + attributesMap.put(Binding.EXCHANGE, binding.getExchange().getId()); + attributesMap.put(Binding.QUEUE, binding.getQueue().getId()); + Map arguments = binding.getArguments(); + if (arguments != null) + { + attributesMap.put(Binding.ARGUMENTS, arguments); + } + String json = _serializer.serialize(attributesMap); + ConfiguredObjectRecord configuredObject = new ConfiguredObjectRecord(binding.getId(), Binding.class.getName(), json); + return configuredObject; + } + + public void recoverQueues(QueueRecoveryHandler qrh, List configuredObjects) + { + for (ConfiguredObjectRecord configuredObjectRecord : configuredObjects) + { + loadQueue(configuredObjectRecord, qrh); + } + } + + public void recoverExchanges(ExchangeRecoveryHandler erh, List configuredObjects) + { + for (ConfiguredObjectRecord configuredObjectRecord : configuredObjects) + { + loadExchange(configuredObjectRecord, erh); + } + } + + public void recoverBindings(BindingRecoveryHandler brh, List configuredObjects) + { + for (ConfiguredObjectRecord configuredObjectRecord : configuredObjects) + { + loadQueueBinding(configuredObjectRecord, brh); + } + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecord.java b/java/broker/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecord.java new file mode 100644 index 0000000000..95e1713d78 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/ConfiguredObjectRecord.java @@ -0,0 +1,65 @@ +/* + * + * 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.store; + +import java.util.UUID; + +public class ConfiguredObjectRecord +{ + private UUID _id; + private String _type; + private String _attributes; + + public ConfiguredObjectRecord(UUID id, String type, String attributes) + { + super(); + _id = id; + _type = type; + _attributes = attributes; + } + + public UUID getId() + { + return _id; + } + + public void setId(UUID id) + { + _id = id; + } + + public String getType() + { + return _type; + } + + public String getAttributes() + { + return _attributes; + } + + @Override + public String toString() + { + return "ConfiguredObjectRecord [id=" + _id + ", type=" + _type + ", attributes=" + _attributes + "]"; + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java deleted file mode 100644 index 2877e25645..0000000000 --- a/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java +++ /dev/null @@ -1,2363 +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.store; - -import org.apache.commons.configuration.Configuration; -import org.apache.log4j.Logger; - -import org.apache.qpid.AMQException; -import org.apache.qpid.AMQStoreException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.federation.Bridge; -import org.apache.qpid.server.federation.BrokerLink; -import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.messages.ConfigStoreMessages; -import org.apache.qpid.server.logging.messages.MessageStoreMessages; -import org.apache.qpid.server.logging.messages.TransactionLogMessages; -import org.apache.qpid.server.message.EnqueableMessage; -import org.apache.qpid.server.queue.AMQQueue; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.IOException; -import java.lang.ref.SoftReference; -import java.nio.ByteBuffer; -import java.sql.Blob; -import java.sql.Connection; -import java.sql.Driver; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.sql.Types; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicLong; - -/** - * An implementation of a {@link MessageStore} that uses Apache Derby as the persistance - * mechanism. - * - * TODO extract the SQL statements into a generic JDBC store - */ -public class DerbyMessageStore implements MessageStore, DurableConfigurationStore -{ - - private static final Logger _logger = Logger.getLogger(DerbyMessageStore.class); - - public static final String ENVIRONMENT_PATH_PROPERTY = "environment-path"; - - - private static final String SQL_DRIVER_NAME = "org.apache.derby.jdbc.EmbeddedDriver"; - - private static final String DB_VERSION_TABLE_NAME = "QPID_DB_VERSION"; - - private static final String EXCHANGE_TABLE_NAME = "QPID_EXCHANGE"; - private static final String QUEUE_TABLE_NAME = "QPID_QUEUE"; - private static final String BINDINGS_TABLE_NAME = "QPID_BINDINGS"; - private static final String QUEUE_ENTRY_TABLE_NAME = "QPID_QUEUE_ENTRY"; - - private static final String META_DATA_TABLE_NAME = "QPID_META_DATA"; - private static final String MESSAGE_CONTENT_TABLE_NAME = "QPID_MESSAGE_CONTENT"; - - private static final String LINKS_TABLE_NAME = "QPID_LINKS"; - private static final String BRIDGES_TABLE_NAME = "QPID_BRIDGES"; - - - private static final int DB_VERSION = 3; - - - - private static Class DRIVER_CLASS; - - private final AtomicLong _messageId = new AtomicLong(0); - private AtomicBoolean _closed = new AtomicBoolean(false); - - private String _connectionURL; - - private static final String TABLE_EXISTANCE_QUERY = "SELECT 1 FROM SYS.SYSTABLES WHERE TABLENAME = ?"; - - private static final String CREATE_DB_VERSION_TABLE = "CREATE TABLE "+DB_VERSION_TABLE_NAME+" ( version int not null )"; - private static final String INSERT_INTO_DB_VERSION = "INSERT INTO "+DB_VERSION_TABLE_NAME+" ( version ) VALUES ( ? )"; - - private static final String CREATE_EXCHANGE_TABLE = "CREATE TABLE "+EXCHANGE_TABLE_NAME+" ( name varchar(255) not null, type varchar(255) not null, autodelete SMALLINT not null, PRIMARY KEY ( name ) )"; - private static final String CREATE_QUEUE_TABLE = "CREATE TABLE "+QUEUE_TABLE_NAME+" ( name varchar(255) not null, owner varchar(255), exclusive SMALLINT not null, arguments blob, PRIMARY KEY ( name ))"; - private static final String CREATE_BINDINGS_TABLE = "CREATE TABLE "+BINDINGS_TABLE_NAME+" ( exchange_name varchar(255) not null, queue_name varchar(255) not null, binding_key varchar(255) not null, arguments blob , PRIMARY KEY ( exchange_name, queue_name, binding_key ) )"; - private static final String SELECT_FROM_QUEUE = "SELECT name, owner, exclusive, arguments FROM " + QUEUE_TABLE_NAME; - private static final String FIND_QUEUE = "SELECT name, owner FROM " + QUEUE_TABLE_NAME + " WHERE name = ?"; - private static final String UPDATE_QUEUE_EXCLUSIVITY = "UPDATE " + QUEUE_TABLE_NAME + " SET exclusive = ? WHERE name = ?"; - private static final String SELECT_FROM_EXCHANGE = "SELECT name, type, autodelete FROM " + EXCHANGE_TABLE_NAME; - private static final String SELECT_FROM_BINDINGS = - "SELECT exchange_name, queue_name, binding_key, arguments FROM " + BINDINGS_TABLE_NAME + " ORDER BY exchange_name"; - private static final String FIND_BINDING = - "SELECT * FROM " + BINDINGS_TABLE_NAME + " WHERE exchange_name = ? AND queue_name = ? AND binding_key = ? "; - private static final String INSERT_INTO_EXCHANGE = "INSERT INTO " + EXCHANGE_TABLE_NAME + " ( name, type, autodelete ) VALUES ( ?, ?, ? )"; - private static final String DELETE_FROM_EXCHANGE = "DELETE FROM " + EXCHANGE_TABLE_NAME + " WHERE name = ?"; - private static final String FIND_EXCHANGE = "SELECT name FROM " + EXCHANGE_TABLE_NAME + " WHERE name = ?"; - private static final String INSERT_INTO_BINDINGS = "INSERT INTO " + BINDINGS_TABLE_NAME + " ( exchange_name, queue_name, binding_key, arguments ) values ( ?, ?, ?, ? )"; - private static final String DELETE_FROM_BINDINGS = "DELETE FROM " + BINDINGS_TABLE_NAME + " WHERE exchange_name = ? AND queue_name = ? AND binding_key = ?"; - private static final String INSERT_INTO_QUEUE = "INSERT INTO " + QUEUE_TABLE_NAME + " (name, owner, exclusive, arguments) VALUES (?, ?, ?, ?)"; - private static final String DELETE_FROM_QUEUE = "DELETE FROM " + QUEUE_TABLE_NAME + " WHERE name = ?"; - - private static final String CREATE_QUEUE_ENTRY_TABLE = "CREATE TABLE "+QUEUE_ENTRY_TABLE_NAME+" ( queue_name varchar(255) not null, message_id bigint not null, PRIMARY KEY (queue_name, message_id) )"; - private static final String INSERT_INTO_QUEUE_ENTRY = "INSERT INTO " + QUEUE_ENTRY_TABLE_NAME + " (queue_name, message_id) values (?,?)"; - private static final String DELETE_FROM_QUEUE_ENTRY = "DELETE FROM " + QUEUE_ENTRY_TABLE_NAME + " WHERE queue_name = ? AND message_id =?"; - private static final String SELECT_FROM_QUEUE_ENTRY = "SELECT queue_name, message_id FROM " + QUEUE_ENTRY_TABLE_NAME + " ORDER BY queue_name, message_id"; - - - private static final String CREATE_META_DATA_TABLE = "CREATE TABLE "+META_DATA_TABLE_NAME+" ( message_id bigint not null, meta_data blob, PRIMARY KEY ( message_id ) )"; - private static final String CREATE_MESSAGE_CONTENT_TABLE = "CREATE TABLE "+MESSAGE_CONTENT_TABLE_NAME+" ( message_id bigint not null, offset int not null, last_byte int not null, content blob , PRIMARY KEY (message_id, offset) )"; - - private static final String INSERT_INTO_MESSAGE_CONTENT = "INSERT INTO " + MESSAGE_CONTENT_TABLE_NAME + "( message_id, offset, last_byte, content ) values (?, ?, ?, ?)"; - private static final String SELECT_FROM_MESSAGE_CONTENT = - "SELECT offset, content FROM " + MESSAGE_CONTENT_TABLE_NAME + " WHERE message_id = ? AND last_byte > ? AND offset < ? ORDER BY message_id, offset"; - private static final String DELETE_FROM_MESSAGE_CONTENT = "DELETE FROM " + MESSAGE_CONTENT_TABLE_NAME + " WHERE message_id = ?"; - - private static final String INSERT_INTO_META_DATA = "INSERT INTO " + META_DATA_TABLE_NAME + "( message_id , meta_data ) values (?, ?)";; - private static final String SELECT_FROM_META_DATA = - "SELECT meta_data FROM " + META_DATA_TABLE_NAME + " WHERE message_id = ?"; - private static final String DELETE_FROM_META_DATA = "DELETE FROM " + META_DATA_TABLE_NAME + " WHERE message_id = ?"; - private static final String SELECT_ALL_FROM_META_DATA = "SELECT message_id, meta_data FROM " + META_DATA_TABLE_NAME; - - private static final String CREATE_LINKS_TABLE = - "CREATE TABLE "+LINKS_TABLE_NAME+" ( id_lsb bigint not null," - + " id_msb bigint not null," - + " create_time bigint not null," - + " arguments blob, PRIMARY KEY ( id_lsb, id_msb ))"; - private static final String SELECT_FROM_LINKS = - "SELECT create_time, arguments FROM " + LINKS_TABLE_NAME + " WHERE id_lsb = ? and id_msb"; - private static final String DELETE_FROM_LINKS = "DELETE FROM " + LINKS_TABLE_NAME - + " WHERE id_lsb = ? and id_msb = ?"; - private static final String SELECT_ALL_FROM_LINKS = "SELECT id_lsb, id_msb, create_time, " - + "arguments FROM " + LINKS_TABLE_NAME; - private static final String FIND_LINK = "SELECT id_lsb, id_msb FROM " + LINKS_TABLE_NAME + " WHERE id_lsb = ? and" - + " id_msb = ?"; - private static final String INSERT_INTO_LINKS = "INSERT INTO " + LINKS_TABLE_NAME + "( id_lsb, " - + "id_msb, create_time, arguments ) values (?, ?, ?, ?)"; - - - private static final String CREATE_BRIDGES_TABLE = - "CREATE TABLE "+BRIDGES_TABLE_NAME+" ( id_lsb bigint not null," - + " id_msb bigint not null," - + " create_time bigint not null," - + " link_id_lsb bigint not null," - + " link_id_msb bigint not null," - + " arguments blob, PRIMARY KEY ( id_lsb, id_msb ))"; - private static final String SELECT_FROM_BRIDGES = - "SELECT create_time, link_id_lsb, link_id_msb, arguments FROM " - + BRIDGES_TABLE_NAME + " WHERE id_lsb = ? and id_msb = ?"; - private static final String DELETE_FROM_BRIDGES = "DELETE FROM " + BRIDGES_TABLE_NAME - + " WHERE id_lsb = ? and id_msb = ?"; - private static final String SELECT_ALL_FROM_BRIDGES = "SELECT id_lsb, id_msb, " - + " create_time," - + " link_id_lsb, link_id_msb, " - + "arguments FROM " + BRIDGES_TABLE_NAME - + " WHERE link_id_lsb = ? and link_id_msb = ?"; - private static final String FIND_BRIDGE = "SELECT id_lsb, id_msb FROM " + BRIDGES_TABLE_NAME + - " WHERE id_lsb = ? and id_msb = ?"; - private static final String INSERT_INTO_BRIDGES = "INSERT INTO " + BRIDGES_TABLE_NAME + "( id_lsb, id_msb, " - + "create_time, " - + "link_id_lsb, link_id_msb, " - + "arguments )" - + " values (?, ?, ?, ?, ?, ?)"; - - - private static final String DERBY_SINGLE_DB_SHUTDOWN_CODE = "08006"; - - - private LogSubject _logSubject; - private boolean _configured; - - - private static final class CommitStoreFuture implements StoreFuture - { - public boolean isComplete() - { - return true; - } - - public void waitForCompletion() - { - - } - } - - private enum State - { - INITIAL, - CONFIGURING, - RECOVERING, - STARTED, - CLOSING, - CLOSED - } - - private State _state = State.INITIAL; - - - public void configureConfigStore(String name, - ConfigurationRecoveryHandler recoveryHandler, - Configuration storeConfiguration, - LogSubject logSubject) throws Exception - { - stateTransition(State.INITIAL, State.CONFIGURING); - _logSubject = logSubject; - CurrentActor.get().message(_logSubject, ConfigStoreMessages.CREATED(this.getClass().getName())); - - if(!_configured) - { - commonConfiguration(name, storeConfiguration, logSubject); - _configured = true; - } - - // this recovers durable exchanges, queues, and bindings - recover(recoveryHandler); - - - stateTransition(State.RECOVERING, State.STARTED); - - } - - - public void configureMessageStore(String name, - MessageStoreRecoveryHandler recoveryHandler, - Configuration storeConfiguration, - LogSubject logSubject) throws Exception - { - if(!_configured) - { - - _logSubject = logSubject; - } - - CurrentActor.get().message(_logSubject, MessageStoreMessages.CREATED(this.getClass().getName())); - - if(!_configured) - { - - commonConfiguration(name, storeConfiguration, logSubject); - _configured = true; - } - - recoverMessages(recoveryHandler); - - } - - - - public void configureTransactionLog(String name, - TransactionLogRecoveryHandler recoveryHandler, - Configuration storeConfiguration, - LogSubject logSubject) throws Exception - { - - if(!_configured) - { - _logSubject = logSubject; - } - CurrentActor.get().message(_logSubject, TransactionLogMessages.CREATED(this.getClass().getName())); - - if(!_configured) - { - - _logSubject = logSubject; - - commonConfiguration(name, storeConfiguration, logSubject); - _configured = true; - } - - recoverQueueEntries(recoveryHandler); - - } - - - - private void commonConfiguration(String name, Configuration storeConfiguration, LogSubject logSubject) - throws ClassNotFoundException, SQLException - { - initialiseDriver(); - - //Update to pick up QPID_WORK and use that as the default location not just derbyDB - - final String databasePath = storeConfiguration.getString(ENVIRONMENT_PATH_PROPERTY, System.getProperty("QPID_WORK") - + File.separator + "derbyDB"); - - File environmentPath = new File(databasePath); - if (!environmentPath.exists()) - { - if (!environmentPath.mkdirs()) - { - throw new IllegalArgumentException("Environment path " + environmentPath + " could not be read or created. " - + "Ensure the path is correct and that the permissions are correct."); - } - } - - CurrentActor.get().message(_logSubject, MessageStoreMessages.STORE_LOCATION(environmentPath.getAbsolutePath())); - - createOrOpenDatabase(name, databasePath); - } - - private static synchronized void initialiseDriver() throws ClassNotFoundException - { - if(DRIVER_CLASS == null) - { - DRIVER_CLASS = (Class) Class.forName(SQL_DRIVER_NAME); - } - } - - private void createOrOpenDatabase(String name, final String environmentPath) throws SQLException - { - //FIXME this the _vhost name should not be added here, but derby wont use an empty directory as was possibly just created. - _connectionURL = "jdbc:derby:" + environmentPath + "/" + name + ";create=true"; - - Connection conn = newAutoCommitConnection(); - - createVersionTable(conn); - createExchangeTable(conn); - createQueueTable(conn); - createBindingsTable(conn); - createQueueEntryTable(conn); - createMetaDataTable(conn); - createMessageContentTable(conn); - createLinkTable(conn); - createBridgeTable(conn); - - conn.close(); - } - - - - private void createVersionTable(final Connection conn) throws SQLException - { - if(!tableExists(DB_VERSION_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute(CREATE_DB_VERSION_TABLE); - } - finally - { - stmt.close(); - } - - PreparedStatement pstmt = conn.prepareStatement(INSERT_INTO_DB_VERSION); - try - { - pstmt.setInt(1, DB_VERSION); - pstmt.execute(); - } - finally - { - pstmt.close(); - } - } - - } - - - private void createExchangeTable(final Connection conn) throws SQLException - { - if(!tableExists(EXCHANGE_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute(CREATE_EXCHANGE_TABLE); - } - finally - { - stmt.close(); - } - } - } - - private void createQueueTable(final Connection conn) throws SQLException - { - if(!tableExists(QUEUE_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute(CREATE_QUEUE_TABLE); - } - finally - { - stmt.close(); - } - } - } - - private void createBindingsTable(final Connection conn) throws SQLException - { - if(!tableExists(BINDINGS_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute(CREATE_BINDINGS_TABLE); - } - finally - { - stmt.close(); - } - } - - } - - private void createQueueEntryTable(final Connection conn) throws SQLException - { - if(!tableExists(QUEUE_ENTRY_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute(CREATE_QUEUE_ENTRY_TABLE); - } - finally - { - stmt.close(); - } - } - - } - - private void createMetaDataTable(final Connection conn) throws SQLException - { - if(!tableExists(META_DATA_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute(CREATE_META_DATA_TABLE); - } - finally - { - stmt.close(); - } - } - - } - - - private void createMessageContentTable(final Connection conn) throws SQLException - { - if(!tableExists(MESSAGE_CONTENT_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute(CREATE_MESSAGE_CONTENT_TABLE); - } - finally - { - stmt.close(); - } - } - - } - - private void createLinkTable(final Connection conn) throws SQLException - { - if(!tableExists(LINKS_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute(CREATE_LINKS_TABLE); - } - finally - { - stmt.close(); - } - } - } - - - private void createBridgeTable(final Connection conn) throws SQLException - { - if(!tableExists(BRIDGES_TABLE_NAME, conn)) - { - Statement stmt = conn.createStatement(); - try - { - stmt.execute(CREATE_BRIDGES_TABLE); - } - finally - { - stmt.close(); - } - } - } - - - - - private boolean tableExists(final String tableName, final Connection conn) throws SQLException - { - PreparedStatement stmt = conn.prepareStatement(TABLE_EXISTANCE_QUERY); - try - { - stmt.setString(1, tableName); - ResultSet rs = stmt.executeQuery(); - try - { - return rs.next(); - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - - } - - public void recover(ConfigurationRecoveryHandler recoveryHandler) throws AMQException - { - stateTransition(State.CONFIGURING, State.RECOVERING); - - CurrentActor.get().message(_logSubject,MessageStoreMessages.RECOVERY_START()); - - try - { - ConfigurationRecoveryHandler.QueueRecoveryHandler qrh = recoveryHandler.begin(this); - loadQueues(qrh); - - ConfigurationRecoveryHandler.ExchangeRecoveryHandler erh = qrh.completeQueueRecovery(); - List exchanges = loadExchanges(erh); - ConfigurationRecoveryHandler.BindingRecoveryHandler brh = erh.completeExchangeRecovery(); - recoverBindings(brh, exchanges); - ConfigurationRecoveryHandler.BrokerLinkRecoveryHandler lrh = brh.completeBindingRecovery(); - recoverBrokerLinks(lrh); - } - catch (SQLException e) - { - - throw new AMQStoreException("Error recovering persistent state: " + e.getMessage(), e); - } - - - } - - private void recoverBrokerLinks(final ConfigurationRecoveryHandler.BrokerLinkRecoveryHandler lrh) - throws SQLException - { - _logger.info("Recovering broker links..."); - - Connection conn = null; - try - { - conn = newAutoCommitConnection(); - - PreparedStatement stmt = conn.prepareStatement(SELECT_ALL_FROM_LINKS); - - try - { - ResultSet rs = stmt.executeQuery(); - - try - { - - while(rs.next()) - { - UUID id = new UUID(rs.getLong(2), rs.getLong(1)); - long createTime = rs.getLong(3); - Blob argumentsAsBlob = rs.getBlob(4); - - byte[] dataAsBytes = argumentsAsBlob.getBytes(1,(int) argumentsAsBlob.length()); - - DataInputStream dis = new DataInputStream(new ByteArrayInputStream(dataAsBytes)); - int size = dis.readInt(); - - Map arguments = new HashMap(); - - for(int i = 0; i < size; i++) - { - arguments.put(dis.readUTF(), dis.readUTF()); - } - - ConfigurationRecoveryHandler.BridgeRecoveryHandler brh = lrh.brokerLink(id, createTime, arguments); - - recoverBridges(brh, id); - - } - } - catch (IOException e) - { - throw new SQLException(e.getMessage(), e); - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - - } - finally - { - if(conn != null) - { - conn.close(); - } - } - - } - - private void recoverBridges(final ConfigurationRecoveryHandler.BridgeRecoveryHandler brh, final UUID linkId) - throws SQLException - { - _logger.info("Recovering bridges for link " + linkId + "..."); - - Connection conn = null; - try - { - conn = newAutoCommitConnection(); - - PreparedStatement stmt = conn.prepareStatement(SELECT_ALL_FROM_BRIDGES); - - try - { - stmt.setLong(1, linkId.getLeastSignificantBits()); - stmt.setLong(2, linkId.getMostSignificantBits()); - - ResultSet rs = stmt.executeQuery(); - - try - { - - while(rs.next()) - { - UUID id = new UUID(rs.getLong(2), rs.getLong(1)); - long createTime = rs.getLong(3); - Blob argumentsAsBlob = rs.getBlob(6); - - byte[] dataAsBytes = argumentsAsBlob.getBytes(1,(int) argumentsAsBlob.length()); - - DataInputStream dis = new DataInputStream(new ByteArrayInputStream(dataAsBytes)); - int size = dis.readInt(); - - Map arguments = new HashMap(); - - for(int i = 0; i < size; i++) - { - arguments.put(dis.readUTF(), dis.readUTF()); - } - - brh.bridge(id, createTime, arguments); - - } - brh.completeBridgeRecoveryForLink(); - } - catch (IOException e) - { - throw new SQLException(e.getMessage(), e); - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - - } - finally - { - if(conn != null) - { - conn.close(); - } - } - - } - - private void loadQueues(ConfigurationRecoveryHandler.QueueRecoveryHandler qrh) throws SQLException - { - Connection conn = newAutoCommitConnection(); - try - { - Statement stmt = conn.createStatement(); - try - { - ResultSet rs = stmt.executeQuery(SELECT_FROM_QUEUE); - try - { - - while(rs.next()) - { - String queueName = rs.getString(1); - String owner = rs.getString(2); - boolean exclusive = rs.getBoolean(3); - Blob argumentsAsBlob = rs.getBlob(4); - - byte[] dataAsBytes = argumentsAsBlob.getBytes(1,(int) argumentsAsBlob.length()); - FieldTable arguments; - if(dataAsBytes.length > 0) - { - - try - { - arguments = new FieldTable(new DataInputStream(new ByteArrayInputStream(dataAsBytes)),dataAsBytes.length); - } - catch (IOException e) - { - throw new RuntimeException("IO Exception should not be thrown",e); - } - } - else - { - arguments = null; - } - - qrh.queue(queueName, owner, exclusive, arguments); - - } - - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - } - finally - { - conn.close(); - } - } - - - private List loadExchanges(ConfigurationRecoveryHandler.ExchangeRecoveryHandler erh) throws SQLException - { - - List exchanges = new ArrayList(); - Connection conn = null; - try - { - conn = newAutoCommitConnection(); - - Statement stmt = conn.createStatement(); - try - { - ResultSet rs = stmt.executeQuery(SELECT_FROM_EXCHANGE); - try - { - while(rs.next()) - { - String exchangeName = rs.getString(1); - String type = rs.getString(2); - boolean autoDelete = rs.getShort(3) != 0; - - exchanges.add(exchangeName); - - erh.exchange(exchangeName, type, autoDelete); - - } - return exchanges; - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - } - finally - { - if(conn != null) - { - conn.close(); - } - } - - } - - private void recoverBindings(ConfigurationRecoveryHandler.BindingRecoveryHandler brh, List exchanges) throws SQLException - { - _logger.info("Recovering bindings..."); - - Connection conn = null; - try - { - conn = newAutoCommitConnection(); - - PreparedStatement stmt = conn.prepareStatement(SELECT_FROM_BINDINGS); - - try - { - ResultSet rs = stmt.executeQuery(); - - try - { - - while(rs.next()) - { - String exchangeName = rs.getString(1); - String queueName = rs.getString(2); - String bindingKey = rs.getString(3); - Blob arguments = rs.getBlob(4); - java.nio.ByteBuffer buf; - - if(arguments != null && arguments.length() != 0) - { - byte[] argumentBytes = arguments.getBytes(1, (int) arguments.length()); - buf = java.nio.ByteBuffer.wrap(argumentBytes); - } - else - { - buf = null; - } - - brh.binding(exchangeName, queueName, bindingKey, buf); - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - - } - finally - { - if(conn != null) - { - conn.close(); - } - } - } - - - - public void close() throws Exception - { - CurrentActor.get().message(_logSubject,MessageStoreMessages.CLOSED()); - _closed.getAndSet(true); - - try - { - Connection conn = DriverManager.getConnection(_connectionURL + ";shutdown=true"); - // Shouldn't reach this point - shutdown=true should throw SQLException - conn.close(); - _logger.error("Unable to shut down the store"); - } - catch (SQLException e) - { - if (e.getSQLState().equalsIgnoreCase(DERBY_SINGLE_DB_SHUTDOWN_CODE)) - { - //expected and represents a clean shutdown of this database only, do nothing. - } - else - { - _logger.error("Exception whilst shutting down the store: " + e); - } - } - } - - public StoredMessage addMessage(StorableMessageMetaData metaData) - { - if(metaData.isPersistent()) - { - return new StoredDerbyMessage(_messageId.incrementAndGet(), metaData); - } - else - { - return new StoredMemoryMessage(_messageId.incrementAndGet(), metaData); - } - } - - public StoredMessage getMessage(long messageNumber) - { - return null; - } - - public void removeMessage(long messageId) - { - try - { - Connection conn = newConnection(); - try - { - PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_META_DATA); - try - { - stmt.setLong(1,messageId); - int results = stmt.executeUpdate(); - stmt.close(); - - if (results == 0) - { - _logger.warn("Message metadata not found for message id " + messageId); - } - - if (_logger.isDebugEnabled()) - { - _logger.debug("Deleted metadata for message " + messageId); - } - - stmt = conn.prepareStatement(DELETE_FROM_MESSAGE_CONTENT); - stmt.setLong(1,messageId); - results = stmt.executeUpdate(); - } - finally - { - stmt.close(); - } - conn.commit(); - } - catch(SQLException e) - { - try - { - conn.rollback(); - } - catch(SQLException t) - { - // ignore - we are re-throwing underlying exception - } - - throw e; - - } - finally - { - conn.close(); - } - } - catch (SQLException e) - { - throw new RuntimeException("Error removing message with id " + messageId + " from database: " + e.getMessage(), e); - } - - } - - public void createExchange(Exchange exchange) throws AMQStoreException - { - if (_state != State.RECOVERING) - { - try - { - Connection conn = newAutoCommitConnection(); - - try - { - - - PreparedStatement stmt = conn.prepareStatement(FIND_EXCHANGE); - try - { - stmt.setString(1, exchange.getNameShortString().toString()); - ResultSet rs = stmt.executeQuery(); - try - { - - // If we don't have any data in the result set then we can add this exchange - if (!rs.next()) - { - - PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_EXCHANGE); - try - { - insertStmt.setString(1, exchange.getName().toString()); - insertStmt.setString(2, exchange.getTypeShortString().asString()); - insertStmt.setShort(3, exchange.isAutoDelete() ? (short) 1 : (short) 0); - insertStmt.execute(); - } - finally - { - insertStmt.close(); - } - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - - } - finally - { - conn.close(); - } - } - catch (SQLException e) - { - throw new AMQStoreException("Error writing Exchange with name " + exchange.getNameShortString() + " to database: " + e.getMessage(), e); - } - } - - } - - public void removeExchange(Exchange exchange) throws AMQStoreException - { - - try - { - Connection conn = newAutoCommitConnection(); - try - { - PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_EXCHANGE); - try - { - stmt.setString(1, exchange.getNameShortString().toString()); - int results = stmt.executeUpdate(); - stmt.close(); - if(results == 0) - { - throw new AMQStoreException("Exchange " + exchange.getNameShortString() + " not found"); - } - } - finally - { - stmt.close(); - } - } - finally - { - conn.close(); - } - } - catch (SQLException e) - { - throw new AMQStoreException("Error deleting Exchange with name " + exchange.getNameShortString() + " from database: " + e.getMessage(), e); - } - } - - public void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) - throws AMQStoreException - { - if (_state != State.RECOVERING) - { - try - { - Connection conn = newAutoCommitConnection(); - - try - { - - PreparedStatement stmt = conn.prepareStatement(FIND_BINDING); - try - { - stmt.setString(1, exchange.getNameShortString().toString() ); - stmt.setString(2, queue.getNameShortString().toString()); - stmt.setString(3, routingKey == null ? null : routingKey.toString()); - - ResultSet rs = stmt.executeQuery(); - try - { - // If this binding is not already in the store then create it. - if (!rs.next()) - { - PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_BINDINGS); - try - { - insertStmt.setString(1, exchange.getNameShortString().toString() ); - insertStmt.setString(2, queue.getNameShortString().toString()); - insertStmt.setString(3, routingKey == null ? null : routingKey.toString()); - if(args != null) - { - // TODO - In Java 6 we could use create/set Blob - byte[] bytes = args.getDataAsBytes(); - ByteArrayInputStream bis = new ByteArrayInputStream(bytes); - insertStmt.setBinaryStream(4, bis, bytes.length); - } - else - { - insertStmt.setNull(4, Types.BLOB); - } - - insertStmt.executeUpdate(); - } - finally - { - insertStmt.close(); - } - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - } - finally - { - conn.close(); - } - } - catch (SQLException e) - { - throw new AMQStoreException("Error writing binding for AMQQueue with name " + queue.getNameShortString() + " to exchange " - + exchange.getNameShortString() + " to database: " + e.getMessage(), e); - } - - } - - - } - - public void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) - throws AMQStoreException - { - Connection conn = null; - PreparedStatement stmt = null; - - try - { - conn = newAutoCommitConnection(); - // exchange_name varchar(255) not null, queue_name varchar(255) not null, binding_key varchar(255), arguments blob - stmt = conn.prepareStatement(DELETE_FROM_BINDINGS); - stmt.setString(1, exchange.getNameShortString().toString() ); - stmt.setString(2, queue.getNameShortString().toString()); - stmt.setString(3, routingKey == null ? null : routingKey.toString()); - - int result = stmt.executeUpdate(); - - if(result != 1) - { - throw new AMQStoreException("Queue binding for queue with name " + queue.getNameShortString() + " to exchange " - + exchange.getNameShortString() + " not found"); - } - } - catch (SQLException e) - { - throw new AMQStoreException("Error removing binding for AMQQueue with name " + queue.getNameShortString() + " to exchange " - + exchange.getNameShortString() + " in database: " + e.getMessage(), e); - } - finally - { - closePreparedStatement(stmt); - closeConnection(conn); - } - } - - public void createQueue(AMQQueue queue) throws AMQStoreException - { - createQueue(queue, null); - } - - public void createQueue(AMQQueue queue, FieldTable arguments) throws AMQStoreException - { - _logger.debug("public void createQueue(AMQQueue queue = " + queue + "): called"); - - if (_state != State.RECOVERING) - { - try - { - Connection conn = newAutoCommitConnection(); - - PreparedStatement stmt = conn.prepareStatement(FIND_QUEUE); - try - { - stmt.setString(1, queue.getNameShortString().toString()); - ResultSet rs = stmt.executeQuery(); - try - { - - // If we don't have any data in the result set then we can add this queue - if (!rs.next()) - { - PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_QUEUE); - - try - { - String owner = queue.getOwner() == null ? null : queue.getOwner().toString(); - - insertStmt.setString(1, queue.getNameShortString().toString()); - insertStmt.setString(2, owner); - insertStmt.setBoolean(3,queue.isExclusive()); - - final byte[] underlying; - if(arguments != null) - { - underlying = arguments.getDataAsBytes(); - } - else - { - underlying = new byte[0]; - } - - ByteArrayInputStream bis = new ByteArrayInputStream(underlying); - insertStmt.setBinaryStream(4,bis,underlying.length); - - insertStmt.execute(); - } - finally - { - insertStmt.close(); - } - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - conn.close(); - - } - catch (SQLException e) - { - throw new AMQStoreException("Error writing AMQQueue with name " + queue.getNameShortString() + " to database: " + e.getMessage(), e); - } - } - } - - /** - * Updates the specified queue in the persistent store, IF it is already present. If the queue - * is not present in the store, it will not be added. - * - * NOTE: Currently only updates the exclusivity. - * - * @param queue The queue to update the entry for. - * @throws AMQStoreException If the operation fails for any reason. - */ - public void updateQueue(final AMQQueue queue) throws AMQStoreException - { - if (_state != State.RECOVERING) - { - try - { - Connection conn = newAutoCommitConnection(); - - try - { - PreparedStatement stmt = conn.prepareStatement(FIND_QUEUE); - try - { - stmt.setString(1, queue.getNameShortString().toString()); - - ResultSet rs = stmt.executeQuery(); - try - { - if (rs.next()) - { - PreparedStatement stmt2 = conn.prepareStatement(UPDATE_QUEUE_EXCLUSIVITY); - try - { - stmt2.setBoolean(1,queue.isExclusive()); - stmt2.setString(2, queue.getNameShortString().toString()); - - stmt2.execute(); - } - finally - { - stmt2.close(); - } - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - } - finally - { - conn.close(); - } - } - catch (SQLException e) - { - throw new AMQStoreException("Error updating AMQQueue with name " + queue.getNameShortString() + " to database: " + e.getMessage(), e); - } - } - - } - - /** - * Convenience method to create a new Connection configured for TRANSACTION_READ_COMMITED - * isolation and with auto-commit transactions enabled. - */ - private Connection newAutoCommitConnection() throws SQLException - { - final Connection connection = newConnection(); - try - { - connection.setAutoCommit(true); - } - catch (SQLException sqlEx) - { - - try - { - connection.close(); - } - finally - { - throw sqlEx; - } - } - - return connection; - } - - /** - * Convenience method to create a new Connection configured for TRANSACTION_READ_COMMITED - * isolation and with auto-commit transactions disabled. - */ - private Connection newConnection() throws SQLException - { - final Connection connection = DriverManager.getConnection(_connectionURL); - try - { - connection.setAutoCommit(false); - connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); - } - catch (SQLException sqlEx) - { - try - { - connection.close(); - } - finally - { - throw sqlEx; - } - } - return connection; - } - - public void removeQueue(final AMQQueue queue) throws AMQStoreException - { - AMQShortString name = queue.getNameShortString(); - _logger.debug("public void removeQueue(AMQShortString name = " + name + "): called"); - Connection conn = null; - PreparedStatement stmt = null; - try - { - conn = newAutoCommitConnection(); - stmt = conn.prepareStatement(DELETE_FROM_QUEUE); - stmt.setString(1, name.toString()); - int results = stmt.executeUpdate(); - - if (results == 0) - { - throw new AMQStoreException("Queue " + name + " not found"); - } - } - catch (SQLException e) - { - throw new AMQStoreException("Error deleting AMQQueue with name " + name + " from database: " + e.getMessage(), e); - } - finally - { - closePreparedStatement(stmt); - closeConnection(conn); - } - - - } - - public void createBrokerLink(final BrokerLink link) throws AMQStoreException - { - _logger.debug("public void createBrokerLink(BrokerLink = " + link + "): called"); - - if (_state != State.RECOVERING) - { - try - { - Connection conn = newAutoCommitConnection(); - - PreparedStatement stmt = conn.prepareStatement(FIND_LINK); - try - { - - stmt.setLong(1, link.getId().getLeastSignificantBits()); - stmt.setLong(2, link.getId().getMostSignificantBits()); - ResultSet rs = stmt.executeQuery(); - try - { - - // If we don't have any data in the result set then we can add this queue - if (!rs.next()) - { - PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_LINKS); - - try - { - - insertStmt.setLong(1, link.getId().getLeastSignificantBits()); - insertStmt.setLong(2, link.getId().getMostSignificantBits()); - insertStmt.setLong(3, link.getCreateTime()); - - byte[] argumentBytes = convertStringMapToBytes(link.getArguments()); - ByteArrayInputStream bis = new ByteArrayInputStream(argumentBytes); - - insertStmt.setBinaryStream(4,bis,argumentBytes.length); - - insertStmt.execute(); - } - finally - { - insertStmt.close(); - } - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - conn.close(); - - } - catch (SQLException e) - { - throw new AMQStoreException("Error writing " + link + " to database: " + e.getMessage(), e); - } - } - } - - private byte[] convertStringMapToBytes(final Map arguments) throws AMQStoreException - { - byte[] argumentBytes; - if(arguments == null) - { - argumentBytes = new byte[0]; - } - else - { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(bos); - - - try - { - dos.writeInt(arguments.size()); - for(Map.Entry arg : arguments.entrySet()) - { - dos.writeUTF(arg.getKey()); - dos.writeUTF(arg.getValue()); - } - } - catch (IOException e) - { - // This should never happen - throw new AMQStoreException(e.getMessage(), e); - } - argumentBytes = bos.toByteArray(); - } - return argumentBytes; - } - - public void deleteBrokerLink(final BrokerLink link) throws AMQStoreException - { - _logger.debug("public void deleteBrokerLink( " + link + "): called"); - Connection conn = null; - PreparedStatement stmt = null; - try - { - conn = newAutoCommitConnection(); - stmt = conn.prepareStatement(DELETE_FROM_LINKS); - stmt.setLong(1, link.getId().getLeastSignificantBits()); - stmt.setLong(2, link.getId().getMostSignificantBits()); - int results = stmt.executeUpdate(); - - if (results == 0) - { - throw new AMQStoreException("Link " + link + " not found"); - } - } - catch (SQLException e) - { - throw new AMQStoreException("Error deleting Link " + link + " from database: " + e.getMessage(), e); - } - finally - { - closePreparedStatement(stmt); - closeConnection(conn); - } - - - } - - public void createBridge(final Bridge bridge) throws AMQStoreException - { - _logger.debug("public void createBridge(BrokerLink = " + bridge + "): called"); - - if (_state != State.RECOVERING) - { - try - { - Connection conn = newAutoCommitConnection(); - - PreparedStatement stmt = conn.prepareStatement(FIND_BRIDGE); - try - { - - UUID id = bridge.getId(); - stmt.setLong(1, id.getLeastSignificantBits()); - stmt.setLong(2, id.getMostSignificantBits()); - ResultSet rs = stmt.executeQuery(); - try - { - - // If we don't have any data in the result set then we can add this queue - if (!rs.next()) - { - PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_BRIDGES); - - try - { - - insertStmt.setLong(1, id.getLeastSignificantBits()); - insertStmt.setLong(2, id.getMostSignificantBits()); - - insertStmt.setLong(3, bridge.getCreateTime()); - - UUID linkId = bridge.getLink().getId(); - insertStmt.setLong(4, linkId.getLeastSignificantBits()); - insertStmt.setLong(5, linkId.getMostSignificantBits()); - - byte[] argumentBytes = convertStringMapToBytes(bridge.getArguments()); - ByteArrayInputStream bis = new ByteArrayInputStream(argumentBytes); - - insertStmt.setBinaryStream(6,bis,argumentBytes.length); - - insertStmt.execute(); - } - finally - { - insertStmt.close(); - } - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - conn.close(); - - } - catch (SQLException e) - { - throw new AMQStoreException("Error writing " + bridge + " to database: " + e.getMessage(), e); - } - } - } - - public void deleteBridge(final Bridge bridge) throws AMQStoreException - { - _logger.debug("public void deleteBridge( " + bridge + "): called"); - Connection conn = null; - PreparedStatement stmt = null; - try - { - conn = newAutoCommitConnection(); - stmt = conn.prepareStatement(DELETE_FROM_BRIDGES); - stmt.setLong(1, bridge.getId().getLeastSignificantBits()); - stmt.setLong(2, bridge.getId().getMostSignificantBits()); - int results = stmt.executeUpdate(); - - if (results == 0) - { - throw new AMQStoreException("Bridge " + bridge + " not found"); - } - } - catch (SQLException e) - { - throw new AMQStoreException("Error deleting bridge " + bridge + " from database: " + e.getMessage(), e); - } - finally - { - closePreparedStatement(stmt); - closeConnection(conn); - } - - } - - public Transaction newTransaction() - { - return new DerbyTransaction(); - } - - public void enqueueMessage(ConnectionWrapper connWrapper, final TransactionLogResource queue, Long messageId) throws AMQStoreException - { - String name = queue.getResourceName(); - - Connection conn = connWrapper.getConnection(); - - - try - { - if (_logger.isDebugEnabled()) - { - _logger.debug("Enqueuing message " + messageId + " on queue " + name + "[Connection" + conn + "]"); - } - - PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_QUEUE_ENTRY); - try - { - stmt.setString(1,name); - stmt.setLong(2,messageId); - stmt.executeUpdate(); - } - finally - { - stmt.close(); - } - } - catch (SQLException e) - { - _logger.error("Failed to enqueue: " + e.getMessage(), e); - throw new AMQStoreException("Error writing enqueued message with id " + messageId + " for queue " + name - + " to database", e); - } - - } - - public void dequeueMessage(ConnectionWrapper connWrapper, final TransactionLogResource queue, Long messageId) throws AMQStoreException - { - String name = queue.getResourceName(); - - - Connection conn = connWrapper.getConnection(); - - - try - { - PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_QUEUE_ENTRY); - try - { - stmt.setString(1,name); - stmt.setLong(2,messageId); - int results = stmt.executeUpdate(); - - - - if(results != 1) - { - throw new AMQStoreException("Unable to find message with id " + messageId + " on queue " + name); - } - - if (_logger.isDebugEnabled()) - { - _logger.debug("Dequeuing message " + messageId + " on queue " + name ); - } - } - finally - { - stmt.close(); - } - } - catch (SQLException e) - { - _logger.error("Failed to dequeue: " + e.getMessage(), e); - throw new AMQStoreException("Error deleting enqueued message with id " + messageId + " for queue " + name - + " from database", e); - } - - } - - private static final class ConnectionWrapper - { - private final Connection _connection; - - public ConnectionWrapper(Connection conn) - { - _connection = conn; - } - - public Connection getConnection() - { - return _connection; - } - } - - - public void commitTran(ConnectionWrapper connWrapper) throws AMQStoreException - { - - try - { - Connection conn = connWrapper.getConnection(); - conn.commit(); - - if (_logger.isDebugEnabled()) - { - _logger.debug("commit tran completed"); - } - - conn.close(); - } - catch (SQLException e) - { - throw new AMQStoreException("Error commit tx: " + e.getMessage(), e); - } - finally - { - - } - } - - public StoreFuture commitTranAsync(ConnectionWrapper connWrapper) throws AMQStoreException - { - commitTran(connWrapper); - return new CommitStoreFuture(); - } - - public void abortTran(ConnectionWrapper connWrapper) throws AMQStoreException - { - if (connWrapper == null) - { - throw new AMQStoreException("Fatal internal error: transactional context is empty at abortTran"); - } - - if (_logger.isDebugEnabled()) - { - _logger.debug("abort tran called: " + connWrapper.getConnection()); - } - - try - { - Connection conn = connWrapper.getConnection(); - conn.rollback(); - conn.close(); - } - catch (SQLException e) - { - throw new AMQStoreException("Error aborting transaction: " + e.getMessage(), e); - } - - } - - public Long getNewMessageId() - { - return _messageId.incrementAndGet(); - } - - - private void storeMetaData(Connection conn, long messageId, StorableMessageMetaData metaData) - throws SQLException - { - if(_logger.isDebugEnabled()) - { - _logger.debug("Adding metadata for message " +messageId); - } - - PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_META_DATA); - try - { - stmt.setLong(1,messageId); - - final int bodySize = 1 + metaData.getStorableSize(); - byte[] underlying = new byte[bodySize]; - underlying[0] = (byte) metaData.getType().ordinal(); - java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(underlying); - buf.position(1); - buf = buf.slice(); - - metaData.writeToBuffer(0, buf); - ByteArrayInputStream bis = new ByteArrayInputStream(underlying); - try - { - stmt.setBinaryStream(2,bis,underlying.length); - int result = stmt.executeUpdate(); - - if(result == 0) - { - throw new RuntimeException("Unable to add meta data for message " +messageId); - } - } - finally - { - try - { - bis.close(); - } - catch (IOException e) - { - - throw new SQLException(e); - } - } - - } - finally - { - stmt.close(); - } - - } - - - - - private void recoverMessages(MessageStoreRecoveryHandler recoveryHandler) throws SQLException - { - Connection conn = newAutoCommitConnection(); - try - { - MessageStoreRecoveryHandler.StoredMessageRecoveryHandler messageHandler = recoveryHandler.begin(); - - Statement stmt = conn.createStatement(); - try - { - ResultSet rs = stmt.executeQuery(SELECT_ALL_FROM_META_DATA); - try - { - - long maxId = 0; - - while(rs.next()) - { - - long messageId = rs.getLong(1); - Blob dataAsBlob = rs.getBlob(2); - - if(messageId > maxId) - { - maxId = messageId; - } - - byte[] dataAsBytes = dataAsBlob.getBytes(1,(int) dataAsBlob.length()); - java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(dataAsBytes); - buf.position(1); - buf = buf.slice(); - MessageMetaDataType type = MessageMetaDataType.values()[dataAsBytes[0]]; - StorableMessageMetaData metaData = type.getFactory().createMetaData(buf); - StoredDerbyMessage message = new StoredDerbyMessage(messageId, metaData, false); - messageHandler.message(message); - } - - _messageId.set(maxId); - - messageHandler.completeMessageRecovery(); - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - } - finally - { - conn.close(); - } - } - - - - private void recoverQueueEntries(TransactionLogRecoveryHandler recoveryHandler) throws SQLException - { - Connection conn = newAutoCommitConnection(); - try - { - TransactionLogRecoveryHandler.QueueEntryRecoveryHandler queueEntryHandler = recoveryHandler.begin(this); - - Statement stmt = conn.createStatement(); - try - { - ResultSet rs = stmt.executeQuery(SELECT_FROM_QUEUE_ENTRY); - try - { - while(rs.next()) - { - - String queueName = rs.getString(1); - long messageId = rs.getLong(2); - queueEntryHandler.queueEntry(queueName,messageId); - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - - queueEntryHandler.completeQueueEntryRecovery(); - } - finally - { - conn.close(); - } - } - - StorableMessageMetaData getMetaData(long messageId) throws SQLException - { - - Connection conn = newAutoCommitConnection(); - try - { - PreparedStatement stmt = conn.prepareStatement(SELECT_FROM_META_DATA); - try - { - stmt.setLong(1,messageId); - ResultSet rs = stmt.executeQuery(); - try - { - - if(rs.next()) - { - Blob dataAsBlob = rs.getBlob(1); - - byte[] dataAsBytes = dataAsBlob.getBytes(1,(int) dataAsBlob.length()); - java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(dataAsBytes); - buf.position(1); - buf = buf.slice(); - MessageMetaDataType type = MessageMetaDataType.values()[dataAsBytes[0]]; - StorableMessageMetaData metaData = type.getFactory().createMetaData(buf); - - return metaData; - } - else - { - throw new RuntimeException("Meta data not found for message with id " + messageId); - } - } - finally - { - rs.close(); - } - } - finally - { - stmt.close(); - } - } - finally - { - conn.close(); - } - } - - - private void addContent(Connection conn, long messageId, int offset, ByteBuffer src) - { - if(_logger.isDebugEnabled()) - { - _logger.debug("Adding content chunk offset " + offset + " for message " +messageId); - } - PreparedStatement stmt = null; - - try - { - src = src.slice(); - - byte[] chunkData = new byte[src.limit()]; - src.duplicate().get(chunkData); - - stmt = conn.prepareStatement(INSERT_INTO_MESSAGE_CONTENT); - stmt.setLong(1,messageId); - stmt.setInt(2, offset); - stmt.setInt(3, offset+chunkData.length); - - - // TODO in Java 6 we could just use blobs - - ByteArrayInputStream bis = new ByteArrayInputStream(chunkData); - stmt.setBinaryStream(4, bis, chunkData.length); - stmt.executeUpdate(); - } - catch (SQLException e) - { - closeConnection(conn); - throw new RuntimeException("Error adding content chunk offset " + offset + " for message " + messageId + ": " + e.getMessage(), e); - } - finally - { - closePreparedStatement(stmt); - } - - } - - - public int getContent(long messageId, int offset, ByteBuffer dst) - { - Connection conn = null; - PreparedStatement stmt = null; - - try - { - conn = newAutoCommitConnection(); - - stmt = conn.prepareStatement(SELECT_FROM_MESSAGE_CONTENT); - stmt.setLong(1,messageId); - stmt.setInt(2, offset); - stmt.setInt(3, offset+dst.remaining()); - ResultSet rs = stmt.executeQuery(); - - int written = 0; - - while(rs.next()) - { - int offsetInMessage = rs.getInt(1); - Blob dataAsBlob = rs.getBlob(2); - - final int size = (int) dataAsBlob.length(); - byte[] dataAsBytes = dataAsBlob.getBytes(1, size); - - int posInArray = offset + written - offsetInMessage; - int count = size - posInArray; - if(count > dst.remaining()) - { - count = dst.remaining(); - } - dst.put(dataAsBytes,posInArray,count); - written+=count; - - if(dst.remaining() == 0) - { - break; - } - } - - return written; - - } - catch (SQLException e) - { - throw new RuntimeException("Error retrieving content from offset " + offset + " for message " + messageId + ": " + e.getMessage(), e); - } - finally - { - closePreparedStatement(stmt); - closeConnection(conn); - } - - - } - - public boolean isPersistent() - { - return true; - } - - - private synchronized void stateTransition(State requiredState, State newState) throws AMQStoreException - { - if (_state != requiredState) - { - throw new AMQStoreException("Cannot transition to the state: " + newState + "; need to be in state: " + requiredState - + "; currently in state: " + _state); - } - - _state = newState; - } - - - private class DerbyTransaction implements Transaction - { - private final ConnectionWrapper _connWrapper; - - - private DerbyTransaction() - { - try - { - _connWrapper = new ConnectionWrapper(newConnection()); - } - catch (SQLException e) - { - throw new RuntimeException(e); - } - } - - public void enqueueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException - { - if(message.getStoredMessage() instanceof StoredDerbyMessage) - { - try - { - ((StoredDerbyMessage)message.getStoredMessage()).store(_connWrapper.getConnection()); - } - catch (SQLException e) - { - throw new AMQStoreException("Exception on enqueuing message " + _messageId, e); - } - } - - DerbyMessageStore.this.enqueueMessage(_connWrapper, queue, message.getMessageNumber()); - } - - public void dequeueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException - { - DerbyMessageStore.this.dequeueMessage(_connWrapper, queue, message.getMessageNumber()); - - } - - public void commitTran() throws AMQStoreException - { - DerbyMessageStore.this.commitTran(_connWrapper); - } - - public StoreFuture commitTranAsync() throws AMQStoreException - { - return DerbyMessageStore.this.commitTranAsync(_connWrapper); - } - - public void abortTran() throws AMQStoreException - { - DerbyMessageStore.this.abortTran(_connWrapper); - } - } - - private class StoredDerbyMessage implements StoredMessage - { - - private final long _messageId; - private StorableMessageMetaData _metaData; - private volatile SoftReference _metaDataRef; - private byte[] _data; - private volatile SoftReference _dataRef; - - - StoredDerbyMessage(long messageId, StorableMessageMetaData metaData) - { - this(messageId, metaData, true); - } - - - StoredDerbyMessage(long messageId, - StorableMessageMetaData metaData, boolean persist) - { - _messageId = messageId; - - - _metaDataRef = new SoftReference(metaData); - if(persist) - { - _metaData = metaData; - } - } - - public StorableMessageMetaData getMetaData() - { - StorableMessageMetaData metaData = _metaData == null ? _metaDataRef.get() : _metaData; - if(metaData == null) - { - try - { - metaData = DerbyMessageStore.this.getMetaData(_messageId); - } - catch (SQLException e) - { - throw new RuntimeException(e); - } - _metaDataRef = new SoftReference(metaData); - } - - return metaData; - } - - public long getMessageNumber() - { - return _messageId; - } - - public void addContent(int offsetInMessage, java.nio.ByteBuffer src) - { - src = src.slice(); - - if(_data == null) - { - _data = new byte[src.remaining()]; - _dataRef = new SoftReference(_data); - src.duplicate().get(_data); - } - else - { - byte[] oldData = _data; - _data = new byte[oldData.length + src.remaining()]; - _dataRef = new SoftReference(_data); - - System.arraycopy(oldData,0,_data,0,oldData.length); - src.duplicate().get(_data, oldData.length, src.remaining()); - } - - } - - public int getContent(int offsetInMessage, java.nio.ByteBuffer dst) - { - byte[] data = _dataRef == null ? null : _dataRef.get(); - if(data != null) - { - int length = Math.min(dst.remaining(), data.length - offsetInMessage); - dst.put(data, offsetInMessage, length); - return length; - } - else - { - return DerbyMessageStore.this.getContent(_messageId, offsetInMessage, dst); - } - } - - - public ByteBuffer getContent(int offsetInMessage, int size) - { - ByteBuffer buf = ByteBuffer.allocate(size); - getContent(offsetInMessage, buf); - buf.position(0); - return buf; - } - - public synchronized StoreFuture flushToStore() - { - try - { - if(_metaData != null) - { - Connection conn = newConnection(); - - store(conn); - - conn.commit(); - conn.close(); - } - } - catch (SQLException e) - { - if(_logger.isDebugEnabled()) - { - _logger.debug("Error when trying to flush message " + _messageId + " to store: " + e); - } - throw new RuntimeException(e); - } - return IMMEDIATE_FUTURE; - } - - private synchronized void store(final Connection conn) throws SQLException - { - if(_metaData != null) - { - try - { - storeMetaData(conn, _messageId, _metaData); - DerbyMessageStore.this.addContent(conn, _messageId, 0, - _data == null ? ByteBuffer.allocate(0) : ByteBuffer.wrap(_data)); - } - finally - { - _metaData = null; - _data = null; - } - } - - if(_logger.isDebugEnabled()) - { - _logger.debug("Storing message " + _messageId + " to store"); - } - } - - public void remove() - { - DerbyMessageStore.this.removeMessage(_messageId); - } - } - - private void closeConnection(final Connection conn) - { - if(conn != null) - { - try - { - conn.close(); - } - catch (SQLException e) - { - _logger.error("Problem closing connection", e); - } - } - } - - private void closePreparedStatement(final PreparedStatement stmt) - { - if (stmt != null) - { - try - { - stmt.close(); - } - catch(SQLException e) - { - _logger.error("Problem closing prepared statement", e); - } - } - } - -} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java index 123ecd8145..655887e5c2 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/DurableConfigurationStore.java @@ -23,12 +23,11 @@ package org.apache.qpid.server.store; import org.apache.commons.configuration.Configuration; import org.apache.qpid.AMQStoreException; -import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.binding.Binding; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.federation.Bridge; import org.apache.qpid.server.federation.BrokerLink; -import org.apache.qpid.server.logging.LogSubject; import org.apache.qpid.server.queue.AMQQueue; public interface DurableConfigurationStore @@ -36,23 +35,21 @@ public interface DurableConfigurationStore public static interface Source { - DurableConfigurationStore getDurableConfigurationStore(); + DurableConfigurationStore getMessageStore(); } /** * Called after instantiation in order to configure the message store. A particular implementation can define * whatever parameters it wants. * - * @param name The name to be used by this storem + * @param name The name to be used by this store * @param recoveryHandler Handler to be called as the store recovers on start up * @param config The apache commons configuration object. - * * @throws Exception If any error occurs that means the store is unable to configure itself. */ void configureConfigStore(String name, ConfigurationRecoveryHandler recoveryHandler, - Configuration config, - LogSubject logSubject) throws Exception; + Configuration config) throws Exception; /** * Makes the specified exchange persistent. * @@ -72,28 +69,22 @@ public interface DurableConfigurationStore void removeExchange(Exchange exchange) throws AMQStoreException; /** - * Binds the specified queue to an exchange with a routing key. + * Store the queue binding. * - * @param exchange The exchange to bind to. - * @param routingKey The routing key to bind by. - * @param queue The queue to bind. - * @param args Additional parameters. + * @param binding queue binding * * @throws AMQStoreException if the operation fails for any reason. */ - void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException; + void bindQueue(Binding binding) throws AMQStoreException; /** - * Unbinds the specified from an exchange under a particular routing key. + * Removes queue binding * - * @param exchange The exchange to unbind from. - * @param routingKey The routing key to unbind. - * @param queue The queue to unbind. - * @param args Additonal parameters. + * @param binding queue binding to remove * * @throws AMQStoreException If the operation fails for any reason. */ - void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException; + void unbindQueue(Binding binding) throws AMQStoreException; /** * Makes the specified queue persistent. diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/Event.java b/java/broker/src/main/java/org/apache/qpid/server/store/Event.java new file mode 100644 index 0000000000..bbde11ab4c --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/Event.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.store; + +public enum Event +{ + BEFORE_INIT, + AFTER_INIT, + BEFORE_ACTIVATE, + AFTER_ACTIVATE, + BEFORE_PASSIVATE, + AFTER_PASSIVATE, + BEFORE_CLOSE, + AFTER_CLOSE +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/EventListener.java b/java/broker/src/main/java/org/apache/qpid/server/store/EventListener.java new file mode 100644 index 0000000000..33ae7b5b24 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/EventListener.java @@ -0,0 +1,25 @@ +/* + * 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.store; + +public interface EventListener +{ + public void event(Event event); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/EventManager.java b/java/broker/src/main/java/org/apache/qpid/server/store/EventManager.java new file mode 100644 index 0000000000..21ae3924b8 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/EventManager.java @@ -0,0 +1,55 @@ +/* + * 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.store; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + +public class EventManager +{ + private Map> _listeners = new EnumMap> (Event.class); + + public synchronized void addEventListener(EventListener listener, Event... events) + { + for(Event event : events) + { + List list = _listeners.get(event); + if(list == null) + { + list = new ArrayList(); + _listeners.put(event,list); + } + list.add(listener); + } + } + + public synchronized void notifyEvent(Event event) + { + if (_listeners.containsKey(event)) + { + for (EventListener listener : _listeners.get(event)) + { + listener.event(event); + } + } + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java index 546a81a050..59624b7a75 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java @@ -21,200 +21,117 @@ package org.apache.qpid.server.store; import org.apache.commons.configuration.Configuration; -import org.apache.log4j.Logger; - -import org.apache.qpid.AMQException; import org.apache.qpid.AMQStoreException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.federation.Bridge; -import org.apache.qpid.server.federation.BrokerLink; -import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.messages.ConfigStoreMessages; -import org.apache.qpid.server.logging.messages.MessageStoreMessages; import org.apache.qpid.server.message.EnqueableMessage; -import org.apache.qpid.server.queue.AMQQueue; -import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; -/** A simple message store that stores the messages in a threadsafe structure in memory. */ -public class MemoryMessageStore implements MessageStore, DurableConfigurationStore +/** A simple message store that stores the messages in a thread-safe structure in memory. */ +public class MemoryMessageStore extends NullMessageStore { - private static final Logger _log = Logger.getLogger(MemoryMessageStore.class); - - private static final int DEFAULT_HASHTABLE_CAPACITY = 50000; - - private static final String HASHTABLE_CAPACITY_CONFIG = "hashtable-capacity"; - - private final AtomicLong _messageId = new AtomicLong(1); - private AtomicBoolean _closed = new AtomicBoolean(false); - private LogSubject _logSubject; + private final AtomicBoolean _closed = new AtomicBoolean(false); private static final Transaction IN_MEMORY_TRANSACTION = new Transaction() { + @Override + public StoreFuture commitTranAsync() throws AMQStoreException + { + return StoreFuture.IMMEDIATE_FUTURE; + } + + @Override public void enqueueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException { } + @Override public void dequeueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException { } + @Override public void commitTran() throws AMQStoreException { } - public StoreFuture commitTranAsync() throws AMQStoreException + @Override + public void abortTran() throws AMQStoreException { - return IMMEDIATE_FUTURE; } - public void abortTran() throws AMQStoreException + @Override + public void removeXid(long format, byte[] globalId, byte[] branchId) { } - }; - - public void configureConfigStore(String name, ConfigurationRecoveryHandler handler, Configuration configuration, LogSubject logSubject) throws Exception - { - _logSubject = logSubject; - CurrentActor.get().message(_logSubject, ConfigStoreMessages.CREATED(this.getClass().getName())); - - - } - - public void configureMessageStore(String name, - MessageStoreRecoveryHandler recoveryHandler, - Configuration config, - LogSubject logSubject) throws Exception - { - if(_logSubject == null) + @Override + public void recordXid(long format, byte[] globalId, byte[] branchId, Record[] enqueues, Record[] dequeues) { - _logSubject = logSubject; } - int hashtableCapacity = config.getInt(name + "." + HASHTABLE_CAPACITY_CONFIG, DEFAULT_HASHTABLE_CAPACITY); - _log.info("Using capacity " + hashtableCapacity + " for hash tables"); - CurrentActor.get().message(_logSubject, MessageStoreMessages.CREATED(this.getClass().getName())); - } - - public void close() throws Exception - { - _closed.getAndSet(true); - CurrentActor.get().message(_logSubject,MessageStoreMessages.CLOSED()); - - } - - public StoredMessage addMessage(StorableMessageMetaData metaData) - { - final long id = _messageId.getAndIncrement(); - StoredMemoryMessage message = new StoredMemoryMessage(id, metaData); - - return message; - } - - - public void createExchange(Exchange exchange) throws AMQStoreException - { - - } - - public void removeExchange(Exchange exchange) throws AMQStoreException - { - - } - - public void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException - { - - } - - public void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException - { - - } - - - public void createQueue(AMQQueue queue) throws AMQStoreException - { - // Not requred to do anything - } + }; - public void createQueue(AMQQueue queue, FieldTable arguments) throws AMQStoreException - { - // Not required to do anything - } + private final StateManager _stateManager; + private final EventManager _eventManager = new EventManager(); - public void removeQueue(final AMQQueue queue) throws AMQStoreException - { - // Not required to do anything - } - - public void updateQueue(final AMQQueue queue) throws AMQStoreException + public MemoryMessageStore() { - // Not required to do anything + _stateManager = new StateManager(_eventManager); } - public void createBrokerLink(final BrokerLink link) throws AMQStoreException + @Override + public void configureConfigStore(String name, ConfigurationRecoveryHandler recoveryHandler, Configuration config) throws Exception { - + _stateManager.attainState(State.CONFIGURING); } - public void deleteBrokerLink(final BrokerLink link) throws AMQStoreException + @Override + public void configureMessageStore(String name, MessageStoreRecoveryHandler recoveryHandler, TransactionLogRecoveryHandler tlogRecoveryHandler, Configuration config) throws Exception { - + _stateManager.attainState(State.CONFIGURED); } - public void createBridge(final Bridge bridge) throws AMQStoreException + @Override + public void activate() throws Exception { - + _stateManager.attainState(State.RECOVERING); + + _stateManager.attainState(State.ACTIVE); } - public void deleteBridge(final Bridge bridge) throws AMQStoreException + @Override + public StoredMessage addMessage(StorableMessageMetaData metaData) { + final long id = _messageId.getAndIncrement(); + StoredMemoryMessage message = new StoredMemoryMessage(id, metaData); + return message; } - public void configureTransactionLog(String name, - TransactionLogRecoveryHandler recoveryHandler, - Configuration storeConfiguration, - LogSubject logSubject) throws Exception - { - //To change body of implemented methods use File | Settings | File Templates. - } - + @Override public Transaction newTransaction() { return IN_MEMORY_TRANSACTION; } - - public List createQueues() throws AMQException + @Override + public boolean isPersistent() { - return null; + return false; } - public Long getNewMessageId() + @Override + public void close() throws Exception { - return _messageId.getAndIncrement(); + _stateManager.attainState(State.CLOSING); + _closed.getAndSet(true); + _stateManager.attainState(State.CLOSED); } - public boolean isPersistent() + @Override + public void addEventListener(EventListener eventListener, Event... events) { - return false; - } - - private void checkNotClosed() throws MessageStoreClosedException - { - if (_closed.get()) - { - throw new MessageStoreClosedException(); - } + _eventManager.addEventListener(eventListener, events); } - - } diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStoreFactory.java b/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStoreFactory.java new file mode 100644 index 0000000000..8724f102c6 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStoreFactory.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.store; + +public class MemoryMessageStoreFactory implements MessageStoreFactory +{ + + @Override + public MessageStore createMessageStore() + { + return new MemoryMessageStore(); + } + + @Override + public String getStoreClassName() + { + return MemoryMessageStore.class.getSimpleName(); + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/MessageMetaDataType.java b/java/broker/src/main/java/org/apache/qpid/server/store/MessageMetaDataType.java index 428bb1e41b..0fab60b6f3 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/store/MessageMetaDataType.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/MessageMetaDataType.java @@ -22,13 +22,16 @@ package org.apache.qpid.server.store; import org.apache.qpid.server.message.MessageMetaData; import org.apache.qpid.server.message.MessageMetaData_0_10; +import org.apache.qpid.server.message.MessageMetaData_1_0; import java.nio.ByteBuffer; public enum MessageMetaDataType { META_DATA_0_8 { public Factory getFactory() { return MessageMetaData.FACTORY; } }, - META_DATA_0_10 { public Factory getFactory() { return MessageMetaData_0_10.FACTORY; } }; + META_DATA_0_10 { public Factory getFactory() { return MessageMetaData_0_10.FACTORY; } }, + META_DATA_1_0 { public Factory getFactory() { return MessageMetaData_1_0.FACTORY; } }; + public static interface Factory diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java index 2fecd5d4be..cf08ee00ff 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java @@ -22,52 +22,28 @@ package org.apache.qpid.server.store; import org.apache.commons.configuration.Configuration; -import org.apache.qpid.AMQStoreException; -import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.server.message.EnqueableMessage; - /** * MessageStore defines the interface to a storage area, which can be used to preserve the state of messages. * */ -public interface MessageStore +public interface MessageStore extends DurableConfigurationStore { - StoreFuture IMMEDIATE_FUTURE = new StoreFuture() - { - public boolean isComplete() - { - return true; - } - - public void waitForCompletion() - { - - } - }; - - /** * Called after instantiation in order to configure the message store. A particular implementation can define * whatever parameters it wants. * - * @param name The name to be used by this storem - * @param recoveryHandler Handler to be called as the store recovers on start up + * @param name The name to be used by this store + * @param messageRecoveryHandler Handler to be called as the store recovers on start up + * @param tlogRecoveryHandler * @param config The apache commons configuration object. - * * @throws Exception If any error occurs that means the store is unable to configure itself. */ void configureMessageStore(String name, - MessageStoreRecoveryHandler recoveryHandler, - Configuration config, - LogSubject logSubject) throws Exception; - - /** - * Called to close and cleanup any resources used by the message store. - * - * @throws Exception If the close fails. - */ - void close() throws Exception; + MessageStoreRecoveryHandler messageRecoveryHandler, + TransactionLogRecoveryHandler tlogRecoveryHandler, + Configuration config) throws Exception; + void activate() throws Exception; public StoredMessage addMessage(T metaData); @@ -79,70 +55,16 @@ public interface MessageStore */ boolean isPersistent(); - - - public static interface Transaction - { - /** - * Places a message onto a specified queue, in a given transactional context. - * - * - * - * @param queue The queue to place the message on. - * @param message - * @throws org.apache.qpid.AMQStoreException If the operation fails for any reason. - */ - void enqueueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException; - - /** - * Extracts a message from a specified queue, in a given transactional context. - * - * @param queue The queue to place the message on. - * @param message The message to dequeue. - * @throws AMQStoreException If the operation fails for any reason, or if the specified message does not exist. - */ - void dequeueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException; - - - /** - * Commits all operations performed within a given transactional context. - * - * @throws AMQStoreException If the operation fails for any reason. - */ - void commitTran() throws AMQStoreException; - - /** - * Commits all operations performed within a given transactional context. - * - * @throws AMQStoreException If the operation fails for any reason. - */ - StoreFuture commitTranAsync() throws AMQStoreException; - - /** - * Abandons all operations performed within a given transactional context. - * - * @throws AMQStoreException If the operation fails for any reason. - */ - void abortTran() throws AMQStoreException; - - - - } - - public void configureTransactionLog(String name, - TransactionLogRecoveryHandler recoveryHandler, - Configuration storeConfiguration, - LogSubject logSubject) throws Exception; - Transaction newTransaction(); + /** + * Called to close and cleanup any resources used by the message store. + * + * @throws Exception If the close fails. + */ + void close() throws Exception; + void addEventListener(EventListener eventListener, Event... events); - public static interface StoreFuture - { - boolean isComplete(); - - void waitForCompletion(); - } - + String getStoreLocation(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/MessageStoreConstants.java b/java/broker/src/main/java/org/apache/qpid/server/store/MessageStoreConstants.java new file mode 100644 index 0000000000..aba7456a44 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/MessageStoreConstants.java @@ -0,0 +1,27 @@ +/* + * 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.store; + +public class MessageStoreConstants +{ + + public static final String ENVIRONMENT_PATH_PROPERTY = "environment-path"; + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/MessageStoreFactory.java b/java/broker/src/main/java/org/apache/qpid/server/store/MessageStoreFactory.java new file mode 100644 index 0000000000..a35db62b03 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/MessageStoreFactory.java @@ -0,0 +1,27 @@ +/* + * 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.store; + +public interface MessageStoreFactory +{ + MessageStore createMessageStore(); + + String getStoreClassName(); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/NullMessageStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/NullMessageStore.java new file mode 100644 index 0000000000..34c7d2d933 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/NullMessageStore.java @@ -0,0 +1,146 @@ +/* + * 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.store; + +import org.apache.commons.configuration.Configuration; +import org.apache.qpid.AMQStoreException; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.binding.Binding; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.federation.Bridge; +import org.apache.qpid.server.federation.BrokerLink; +import org.apache.qpid.server.queue.AMQQueue; + +public class NullMessageStore implements MessageStore +{ + @Override + public void configureConfigStore(String name, + ConfigurationRecoveryHandler recoveryHandler, + Configuration config) throws Exception + { + } + + @Override + public void createExchange(Exchange exchange) throws AMQStoreException + { + } + + @Override + public void removeExchange(Exchange exchange) throws AMQStoreException + { + } + + @Override + public void bindQueue(Binding binding) throws AMQStoreException + { + } + + @Override + public void unbindQueue(Binding binding) throws AMQStoreException + { + } + + @Override + public void createQueue(AMQQueue queue) throws AMQStoreException + { + } + + @Override + public void createQueue(AMQQueue queue, FieldTable arguments) throws AMQStoreException + { + } + + @Override + public void removeQueue(AMQQueue queue) throws AMQStoreException + { + } + + @Override + public void updateQueue(AMQQueue queue) throws AMQStoreException + { + } + + @Override + public void createBrokerLink(final BrokerLink link) throws AMQStoreException + { + } + + @Override + public void deleteBrokerLink(final BrokerLink link) throws AMQStoreException + { + } + + @Override + public void createBridge(final Bridge bridge) throws AMQStoreException + { + } + + @Override + public void deleteBridge(final Bridge bridge) throws AMQStoreException + { + } + + @Override + public void configureMessageStore(String name, + MessageStoreRecoveryHandler recoveryHandler, + TransactionLogRecoveryHandler tlogRecoveryHandler, Configuration config) throws Exception + { + } + + @Override + public void close() throws Exception + { + } + + @Override + public StoredMessage addMessage(T metaData) + { + return null; + } + + @Override + public boolean isPersistent() + { + return false; + } + + @Override + public Transaction newTransaction() + { + return null; + } + + @Override + public void activate() throws Exception + { + } + + @Override + public void addEventListener(EventListener eventListener, Event... events) + { + } + + @Override + public String getStoreLocation() + { + return null; + } + +} \ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/OperationalLoggingListener.java b/java/broker/src/main/java/org/apache/qpid/server/store/OperationalLoggingListener.java new file mode 100644 index 0000000000..caff17daa5 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/OperationalLoggingListener.java @@ -0,0 +1,73 @@ +/* + * 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.store; + +import org.apache.qpid.server.logging.LogSubject; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.messages.ConfigStoreMessages; +import org.apache.qpid.server.logging.messages.MessageStoreMessages; +import org.apache.qpid.server.logging.messages.TransactionLogMessages; + +public class OperationalLoggingListener implements EventListener +{ + protected final LogSubject _logSubject; + private MessageStore _store; + + private OperationalLoggingListener(final MessageStore store, LogSubject logSubject) + { + _logSubject = logSubject; + store.addEventListener(this, Event.BEFORE_INIT, Event.AFTER_INIT, Event.BEFORE_ACTIVATE, Event.AFTER_ACTIVATE, Event.AFTER_CLOSE); + _store = store; + } + + public void event(Event event) + { + switch(event) + { + case BEFORE_INIT: + CurrentActor.get().message(_logSubject, ConfigStoreMessages.CREATED()); + break; + case AFTER_INIT: + CurrentActor.get().message(_logSubject, MessageStoreMessages.CREATED()); + CurrentActor.get().message(_logSubject, TransactionLogMessages.CREATED()); + String storeLocation = _store.getStoreLocation(); + if (storeLocation != null) + { + CurrentActor.get().message(_logSubject, MessageStoreMessages.STORE_LOCATION(storeLocation)); + } + break; + case BEFORE_ACTIVATE: + CurrentActor.get().message(_logSubject, MessageStoreMessages.RECOVERY_START()); + break; + case AFTER_ACTIVATE: + CurrentActor.get().message(_logSubject, MessageStoreMessages.RECOVERY_COMPLETE()); + break; + case AFTER_CLOSE: + CurrentActor.get().message(_logSubject,MessageStoreMessages.CLOSED()); + break; + + } + } + + public static void listen(final MessageStore store, LogSubject logSubject) + { + new OperationalLoggingListener(store, logSubject); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/State.java b/java/broker/src/main/java/org/apache/qpid/server/store/State.java new file mode 100644 index 0000000000..7cbdede85e --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/State.java @@ -0,0 +1,38 @@ +/* + * + * 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.store; + +public enum State +{ + + INITIAL, + CONFIGURING, + CONFIGURED, + RECOVERING, + ACTIVE, + QUIESCING, + QUIESCED, + CLOSING, + CLOSED; + + + +} \ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/StateManager.java b/java/broker/src/main/java/org/apache/qpid/server/store/StateManager.java new file mode 100644 index 0000000000..5998be5bb6 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/StateManager.java @@ -0,0 +1,151 @@ +/* + * + * 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.store; + + +import java.util.EnumMap; +import java.util.Map; + +public class StateManager +{ + private State _state = State.INITIAL; + private EventListener _eventListener; + + private static final Map> _validTransitions = new EnumMap>(State.class); + + + static class Transition + { + private final Event _event; + private final State _endState; + private final State _startState; + + public Transition(State startState, State endState, Event event) + { + _event = event; + _startState = startState; + _endState = endState; + + Map stateTransitions = _validTransitions.get(startState); + if(stateTransitions == null) + { + stateTransitions = new EnumMap(State.class); + _validTransitions.put(startState, stateTransitions); + } + stateTransitions.put(endState, this); + } + + public Event getEvent() + { + return _event; + } + + public State getStartState() + { + return _startState; + } + + public State getEndState() + { + return _endState; + } + + } + + public static final Transition CONFIGURE = new Transition(State.INITIAL, State.CONFIGURING, Event.BEFORE_INIT); + public static final Transition CONFIGURE_COMPLETE = new Transition(State.CONFIGURING, State.CONFIGURED, Event.AFTER_INIT); + public static final Transition RECOVER = new Transition(State.CONFIGURED, State.RECOVERING, Event.BEFORE_ACTIVATE); + public static final Transition ACTIVATE = new Transition(State.RECOVERING, State.ACTIVE, Event.AFTER_ACTIVATE); + public static final Transition CLOSE_ACTIVE = new Transition(State.ACTIVE, State.CLOSING, Event.BEFORE_CLOSE); + public static final Transition CLOSE_QUIESCED = new Transition(State.QUIESCED, State.CLOSING, Event.BEFORE_CLOSE); + public static final Transition CLOSE_COMPLETE = new Transition(State.CLOSING, State.CLOSED, Event.AFTER_CLOSE); + public static final Transition QUIESCE = new Transition(State.ACTIVE, State.QUIESCING, Event.BEFORE_PASSIVATE); + public static final Transition QUIESCE_COMPLETE = new Transition(State.QUIESCING, State.QUIESCED, Event.BEFORE_PASSIVATE); + public static final Transition RESTART = new Transition(State.QUIESCED, State.RECOVERING, Event.BEFORE_ACTIVATE); + + + public StateManager(final EventManager eventManager) + { + this(new EventListener() + { + @Override + public void event(Event event) + { + eventManager.notifyEvent(event); + } + }); + } + + + public StateManager(EventListener eventListener) + { + _eventListener = eventListener; + } + + public synchronized State getState() + { + return _state; + } + + public synchronized void stateTransition(final State current, final State desired) + { + if (_state != current) + { + throw new IllegalStateException("Cannot transition to the state: " + desired + "; need to be in state: " + current + + "; currently in state: " + _state); + } + attainState(desired); + } + + public synchronized void attainState(State desired) + { + Transition transition = null; + final Map stateTransitionMap = _validTransitions.get(_state); + if(stateTransitionMap != null) + { + transition = stateTransitionMap.get(desired); + } + if(transition == null) + { + throw new IllegalStateException("No valid transition from state " + _state + " to state " + desired); + } + _state = desired; + _eventListener.event(transition.getEvent()); + } + + public synchronized boolean isInState(State testedState) + { + return _state.equals(testedState); + } + + public synchronized boolean isNotInState(State testedState) + { + return !isInState(testedState); + } + + public synchronized void checkInState(State checkedState) + { + if (isNotInState(checkedState)) + { + throw new IllegalStateException("Unexpected state. Was : " + _state + " but expected : " + checkedState); + } + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/StoreFuture.java b/java/broker/src/main/java/org/apache/qpid/server/store/StoreFuture.java new file mode 100644 index 0000000000..7d3bf90a75 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/StoreFuture.java @@ -0,0 +1,40 @@ +/* +* +* 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.store; + +public interface StoreFuture +{ + StoreFuture IMMEDIATE_FUTURE = new StoreFuture() + { + public boolean isComplete() + { + return true; + } + + public void waitForCompletion() + { + } + }; + + boolean isComplete(); + + void waitForCompletion(); +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/StoredMemoryMessage.java b/java/broker/src/main/java/org/apache/qpid/server/store/StoredMemoryMessage.java index 144cc629bd..e7302270bb 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/store/StoredMemoryMessage.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/StoredMemoryMessage.java @@ -122,9 +122,9 @@ public class StoredMemoryMessage implements StoredMessage return buf; } - public MessageStore.StoreFuture flushToStore() + public StoreFuture flushToStore() { - return MessageStore.IMMEDIATE_FUTURE; + return StoreFuture.IMMEDIATE_FUTURE; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/StoredMessage.java b/java/broker/src/main/java/org/apache/qpid/server/store/StoredMessage.java index d4a0381929..7909003855 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/store/StoredMessage.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/StoredMessage.java @@ -34,7 +34,7 @@ public interface StoredMessage ByteBuffer getContent(int offsetInMessage, int size); - MessageStore.StoreFuture flushToStore(); + StoreFuture flushToStore(); void remove(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/Transaction.java b/java/broker/src/main/java/org/apache/qpid/server/store/Transaction.java new file mode 100644 index 0000000000..ed6b89e373 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/Transaction.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.store; + +import org.apache.qpid.AMQStoreException; +import org.apache.qpid.server.message.EnqueableMessage; + +public interface Transaction +{ + /** + * Places a message onto a specified queue, in a given transactional context. + * + * + * + * @param queue The queue to place the message on. + * @param message + * @throws org.apache.qpid.AMQStoreException If the operation fails for any reason. + */ + void enqueueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException; + + /** + * Extracts a message from a specified queue, in a given transactional context. + * + * @param queue The queue to place the message on. + * @param message The message to dequeue. + * @throws AMQStoreException If the operation fails for any reason, or if the specified message does not exist. + */ + void dequeueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException; + + + /** + * Commits all operations performed within a given transactional context. + * + * @throws AMQStoreException If the operation fails for any reason. + */ + void commitTran() throws AMQStoreException; + + /** + * Commits all operations performed within a given transactional context. + * + * @throws AMQStoreException If the operation fails for any reason. + */ + StoreFuture commitTranAsync() throws AMQStoreException; + + /** + * Abandons all operations performed within a given transactional context. + * + * @throws AMQStoreException If the operation fails for any reason. + */ + void abortTran() throws AMQStoreException; + + + public static interface Record + { + TransactionLogResource getQueue(); + EnqueableMessage getMessage(); + } + + void removeXid(long format, byte[] globalId, byte[] branchId) throws AMQStoreException; + + void recordXid(long format, byte[] globalId, byte[] branchId, Transaction.Record[] enqueues, Transaction.Record[] dequeues) + throws AMQStoreException; +} \ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/TransactionLogRecoveryHandler.java b/java/broker/src/main/java/org/apache/qpid/server/store/TransactionLogRecoveryHandler.java index 802596ed1e..bd4da648f9 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/store/TransactionLogRecoveryHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/TransactionLogRecoveryHandler.java @@ -20,14 +20,23 @@ */ package org.apache.qpid.server.store; +import java.util.UUID; + public interface TransactionLogRecoveryHandler { QueueEntryRecoveryHandler begin(MessageStore log); public static interface QueueEntryRecoveryHandler { - void queueEntry(String queuename, long messageId); + DtxRecordRecoveryHandler completeQueueEntryRecovery(); + + void queueEntry(UUID queueId, long messageId); + } + + public static interface DtxRecordRecoveryHandler + { + void dtxRecord(long format, byte[] globalId, byte[] branchId, Transaction.Record[] enqueues, Transaction.Record[] dequeues); - void completeQueueEntryRecovery(); + void completeDtxRecordRecovery(); } } diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/TransactionLogResource.java b/java/broker/src/main/java/org/apache/qpid/server/store/TransactionLogResource.java index 0d81dd151d..576dca847d 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/store/TransactionLogResource.java +++ b/java/broker/src/main/java/org/apache/qpid/server/store/TransactionLogResource.java @@ -20,7 +20,9 @@ */ package org.apache.qpid.server.store; +import java.util.UUID; + public interface TransactionLogResource { - public String getResourceName(); + public UUID getId(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStore.java new file mode 100644 index 0000000000..0371cdcfcb --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStore.java @@ -0,0 +1,2449 @@ +/* +* +* 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.store.derby; + + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.lang.ref.SoftReference; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.sql.Blob; +import java.sql.Connection; +import java.sql.Driver; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import org.apache.commons.configuration.Configuration; +import org.apache.log4j.Logger; +import org.apache.qpid.AMQException; +import org.apache.qpid.AMQStoreException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.binding.Binding; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.federation.Bridge; +import org.apache.qpid.server.federation.BrokerLink; +import org.apache.qpid.server.message.EnqueableMessage; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.store.ConfigurationRecoveryHandler; +import org.apache.qpid.server.store.ConfiguredObjectHelper; +import org.apache.qpid.server.store.ConfiguredObjectRecord; +import org.apache.qpid.server.store.Event; +import org.apache.qpid.server.store.EventListener; +import org.apache.qpid.server.store.EventManager; +import org.apache.qpid.server.store.MessageMetaDataType; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.MessageStoreConstants; +import org.apache.qpid.server.store.MessageStoreRecoveryHandler; +import org.apache.qpid.server.store.State; +import org.apache.qpid.server.store.StateManager; +import org.apache.qpid.server.store.StorableMessageMetaData; +import org.apache.qpid.server.store.StoreFuture; +import org.apache.qpid.server.store.StoredMemoryMessage; +import org.apache.qpid.server.store.StoredMessage; +import org.apache.qpid.server.store.Transaction; +import org.apache.qpid.server.store.TransactionLogRecoveryHandler; +import org.apache.qpid.server.store.TransactionLogResource; + +/** + * An implementation of a {@link MessageStore} that uses Apache Derby as the persistence + * mechanism. + * + * TODO extract the SQL statements into a generic JDBC store + */ +public class DerbyMessageStore implements MessageStore +{ + + private static final Logger _logger = Logger.getLogger(DerbyMessageStore.class); + + private static final String SQL_DRIVER_NAME = "org.apache.derby.jdbc.EmbeddedDriver"; + + private static final String DB_VERSION_TABLE_NAME = "QPID_DB_VERSION"; + + private static final String QUEUE_ENTRY_TABLE_NAME = "QPID_QUEUE_ENTRIES"; + + private static final String META_DATA_TABLE_NAME = "QPID_MESSAGE_METADATA"; + private static final String MESSAGE_CONTENT_TABLE_NAME = "QPID_MESSAGE_CONTENT"; + + private static final String LINKS_TABLE_NAME = "QPID_LINKS"; + private static final String BRIDGES_TABLE_NAME = "QPID_BRIDGES"; + + private static final String XID_TABLE_NAME = "QPID_XIDS"; + private static final String XID_ACTIONS_TABLE_NAME = "QPID_XID_ACTIONS"; + + private static final String CONFIGURED_OBJECTS_TABLE_NAME = "QPID_CONFIGURED_OBJECTS"; + + private static final int DB_VERSION = 6; + + + + private static Class DRIVER_CLASS; + + private final AtomicLong _messageId = new AtomicLong(0); + private AtomicBoolean _closed = new AtomicBoolean(false); + + private String _connectionURL; + + private static final String TABLE_EXISTANCE_QUERY = "SELECT 1 FROM SYS.SYSTABLES WHERE TABLENAME = ?"; + + private static final String CREATE_DB_VERSION_TABLE = "CREATE TABLE "+DB_VERSION_TABLE_NAME+" ( version int not null )"; + private static final String INSERT_INTO_DB_VERSION = "INSERT INTO "+DB_VERSION_TABLE_NAME+" ( version ) VALUES ( ? )"; + + private static final String CREATE_QUEUE_ENTRY_TABLE = "CREATE TABLE "+QUEUE_ENTRY_TABLE_NAME+" ( queue_id varchar(36) not null, message_id bigint not null, PRIMARY KEY (queue_id, message_id) )"; + private static final String INSERT_INTO_QUEUE_ENTRY = "INSERT INTO " + QUEUE_ENTRY_TABLE_NAME + " (queue_id, message_id) values (?,?)"; + private static final String DELETE_FROM_QUEUE_ENTRY = "DELETE FROM " + QUEUE_ENTRY_TABLE_NAME + " WHERE queue_id = ? AND message_id =?"; + private static final String SELECT_FROM_QUEUE_ENTRY = "SELECT queue_id, message_id FROM " + QUEUE_ENTRY_TABLE_NAME + " ORDER BY queue_id, message_id"; + + + private static final String CREATE_META_DATA_TABLE = "CREATE TABLE " + META_DATA_TABLE_NAME + + " ( message_id bigint not null, meta_data blob, PRIMARY KEY ( message_id ) )"; + private static final String CREATE_MESSAGE_CONTENT_TABLE = "CREATE TABLE " + MESSAGE_CONTENT_TABLE_NAME + + " ( message_id bigint not null, content blob , PRIMARY KEY (message_id) )"; + + private static final String INSERT_INTO_MESSAGE_CONTENT = "INSERT INTO " + MESSAGE_CONTENT_TABLE_NAME + + "( message_id, content ) values (?, ?)"; + private static final String SELECT_FROM_MESSAGE_CONTENT = "SELECT content FROM " + MESSAGE_CONTENT_TABLE_NAME + + " WHERE message_id = ?"; + private static final String DELETE_FROM_MESSAGE_CONTENT = "DELETE FROM " + MESSAGE_CONTENT_TABLE_NAME + + " WHERE message_id = ?"; + + private static final String INSERT_INTO_META_DATA = "INSERT INTO " + META_DATA_TABLE_NAME + "( message_id , meta_data ) values (?, ?)";; + private static final String SELECT_FROM_META_DATA = + "SELECT meta_data FROM " + META_DATA_TABLE_NAME + " WHERE message_id = ?"; + private static final String DELETE_FROM_META_DATA = "DELETE FROM " + META_DATA_TABLE_NAME + " WHERE message_id = ?"; + private static final String SELECT_ALL_FROM_META_DATA = "SELECT message_id, meta_data FROM " + META_DATA_TABLE_NAME; + + private static final String CREATE_LINKS_TABLE = + "CREATE TABLE "+LINKS_TABLE_NAME+" ( id_lsb bigint not null," + + " id_msb bigint not null," + + " create_time bigint not null," + + " arguments blob, PRIMARY KEY ( id_lsb, id_msb ))"; + private static final String SELECT_FROM_LINKS = + "SELECT create_time, arguments FROM " + LINKS_TABLE_NAME + " WHERE id_lsb = ? and id_msb"; + private static final String DELETE_FROM_LINKS = "DELETE FROM " + LINKS_TABLE_NAME + + " WHERE id_lsb = ? and id_msb = ?"; + private static final String SELECT_ALL_FROM_LINKS = "SELECT id_lsb, id_msb, create_time, " + + "arguments FROM " + LINKS_TABLE_NAME; + private static final String FIND_LINK = "SELECT id_lsb, id_msb FROM " + LINKS_TABLE_NAME + " WHERE id_lsb = ? and" + + " id_msb = ?"; + private static final String INSERT_INTO_LINKS = "INSERT INTO " + LINKS_TABLE_NAME + "( id_lsb, " + + "id_msb, create_time, arguments ) values (?, ?, ?, ?)"; + + + private static final String CREATE_BRIDGES_TABLE = + "CREATE TABLE "+BRIDGES_TABLE_NAME+" ( id_lsb bigint not null," + + " id_msb bigint not null," + + " create_time bigint not null," + + " link_id_lsb bigint not null," + + " link_id_msb bigint not null," + + " arguments blob, PRIMARY KEY ( id_lsb, id_msb ))"; + private static final String SELECT_FROM_BRIDGES = + "SELECT create_time, link_id_lsb, link_id_msb, arguments FROM " + + BRIDGES_TABLE_NAME + " WHERE id_lsb = ? and id_msb = ?"; + private static final String DELETE_FROM_BRIDGES = "DELETE FROM " + BRIDGES_TABLE_NAME + + " WHERE id_lsb = ? and id_msb = ?"; + private static final String SELECT_ALL_FROM_BRIDGES = "SELECT id_lsb, id_msb, " + + " create_time," + + " link_id_lsb, link_id_msb, " + + "arguments FROM " + BRIDGES_TABLE_NAME + + " WHERE link_id_lsb = ? and link_id_msb = ?"; + private static final String FIND_BRIDGE = "SELECT id_lsb, id_msb FROM " + BRIDGES_TABLE_NAME + + " WHERE id_lsb = ? and id_msb = ?"; + private static final String INSERT_INTO_BRIDGES = "INSERT INTO " + BRIDGES_TABLE_NAME + "( id_lsb, id_msb, " + + "create_time, " + + "link_id_lsb, link_id_msb, " + + "arguments )" + + " values (?, ?, ?, ?, ?, ?)"; + + private static final String CREATE_XIDS_TABLE = + "CREATE TABLE "+XID_TABLE_NAME+" ( format bigint not null," + + " global_id varchar(64) for bit data, branch_id varchar(64) for bit data, PRIMARY KEY ( format, " + + "global_id, branch_id ))"; + private static final String INSERT_INTO_XIDS = + "INSERT INTO "+XID_TABLE_NAME+" ( format, global_id, branch_id ) values (?, ?, ?)"; + private static final String DELETE_FROM_XIDS = "DELETE FROM " + XID_TABLE_NAME + + " WHERE format = ? and global_id = ? and branch_id = ?"; + private static final String SELECT_ALL_FROM_XIDS = "SELECT format, global_id, branch_id FROM " + XID_TABLE_NAME; + + + private static final String CREATE_XID_ACTIONS_TABLE = + "CREATE TABLE "+XID_ACTIONS_TABLE_NAME+" ( format bigint not null," + + " global_id varchar(64) for bit data not null, branch_id varchar(64) for bit data not null, " + + "action_type char not null, queue_id varchar(36) not null, message_id bigint not null" + + ", PRIMARY KEY ( " + + "format, global_id, branch_id, action_type, queue_id, message_id))"; + private static final String INSERT_INTO_XID_ACTIONS = + "INSERT INTO "+XID_ACTIONS_TABLE_NAME+" ( format, global_id, branch_id, action_type, " + + "queue_id, message_id ) values (?,?,?,?,?,?) "; + private static final String DELETE_FROM_XID_ACTIONS = "DELETE FROM " + XID_ACTIONS_TABLE_NAME + + " WHERE format = ? and global_id = ? and branch_id = ?"; + private static final String SELECT_ALL_FROM_XID_ACTIONS = + "SELECT action_type, queue_id, message_id FROM " + XID_ACTIONS_TABLE_NAME + + " WHERE format = ? and global_id = ? and branch_id = ?"; + + private static final String CREATE_CONFIGURED_OBJECTS_TABLE = "CREATE TABLE " + CONFIGURED_OBJECTS_TABLE_NAME + + " ( id VARCHAR(36) not null, object_type varchar(255), attributes blob, PRIMARY KEY (id))"; + private static final String INSERT_INTO_CONFIGURED_OBJECTS = "INSERT INTO " + CONFIGURED_OBJECTS_TABLE_NAME + + " ( id, object_type, attributes) VALUES (?,?,?)"; + private static final String UPDATE_CONFIGURED_OBJECTS = "UPDATE " + CONFIGURED_OBJECTS_TABLE_NAME + + " set object_type =?, attributes = ? where id = ?"; + private static final String DELETE_FROM_CONFIGURED_OBJECTS = "DELETE FROM " + CONFIGURED_OBJECTS_TABLE_NAME + + " where id = ?"; + private static final String FIND_CONFIGURED_OBJECT = "SELECT object_type, attributes FROM " + CONFIGURED_OBJECTS_TABLE_NAME + + " where id = ?"; + private static final String SELECT_FROM_CONFIGURED_OBJECTS = "SELECT id, object_type, attributes FROM " + CONFIGURED_OBJECTS_TABLE_NAME; + + private final Charset UTF8_CHARSET = Charset.forName("UTF-8"); + + private static final String DERBY_SINGLE_DB_SHUTDOWN_CODE = "08006"; + + private final StateManager _stateManager; + + private final EventManager _eventManager = new EventManager(); + + private MessageStoreRecoveryHandler _messageRecoveryHandler; + + private TransactionLogRecoveryHandler _tlogRecoveryHandler; + + private ConfigurationRecoveryHandler _configRecoveryHandler; + private String _storeLocation; + + public DerbyMessageStore() + { + _stateManager = new StateManager(_eventManager); + } + + private ConfiguredObjectHelper _configuredObjectHelper = new ConfiguredObjectHelper(); + + @Override + public void configureConfigStore(String name, + ConfigurationRecoveryHandler configRecoveryHandler, + Configuration storeConfiguration) throws Exception + { + _stateManager.attainState(State.CONFIGURING); + _configRecoveryHandler = configRecoveryHandler; + + commonConfiguration(name, storeConfiguration); + + } + + @Override + public void configureMessageStore(String name, + MessageStoreRecoveryHandler recoveryHandler, + TransactionLogRecoveryHandler tlogRecoveryHandler, + Configuration storeConfiguration) throws Exception + { + _tlogRecoveryHandler = tlogRecoveryHandler; + _messageRecoveryHandler = recoveryHandler; + + _stateManager.attainState(State.CONFIGURED); + } + + @Override + public void activate() throws Exception + { + _stateManager.attainState(State.RECOVERING); + + // this recovers durable exchanges, queues, and bindings + recoverConfiguration(_configRecoveryHandler); + recoverMessages(_messageRecoveryHandler); + TransactionLogRecoveryHandler.DtxRecordRecoveryHandler dtxrh = recoverQueueEntries(_tlogRecoveryHandler); + recoverXids(dtxrh); + + _stateManager.attainState(State.ACTIVE); + } + + private void commonConfiguration(String name, Configuration storeConfiguration) + throws ClassNotFoundException, SQLException + { + initialiseDriver(); + + //Update to pick up QPID_WORK and use that as the default location not just derbyDB + + final String databasePath = storeConfiguration.getString(MessageStoreConstants.ENVIRONMENT_PATH_PROPERTY, System.getProperty("QPID_WORK") + + File.separator + "derbyDB"); + + File environmentPath = new File(databasePath); + if (!environmentPath.exists()) + { + if (!environmentPath.mkdirs()) + { + throw new IllegalArgumentException("Environment path " + environmentPath + " could not be read or created. " + + "Ensure the path is correct and that the permissions are correct."); + } + } + + _storeLocation = databasePath; + + createOrOpenDatabase(name, databasePath); + } + + private static synchronized void initialiseDriver() throws ClassNotFoundException + { + if(DRIVER_CLASS == null) + { + DRIVER_CLASS = (Class) Class.forName(SQL_DRIVER_NAME); + } + } + + private void createOrOpenDatabase(String name, final String environmentPath) throws SQLException + { + //FIXME this the _vhost name should not be added here, but derby wont use an empty directory as was possibly just created. + _connectionURL = "jdbc:derby:" + environmentPath + "/" + name + ";create=true"; + + Connection conn = newAutoCommitConnection(); + + createVersionTable(conn); + createConfiguredObjectsTable(conn); + createQueueEntryTable(conn); + createMetaDataTable(conn); + createMessageContentTable(conn); + createLinkTable(conn); + createBridgeTable(conn); + createXidTable(conn); + createXidActionTable(conn); + conn.close(); + } + + + + private void createVersionTable(final Connection conn) throws SQLException + { + if(!tableExists(DB_VERSION_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute(CREATE_DB_VERSION_TABLE); + } + finally + { + stmt.close(); + } + + PreparedStatement pstmt = conn.prepareStatement(INSERT_INTO_DB_VERSION); + try + { + pstmt.setInt(1, DB_VERSION); + pstmt.execute(); + } + finally + { + pstmt.close(); + } + } + + } + + private void createConfiguredObjectsTable(final Connection conn) throws SQLException + { + if(!tableExists(CONFIGURED_OBJECTS_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute(CREATE_CONFIGURED_OBJECTS_TABLE); + } + finally + { + stmt.close(); + } + } + } + + private void createQueueEntryTable(final Connection conn) throws SQLException + { + if(!tableExists(QUEUE_ENTRY_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute(CREATE_QUEUE_ENTRY_TABLE); + } + finally + { + stmt.close(); + } + } + + } + + private void createMetaDataTable(final Connection conn) throws SQLException + { + if(!tableExists(META_DATA_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute(CREATE_META_DATA_TABLE); + } + finally + { + stmt.close(); + } + } + + } + + + private void createMessageContentTable(final Connection conn) throws SQLException + { + if(!tableExists(MESSAGE_CONTENT_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute(CREATE_MESSAGE_CONTENT_TABLE); + } + finally + { + stmt.close(); + } + } + + } + + private void createLinkTable(final Connection conn) throws SQLException + { + if(!tableExists(LINKS_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute(CREATE_LINKS_TABLE); + } + finally + { + stmt.close(); + } + } + } + + + private void createBridgeTable(final Connection conn) throws SQLException + { + if(!tableExists(BRIDGES_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute(CREATE_BRIDGES_TABLE); + } + finally + { + stmt.close(); + } + } + } + + private void createXidTable(final Connection conn) throws SQLException + { + if(!tableExists(XID_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute(CREATE_XIDS_TABLE); + } + finally + { + stmt.close(); + } + } + } + + + private void createXidActionTable(final Connection conn) throws SQLException + { + if(!tableExists(XID_ACTIONS_TABLE_NAME, conn)) + { + Statement stmt = conn.createStatement(); + try + { + stmt.execute(CREATE_XID_ACTIONS_TABLE); + } + finally + { + stmt.close(); + } + } + } + + private boolean tableExists(final String tableName, final Connection conn) throws SQLException + { + PreparedStatement stmt = conn.prepareStatement(TABLE_EXISTANCE_QUERY); + try + { + stmt.setString(1, tableName); + ResultSet rs = stmt.executeQuery(); + try + { + return rs.next(); + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + } + + private void recoverConfiguration(ConfigurationRecoveryHandler recoveryHandler) throws AMQException + { + try + { + List configuredObjects = loadConfiguredObjects(); + ConfigurationRecoveryHandler.QueueRecoveryHandler qrh = recoveryHandler.begin(this); + _configuredObjectHelper.recoverQueues(qrh, configuredObjects); + + ConfigurationRecoveryHandler.ExchangeRecoveryHandler erh = qrh.completeQueueRecovery(); + _configuredObjectHelper.recoverExchanges(erh, configuredObjects); + + ConfigurationRecoveryHandler.BindingRecoveryHandler brh = erh.completeExchangeRecovery(); + _configuredObjectHelper.recoverBindings(brh, configuredObjects); + + ConfigurationRecoveryHandler.BrokerLinkRecoveryHandler lrh = brh.completeBindingRecovery(); + recoverBrokerLinks(lrh); + } + catch (SQLException e) + { + throw new AMQStoreException("Error recovering persistent state: " + e.getMessage(), e); + } + } + + private void recoverBrokerLinks(final ConfigurationRecoveryHandler.BrokerLinkRecoveryHandler lrh) + throws SQLException + { + _logger.info("Recovering broker links..."); + + Connection conn = null; + try + { + conn = newAutoCommitConnection(); + + PreparedStatement stmt = conn.prepareStatement(SELECT_ALL_FROM_LINKS); + + try + { + ResultSet rs = stmt.executeQuery(); + + try + { + + while(rs.next()) + { + UUID id = new UUID(rs.getLong(2), rs.getLong(1)); + long createTime = rs.getLong(3); + Blob argumentsAsBlob = rs.getBlob(4); + + byte[] dataAsBytes = argumentsAsBlob.getBytes(1,(int) argumentsAsBlob.length()); + + DataInputStream dis = new DataInputStream(new ByteArrayInputStream(dataAsBytes)); + int size = dis.readInt(); + + Map arguments = new HashMap(); + + for(int i = 0; i < size; i++) + { + arguments.put(dis.readUTF(), dis.readUTF()); + } + + ConfigurationRecoveryHandler.BridgeRecoveryHandler brh = lrh.brokerLink(id, createTime, arguments); + + recoverBridges(brh, id); + + } + } + catch (IOException e) + { + throw new SQLException(e.getMessage(), e); + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + + } + finally + { + if(conn != null) + { + conn.close(); + } + } + + } + + private void recoverBridges(final ConfigurationRecoveryHandler.BridgeRecoveryHandler brh, final UUID linkId) + throws SQLException + { + _logger.info("Recovering bridges for link " + linkId + "..."); + + Connection conn = null; + try + { + conn = newAutoCommitConnection(); + + PreparedStatement stmt = conn.prepareStatement(SELECT_ALL_FROM_BRIDGES); + + try + { + stmt.setLong(1, linkId.getLeastSignificantBits()); + stmt.setLong(2, linkId.getMostSignificantBits()); + + ResultSet rs = stmt.executeQuery(); + + try + { + + while(rs.next()) + { + UUID id = new UUID(rs.getLong(2), rs.getLong(1)); + long createTime = rs.getLong(3); + Blob argumentsAsBlob = rs.getBlob(6); + + byte[] dataAsBytes = argumentsAsBlob.getBytes(1,(int) argumentsAsBlob.length()); + + DataInputStream dis = new DataInputStream(new ByteArrayInputStream(dataAsBytes)); + int size = dis.readInt(); + + Map arguments = new HashMap(); + + for(int i = 0; i < size; i++) + { + arguments.put(dis.readUTF(), dis.readUTF()); + } + + brh.bridge(id, createTime, arguments); + + } + brh.completeBridgeRecoveryForLink(); + } + catch (IOException e) + { + throw new SQLException(e.getMessage(), e); + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + + } + finally + { + if(conn != null) + { + conn.close(); + } + } + + } + + @Override + public void close() throws Exception + { + _closed.getAndSet(true); + _stateManager.stateTransition(State.ACTIVE, State.CLOSING); + + try + { + Connection conn = DriverManager.getConnection(_connectionURL + ";shutdown=true"); + // Shouldn't reach this point - shutdown=true should throw SQLException + conn.close(); + _logger.error("Unable to shut down the store"); + } + catch (SQLException e) + { + if (e.getSQLState().equalsIgnoreCase(DERBY_SINGLE_DB_SHUTDOWN_CODE)) + { + //expected and represents a clean shutdown of this database only, do nothing. + } + else + { + _logger.error("Exception whilst shutting down the store: " + e); + } + } + + _stateManager.stateTransition(State.CLOSING, State.CLOSED); + } + + @Override + public StoredMessage addMessage(StorableMessageMetaData metaData) + { + if(metaData.isPersistent()) + { + return new StoredDerbyMessage(_messageId.incrementAndGet(), metaData); + } + else + { + return new StoredMemoryMessage(_messageId.incrementAndGet(), metaData); + } + } + + public StoredMessage getMessage(long messageNumber) + { + return null; + } + + public void removeMessage(long messageId) + { + try + { + Connection conn = newConnection(); + try + { + PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_META_DATA); + try + { + stmt.setLong(1,messageId); + int results = stmt.executeUpdate(); + stmt.close(); + + if (results == 0) + { + _logger.warn("Message metadata not found for message id " + messageId); + } + + if (_logger.isDebugEnabled()) + { + _logger.debug("Deleted metadata for message " + messageId); + } + + stmt = conn.prepareStatement(DELETE_FROM_MESSAGE_CONTENT); + stmt.setLong(1,messageId); + results = stmt.executeUpdate(); + } + finally + { + stmt.close(); + } + conn.commit(); + } + catch(SQLException e) + { + try + { + conn.rollback(); + } + catch(SQLException t) + { + // ignore - we are re-throwing underlying exception + } + + throw e; + + } + finally + { + conn.close(); + } + } + catch (SQLException e) + { + throw new RuntimeException("Error removing message with id " + messageId + " from database: " + e.getMessage(), e); + } + + } + + @Override + public void createExchange(Exchange exchange) throws AMQStoreException + { + if (_stateManager.isInState(State.ACTIVE)) + { + ConfiguredObjectRecord configuredObject = _configuredObjectHelper.createExchangeConfiguredObject(exchange); + insertConfiguredObject(configuredObject); + } + + } + + @Override + public void removeExchange(Exchange exchange) throws AMQStoreException + { + int results = removeConfiguredObject(exchange.getId()); + if (results == 0) + { + throw new AMQStoreException("Exchange " + exchange.getName() + " with id " + exchange.getId() + " not found"); + } + } + + @Override + public void bindQueue(Binding binding) + throws AMQStoreException + { + if (_stateManager.isInState(State.ACTIVE)) + { + ConfiguredObjectRecord configuredObject = _configuredObjectHelper.createBindingConfiguredObject(binding); + insertConfiguredObject(configuredObject); + } + } + + @Override + public void unbindQueue(Binding binding) + throws AMQStoreException + { + int results = removeConfiguredObject(binding.getId()); + if (results == 0) + { + throw new AMQStoreException("Binding " + binding + " not found"); + } + } + + @Override + public void createQueue(AMQQueue queue) throws AMQStoreException + { + createQueue(queue, null); + } + + @Override + public void createQueue(AMQQueue queue, FieldTable arguments) throws AMQStoreException + { + _logger.debug("public void createQueue(AMQQueue queue = " + queue + "): called"); + + if (_stateManager.isInState(State.ACTIVE)) + { + ConfiguredObjectRecord queueConfiguredObject = _configuredObjectHelper.createQueueConfiguredObject(queue, arguments); + insertConfiguredObject(queueConfiguredObject); + } + } + + /** + * Updates the specified queue in the persistent store, IF it is already present. If the queue + * is not present in the store, it will not be added. + * + * NOTE: Currently only updates the exclusivity. + * + * @param queue The queue to update the entry for. + * @throws AMQStoreException If the operation fails for any reason. + */ + @Override + public void updateQueue(final AMQQueue queue) throws AMQStoreException + { + if (_stateManager.isInState(State.ACTIVE)) + { + ConfiguredObjectRecord queueConfiguredObject = loadConfiguredObject(queue.getId()); + if (queueConfiguredObject != null) + { + ConfiguredObjectRecord newQueueRecord = _configuredObjectHelper.updateQueueConfiguredObject(queue, queueConfiguredObject); + updateConfiguredObject(newQueueRecord); + } + } + + } + + /** + * Convenience method to create a new Connection configured for TRANSACTION_READ_COMMITED + * isolation and with auto-commit transactions enabled. + */ + private Connection newAutoCommitConnection() throws SQLException + { + final Connection connection = newConnection(); + try + { + connection.setAutoCommit(true); + } + catch (SQLException sqlEx) + { + + try + { + connection.close(); + } + finally + { + throw sqlEx; + } + } + + return connection; + } + + /** + * Convenience method to create a new Connection configured for TRANSACTION_READ_COMMITED + * isolation and with auto-commit transactions disabled. + */ + private Connection newConnection() throws SQLException + { + final Connection connection = DriverManager.getConnection(_connectionURL); + try + { + connection.setAutoCommit(false); + connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); + } + catch (SQLException sqlEx) + { + try + { + connection.close(); + } + finally + { + throw sqlEx; + } + } + return connection; + } + + @Override + public void removeQueue(final AMQQueue queue) throws AMQStoreException + { + AMQShortString name = queue.getNameShortString(); + _logger.debug("public void removeQueue(AMQShortString name = " + name + "): called"); + int results = removeConfiguredObject(queue.getId()); + if (results == 0) + { + throw new AMQStoreException("Queue " + name + " with id " + queue.getId() + " not found"); + } + } + + @Override + public void createBrokerLink(final BrokerLink link) throws AMQStoreException + { + _logger.debug("public void createBrokerLink(BrokerLink = " + link + "): called"); + + if (_stateManager.isInState(State.ACTIVE)) + { + try + { + Connection conn = newAutoCommitConnection(); + + PreparedStatement stmt = conn.prepareStatement(FIND_LINK); + try + { + + stmt.setLong(1, link.getId().getLeastSignificantBits()); + stmt.setLong(2, link.getId().getMostSignificantBits()); + ResultSet rs = stmt.executeQuery(); + try + { + + // If we don't have any data in the result set then we can add this queue + if (!rs.next()) + { + PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_LINKS); + + try + { + + insertStmt.setLong(1, link.getId().getLeastSignificantBits()); + insertStmt.setLong(2, link.getId().getMostSignificantBits()); + insertStmt.setLong(3, link.getCreateTime()); + + byte[] argumentBytes = convertStringMapToBytes(link.getArguments()); + ByteArrayInputStream bis = new ByteArrayInputStream(argumentBytes); + + insertStmt.setBinaryStream(4,bis,argumentBytes.length); + + insertStmt.execute(); + } + finally + { + insertStmt.close(); + } + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + conn.close(); + + } + catch (SQLException e) + { + throw new AMQStoreException("Error writing " + link + " to database: " + e.getMessage(), e); + } + } + } + + private byte[] convertStringMapToBytes(final Map arguments) throws AMQStoreException + { + byte[] argumentBytes; + if(arguments == null) + { + argumentBytes = new byte[0]; + } + else + { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(bos); + + + try + { + dos.writeInt(arguments.size()); + for(Map.Entry arg : arguments.entrySet()) + { + dos.writeUTF(arg.getKey()); + dos.writeUTF(arg.getValue()); + } + } + catch (IOException e) + { + // This should never happen + throw new AMQStoreException(e.getMessage(), e); + } + argumentBytes = bos.toByteArray(); + } + return argumentBytes; + } + + @Override + public void deleteBrokerLink(final BrokerLink link) throws AMQStoreException + { + _logger.debug("public void deleteBrokerLink( " + link + "): called"); + Connection conn = null; + PreparedStatement stmt = null; + try + { + conn = newAutoCommitConnection(); + stmt = conn.prepareStatement(DELETE_FROM_LINKS); + stmt.setLong(1, link.getId().getLeastSignificantBits()); + stmt.setLong(2, link.getId().getMostSignificantBits()); + int results = stmt.executeUpdate(); + + if (results == 0) + { + throw new AMQStoreException("Link " + link + " not found"); + } + } + catch (SQLException e) + { + throw new AMQStoreException("Error deleting Link " + link + " from database: " + e.getMessage(), e); + } + finally + { + closePreparedStatement(stmt); + closeConnection(conn); + } + + + } + + @Override + public void createBridge(final Bridge bridge) throws AMQStoreException + { + _logger.debug("public void createBridge(BrokerLink = " + bridge + "): called"); + + if (_stateManager.isInState(State.ACTIVE)) + { + try + { + Connection conn = newAutoCommitConnection(); + + PreparedStatement stmt = conn.prepareStatement(FIND_BRIDGE); + try + { + + UUID id = bridge.getId(); + stmt.setLong(1, id.getLeastSignificantBits()); + stmt.setLong(2, id.getMostSignificantBits()); + ResultSet rs = stmt.executeQuery(); + try + { + + // If we don't have any data in the result set then we can add this queue + if (!rs.next()) + { + PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_BRIDGES); + + try + { + + insertStmt.setLong(1, id.getLeastSignificantBits()); + insertStmt.setLong(2, id.getMostSignificantBits()); + + insertStmt.setLong(3, bridge.getCreateTime()); + + UUID linkId = bridge.getLink().getId(); + insertStmt.setLong(4, linkId.getLeastSignificantBits()); + insertStmt.setLong(5, linkId.getMostSignificantBits()); + + byte[] argumentBytes = convertStringMapToBytes(bridge.getArguments()); + ByteArrayInputStream bis = new ByteArrayInputStream(argumentBytes); + + insertStmt.setBinaryStream(6,bis,argumentBytes.length); + + insertStmt.execute(); + } + finally + { + insertStmt.close(); + } + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + conn.close(); + + } + catch (SQLException e) + { + throw new AMQStoreException("Error writing " + bridge + " to database: " + e.getMessage(), e); + } + } + } + + @Override + public void deleteBridge(final Bridge bridge) throws AMQStoreException + { + _logger.debug("public void deleteBridge( " + bridge + "): called"); + Connection conn = null; + PreparedStatement stmt = null; + try + { + conn = newAutoCommitConnection(); + stmt = conn.prepareStatement(DELETE_FROM_BRIDGES); + stmt.setLong(1, bridge.getId().getLeastSignificantBits()); + stmt.setLong(2, bridge.getId().getMostSignificantBits()); + int results = stmt.executeUpdate(); + + if (results == 0) + { + throw new AMQStoreException("Bridge " + bridge + " not found"); + } + } + catch (SQLException e) + { + throw new AMQStoreException("Error deleting bridge " + bridge + " from database: " + e.getMessage(), e); + } + finally + { + closePreparedStatement(stmt); + closeConnection(conn); + } + + } + + @Override + public Transaction newTransaction() + { + return new DerbyTransaction(); + } + + public void enqueueMessage(ConnectionWrapper connWrapper, final TransactionLogResource queue, Long messageId) throws AMQStoreException + { + Connection conn = connWrapper.getConnection(); + + + try + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Enqueuing message " + messageId + " on queue " + (queue instanceof AMQQueue ? ((AMQQueue)queue).getName() : "" ) + queue.getId()+ "[Connection" + conn + "]"); + } + + PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_QUEUE_ENTRY); + try + { + stmt.setString(1, queue.getId().toString()); + stmt.setLong(2,messageId); + stmt.executeUpdate(); + } + finally + { + stmt.close(); + } + } + catch (SQLException e) + { + _logger.error("Failed to enqueue: " + e.getMessage(), e); + throw new AMQStoreException("Error writing enqueued message with id " + messageId + " for queue " + (queue instanceof AMQQueue ? ((AMQQueue)queue).getName() : "" ) + " with id " + queue.getId() + + " to database", e); + } + + } + + public void dequeueMessage(ConnectionWrapper connWrapper, final TransactionLogResource queue, Long messageId) throws AMQStoreException + { + + Connection conn = connWrapper.getConnection(); + + + try + { + PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_QUEUE_ENTRY); + try + { + stmt.setString(1, queue.getId().toString()); + stmt.setLong(2,messageId); + int results = stmt.executeUpdate(); + + + + if(results != 1) + { + throw new AMQStoreException("Unable to find message with id " + messageId + " on queue " + (queue instanceof AMQQueue ? ((AMQQueue)queue).getName() : "" ) + + " with id " + queue.getId()); + } + + if (_logger.isDebugEnabled()) + { + _logger.debug("Dequeuing message " + messageId + " on queue " + (queue instanceof AMQQueue ? ((AMQQueue)queue).getName() : "" ) + + " with id " + queue.getId()); + } + } + finally + { + stmt.close(); + } + } + catch (SQLException e) + { + _logger.error("Failed to dequeue: " + e.getMessage(), e); + throw new AMQStoreException("Error deleting enqueued message with id " + messageId + " for queue " + (queue instanceof AMQQueue ? ((AMQQueue)queue).getName() : "" ) + + " with id " + queue.getId() + " from database", e); + } + + } + + + private void removeXid(ConnectionWrapper connWrapper, long format, byte[] globalId, byte[] branchId) + throws AMQStoreException + { + Connection conn = connWrapper.getConnection(); + + + try + { + PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_XIDS); + try + { + stmt.setLong(1,format); + stmt.setBytes(2,globalId); + stmt.setBytes(3,branchId); + int results = stmt.executeUpdate(); + + + + if(results != 1) + { + throw new AMQStoreException("Unable to find message with xid"); + } + } + finally + { + stmt.close(); + } + + stmt = conn.prepareStatement(DELETE_FROM_XID_ACTIONS); + try + { + stmt.setLong(1,format); + stmt.setBytes(2,globalId); + stmt.setBytes(3,branchId); + int results = stmt.executeUpdate(); + + } + finally + { + stmt.close(); + } + + } + catch (SQLException e) + { + _logger.error("Failed to dequeue: " + e.getMessage(), e); + throw new AMQStoreException("Error deleting enqueued message with xid", e); + } + + } + + + private void recordXid(ConnectionWrapper connWrapper, long format, byte[] globalId, byte[] branchId, + Transaction.Record[] enqueues, Transaction.Record[] dequeues) throws AMQStoreException + { + Connection conn = connWrapper.getConnection(); + + + try + { + + PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_XIDS); + try + { + stmt.setLong(1,format); + stmt.setBytes(2, globalId); + stmt.setBytes(3, branchId); + stmt.executeUpdate(); + } + finally + { + stmt.close(); + } + + stmt = conn.prepareStatement(INSERT_INTO_XID_ACTIONS); + + try + { + stmt.setLong(1,format); + stmt.setBytes(2, globalId); + stmt.setBytes(3, branchId); + + if(enqueues != null) + { + stmt.setString(4, "E"); + for(Transaction.Record record : enqueues) + { + stmt.setString(5, record.getQueue().getId().toString()); + stmt.setLong(6, record.getMessage().getMessageNumber()); + stmt.executeUpdate(); + } + } + + if(dequeues != null) + { + stmt.setString(4, "D"); + for(Transaction.Record record : dequeues) + { + stmt.setString(5, record.getQueue().getId().toString()); + stmt.setLong(6, record.getMessage().getMessageNumber()); + stmt.executeUpdate(); + } + } + + } + finally + { + stmt.close(); + } + + } + catch (SQLException e) + { + _logger.error("Failed to enqueue: " + e.getMessage(), e); + throw new AMQStoreException("Error writing xid ", e); + } + + } + + private static final class ConnectionWrapper + { + private final Connection _connection; + + public ConnectionWrapper(Connection conn) + { + _connection = conn; + } + + public Connection getConnection() + { + return _connection; + } + } + + + public void commitTran(ConnectionWrapper connWrapper) throws AMQStoreException + { + + try + { + Connection conn = connWrapper.getConnection(); + conn.commit(); + + if (_logger.isDebugEnabled()) + { + _logger.debug("commit tran completed"); + } + + conn.close(); + } + catch (SQLException e) + { + throw new AMQStoreException("Error commit tx: " + e.getMessage(), e); + } + finally + { + + } + } + + public StoreFuture commitTranAsync(ConnectionWrapper connWrapper) throws AMQStoreException + { + commitTran(connWrapper); + return StoreFuture.IMMEDIATE_FUTURE; + } + + public void abortTran(ConnectionWrapper connWrapper) throws AMQStoreException + { + if (connWrapper == null) + { + throw new AMQStoreException("Fatal internal error: transactional context is empty at abortTran"); + } + + if (_logger.isDebugEnabled()) + { + _logger.debug("abort tran called: " + connWrapper.getConnection()); + } + + try + { + Connection conn = connWrapper.getConnection(); + conn.rollback(); + conn.close(); + } + catch (SQLException e) + { + throw new AMQStoreException("Error aborting transaction: " + e.getMessage(), e); + } + + } + + public Long getNewMessageId() + { + return _messageId.incrementAndGet(); + } + + + private void storeMetaData(Connection conn, long messageId, StorableMessageMetaData metaData) + throws SQLException + { + if(_logger.isDebugEnabled()) + { + _logger.debug("Adding metadata for message " +messageId); + } + + PreparedStatement stmt = conn.prepareStatement(INSERT_INTO_META_DATA); + try + { + stmt.setLong(1,messageId); + + final int bodySize = 1 + metaData.getStorableSize(); + byte[] underlying = new byte[bodySize]; + underlying[0] = (byte) metaData.getType().ordinal(); + java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(underlying); + buf.position(1); + buf = buf.slice(); + + metaData.writeToBuffer(0, buf); + ByteArrayInputStream bis = new ByteArrayInputStream(underlying); + try + { + stmt.setBinaryStream(2,bis,underlying.length); + int result = stmt.executeUpdate(); + + if(result == 0) + { + throw new RuntimeException("Unable to add meta data for message " +messageId); + } + } + finally + { + try + { + bis.close(); + } + catch (IOException e) + { + + throw new SQLException(e); + } + } + + } + finally + { + stmt.close(); + } + + } + + + + + private void recoverMessages(MessageStoreRecoveryHandler recoveryHandler) throws SQLException + { + Connection conn = newAutoCommitConnection(); + try + { + MessageStoreRecoveryHandler.StoredMessageRecoveryHandler messageHandler = recoveryHandler.begin(); + + Statement stmt = conn.createStatement(); + try + { + ResultSet rs = stmt.executeQuery(SELECT_ALL_FROM_META_DATA); + try + { + + long maxId = 0; + + while(rs.next()) + { + + long messageId = rs.getLong(1); + Blob dataAsBlob = rs.getBlob(2); + + if(messageId > maxId) + { + maxId = messageId; + } + + byte[] dataAsBytes = dataAsBlob.getBytes(1,(int) dataAsBlob.length()); + java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(dataAsBytes); + buf.position(1); + buf = buf.slice(); + MessageMetaDataType type = MessageMetaDataType.values()[dataAsBytes[0]]; + StorableMessageMetaData metaData = type.getFactory().createMetaData(buf); + StoredDerbyMessage message = new StoredDerbyMessage(messageId, metaData, false); + messageHandler.message(message); + } + + _messageId.set(maxId); + + messageHandler.completeMessageRecovery(); + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + } + finally + { + conn.close(); + } + } + + + + private TransactionLogRecoveryHandler.DtxRecordRecoveryHandler recoverQueueEntries(TransactionLogRecoveryHandler recoveryHandler) throws SQLException + { + Connection conn = newAutoCommitConnection(); + try + { + TransactionLogRecoveryHandler.QueueEntryRecoveryHandler queueEntryHandler = recoveryHandler.begin(this); + + Statement stmt = conn.createStatement(); + try + { + ResultSet rs = stmt.executeQuery(SELECT_FROM_QUEUE_ENTRY); + try + { + while(rs.next()) + { + + String id = rs.getString(1); + long messageId = rs.getLong(2); + queueEntryHandler.queueEntry(UUID.fromString(id), messageId); + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + + return queueEntryHandler.completeQueueEntryRecovery(); + } + finally + { + conn.close(); + } + } + + private static final class Xid + { + + private final long _format; + private final byte[] _globalId; + private final byte[] _branchId; + + public Xid(long format, byte[] globalId, byte[] branchId) + { + _format = format; + _globalId = globalId; + _branchId = branchId; + } + + public long getFormat() + { + return _format; + } + + public byte[] getGlobalId() + { + return _globalId; + } + + public byte[] getBranchId() + { + return _branchId; + } + } + + private static class RecordImpl implements Transaction.Record, TransactionLogResource, EnqueableMessage + { + + private long _messageNumber; + private UUID _queueId; + + public RecordImpl(UUID queueId, long messageNumber) + { + _messageNumber = messageNumber; + _queueId = queueId; + } + + @Override + public TransactionLogResource getQueue() + { + return this; + } + + @Override + public EnqueableMessage getMessage() + { + return this; + } + + @Override + public long getMessageNumber() + { + return _messageNumber; + } + + @Override + public boolean isPersistent() + { + return true; + } + + @Override + public StoredMessage getStoredMessage() + { + throw new UnsupportedOperationException(); + } + + @Override + public UUID getId() + { + return _queueId; + } + } + + private void recoverXids(TransactionLogRecoveryHandler.DtxRecordRecoveryHandler dtxrh) throws SQLException + { + Connection conn = newAutoCommitConnection(); + try + { + List xids = new ArrayList(); + + Statement stmt = conn.createStatement(); + try + { + ResultSet rs = stmt.executeQuery(SELECT_ALL_FROM_XIDS); + try + { + while(rs.next()) + { + + long format = rs.getLong(1); + byte[] globalId = rs.getBytes(2); + byte[] branchId = rs.getBytes(3); + xids.add(new Xid(format, globalId, branchId)); + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + + + + for(Xid xid : xids) + { + List enqueues = new ArrayList(); + List dequeues = new ArrayList(); + + PreparedStatement pstmt = conn.prepareStatement(SELECT_ALL_FROM_XID_ACTIONS); + + try + { + pstmt.setLong(1, xid.getFormat()); + pstmt.setBytes(2, xid.getGlobalId()); + pstmt.setBytes(3, xid.getBranchId()); + + ResultSet rs = pstmt.executeQuery(); + try + { + while(rs.next()) + { + + String actionType = rs.getString(1); + UUID queueId = UUID.fromString(rs.getString(2)); + long messageId = rs.getLong(3); + + RecordImpl record = new RecordImpl(queueId, messageId); + List records = "E".equals(actionType) ? enqueues : dequeues; + records.add(record); + } + } + finally + { + rs.close(); + } + } + finally + { + pstmt.close(); + } + + dtxrh.dtxRecord(xid.getFormat(), xid.getGlobalId(), xid.getBranchId(), + enqueues.toArray(new RecordImpl[enqueues.size()]), + dequeues.toArray(new RecordImpl[dequeues.size()])); + } + + + dtxrh.completeDtxRecordRecovery(); + } + finally + { + conn.close(); + } + + } + + StorableMessageMetaData getMetaData(long messageId) throws SQLException + { + + Connection conn = newAutoCommitConnection(); + try + { + PreparedStatement stmt = conn.prepareStatement(SELECT_FROM_META_DATA); + try + { + stmt.setLong(1,messageId); + ResultSet rs = stmt.executeQuery(); + try + { + + if(rs.next()) + { + Blob dataAsBlob = rs.getBlob(1); + + byte[] dataAsBytes = dataAsBlob.getBytes(1,(int) dataAsBlob.length()); + java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(dataAsBytes); + buf.position(1); + buf = buf.slice(); + MessageMetaDataType type = MessageMetaDataType.values()[dataAsBytes[0]]; + StorableMessageMetaData metaData = type.getFactory().createMetaData(buf); + + return metaData; + } + else + { + throw new RuntimeException("Meta data not found for message with id " + messageId); + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + } + finally + { + conn.close(); + } + } + + + private void addContent(Connection conn, long messageId, ByteBuffer src) + { + if(_logger.isDebugEnabled()) + { + _logger.debug("Adding content for message " +messageId); + } + PreparedStatement stmt = null; + + try + { + src = src.slice(); + + byte[] chunkData = new byte[src.limit()]; + src.duplicate().get(chunkData); + + stmt = conn.prepareStatement(INSERT_INTO_MESSAGE_CONTENT); + stmt.setLong(1,messageId); + + ByteArrayInputStream bis = new ByteArrayInputStream(chunkData); + stmt.setBinaryStream(2, bis, chunkData.length); + stmt.executeUpdate(); + } + catch (SQLException e) + { + closeConnection(conn); + throw new RuntimeException("Error adding content for message " + messageId + ": " + e.getMessage(), e); + } + finally + { + closePreparedStatement(stmt); + } + + } + + + public int getContent(long messageId, int offset, ByteBuffer dst) + { + Connection conn = null; + PreparedStatement stmt = null; + + try + { + conn = newAutoCommitConnection(); + + stmt = conn.prepareStatement(SELECT_FROM_MESSAGE_CONTENT); + stmt.setLong(1,messageId); + ResultSet rs = stmt.executeQuery(); + + int written = 0; + + if (rs.next()) + { + + Blob dataAsBlob = rs.getBlob(1); + + final int size = (int) dataAsBlob.length(); + byte[] dataAsBytes = dataAsBlob.getBytes(1, size); + + if (offset > size) + { + throw new RuntimeException("Offset " + offset + " is greater than message size " + size + + " for message id " + messageId + "!"); + + } + + written = size - offset; + if(written > dst.remaining()) + { + written = dst.remaining(); + } + + dst.put(dataAsBytes, offset, written); + } + + return written; + + } + catch (SQLException e) + { + throw new RuntimeException("Error retrieving content from offset " + offset + " for message " + messageId + ": " + e.getMessage(), e); + } + finally + { + closePreparedStatement(stmt); + closeConnection(conn); + } + + + } + + @Override + public boolean isPersistent() + { + return true; + } + + + private class DerbyTransaction implements Transaction + { + private final ConnectionWrapper _connWrapper; + + + private DerbyTransaction() + { + try + { + _connWrapper = new ConnectionWrapper(newConnection()); + } + catch (SQLException e) + { + throw new RuntimeException(e); + } + } + + @Override + public void enqueueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException + { + if(message.getStoredMessage() instanceof StoredDerbyMessage) + { + try + { + ((StoredDerbyMessage)message.getStoredMessage()).store(_connWrapper.getConnection()); + } + catch (SQLException e) + { + throw new AMQStoreException("Exception on enqueuing message " + _messageId, e); + } + } + + DerbyMessageStore.this.enqueueMessage(_connWrapper, queue, message.getMessageNumber()); + } + + @Override + public void dequeueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException + { + DerbyMessageStore.this.dequeueMessage(_connWrapper, queue, message.getMessageNumber()); + + } + + @Override + public void commitTran() throws AMQStoreException + { + DerbyMessageStore.this.commitTran(_connWrapper); + } + + @Override + public StoreFuture commitTranAsync() throws AMQStoreException + { + return DerbyMessageStore.this.commitTranAsync(_connWrapper); + } + + @Override + public void abortTran() throws AMQStoreException + { + DerbyMessageStore.this.abortTran(_connWrapper); + } + + @Override + public void removeXid(long format, byte[] globalId, byte[] branchId) throws AMQStoreException + { + DerbyMessageStore.this.removeXid(_connWrapper, format, globalId, branchId); + } + + @Override + public void recordXid(long format, byte[] globalId, byte[] branchId, Record[] enqueues, Record[] dequeues) + throws AMQStoreException + { + DerbyMessageStore.this.recordXid(_connWrapper, format, globalId, branchId, enqueues, dequeues); + } + } + + + + private class StoredDerbyMessage implements StoredMessage + { + + private final long _messageId; + private StorableMessageMetaData _metaData; + private volatile SoftReference _metaDataRef; + private byte[] _data; + private volatile SoftReference _dataRef; + + + StoredDerbyMessage(long messageId, StorableMessageMetaData metaData) + { + this(messageId, metaData, true); + } + + + StoredDerbyMessage(long messageId, + StorableMessageMetaData metaData, boolean persist) + { + _messageId = messageId; + + + _metaDataRef = new SoftReference(metaData); + if(persist) + { + _metaData = metaData; + } + } + + @Override + public StorableMessageMetaData getMetaData() + { + StorableMessageMetaData metaData = _metaData == null ? _metaDataRef.get() : _metaData; + if(metaData == null) + { + try + { + metaData = DerbyMessageStore.this.getMetaData(_messageId); + } + catch (SQLException e) + { + throw new RuntimeException(e); + } + _metaDataRef = new SoftReference(metaData); + } + + return metaData; + } + + @Override + public long getMessageNumber() + { + return _messageId; + } + + @Override + public void addContent(int offsetInMessage, java.nio.ByteBuffer src) + { + src = src.slice(); + + if(_data == null) + { + _data = new byte[src.remaining()]; + _dataRef = new SoftReference(_data); + src.duplicate().get(_data); + } + else + { + byte[] oldData = _data; + _data = new byte[oldData.length + src.remaining()]; + _dataRef = new SoftReference(_data); + + System.arraycopy(oldData,0,_data,0,oldData.length); + src.duplicate().get(_data, oldData.length, src.remaining()); + } + + } + + @Override + public int getContent(int offsetInMessage, java.nio.ByteBuffer dst) + { + byte[] data = _dataRef == null ? null : _dataRef.get(); + if(data != null) + { + int length = Math.min(dst.remaining(), data.length - offsetInMessage); + dst.put(data, offsetInMessage, length); + return length; + } + else + { + return DerbyMessageStore.this.getContent(_messageId, offsetInMessage, dst); + } + } + + + @Override + public ByteBuffer getContent(int offsetInMessage, int size) + { + ByteBuffer buf = ByteBuffer.allocate(size); + getContent(offsetInMessage, buf); + buf.position(0); + return buf; + } + + @Override + public synchronized StoreFuture flushToStore() + { + try + { + if(_metaData != null) + { + Connection conn = newConnection(); + + store(conn); + + conn.commit(); + conn.close(); + } + } + catch (SQLException e) + { + if(_logger.isDebugEnabled()) + { + _logger.debug("Error when trying to flush message " + _messageId + " to store: " + e); + } + throw new RuntimeException(e); + } + return StoreFuture.IMMEDIATE_FUTURE; + } + + private synchronized void store(final Connection conn) throws SQLException + { + if(_metaData != null) + { + try + { + storeMetaData(conn, _messageId, _metaData); + DerbyMessageStore.this.addContent(conn, _messageId, + _data == null ? ByteBuffer.allocate(0) : ByteBuffer.wrap(_data)); + } + finally + { + _metaData = null; + _data = null; + } + } + + if(_logger.isDebugEnabled()) + { + _logger.debug("Storing message " + _messageId + " to store"); + } + } + + @Override + public void remove() + { + DerbyMessageStore.this.removeMessage(_messageId); + } + } + + private void closeConnection(final Connection conn) + { + if(conn != null) + { + try + { + conn.close(); + } + catch (SQLException e) + { + _logger.error("Problem closing connection", e); + } + } + } + + private void closePreparedStatement(final PreparedStatement stmt) + { + if (stmt != null) + { + try + { + stmt.close(); + } + catch(SQLException e) + { + _logger.error("Problem closing prepared statement", e); + } + } + } + + @Override + public void addEventListener(EventListener eventListener, Event... events) + { + _eventManager.addEventListener(eventListener, events); + } + + @Override + public String getStoreLocation() + { + return _storeLocation; + } + + private void insertConfiguredObject(ConfiguredObjectRecord configuredObject) throws AMQStoreException + { + if (_stateManager.isInState(State.ACTIVE)) + { + try + { + Connection conn = newAutoCommitConnection(); + try + { + PreparedStatement stmt = conn.prepareStatement(FIND_CONFIGURED_OBJECT); + try + { + stmt.setString(1, configuredObject.getId().toString()); + ResultSet rs = stmt.executeQuery(); + try + { + // If we don't have any data in the result set then we can add this configured object + if (!rs.next()) + { + PreparedStatement insertStmt = conn.prepareStatement(INSERT_INTO_CONFIGURED_OBJECTS); + try + { + insertStmt.setString(1, configuredObject.getId().toString()); + insertStmt.setString(2, configuredObject.getType()); + if(configuredObject.getAttributes() == null) + { + insertStmt.setNull(3, Types.BLOB); + } + else + { + byte[] attributesAsBytes = configuredObject.getAttributes().getBytes(UTF8_CHARSET); + ByteArrayInputStream bis = new ByteArrayInputStream(attributesAsBytes); + insertStmt.setBinaryStream(3, bis, attributesAsBytes.length); + } + insertStmt.execute(); + } + finally + { + insertStmt.close(); + } + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + } + finally + { + conn.close(); + } + } + catch (SQLException e) + { + throw new AMQStoreException("Error inserting of configured object " + configuredObject + " into database: " + e.getMessage(), e); + } + } + } + + private int removeConfiguredObject(UUID id) throws AMQStoreException + { + int results = 0; + try + { + Connection conn = newAutoCommitConnection(); + try + { + PreparedStatement stmt = conn.prepareStatement(DELETE_FROM_CONFIGURED_OBJECTS); + try + { + stmt.setString(1, id.toString()); + results = stmt.executeUpdate(); + } + finally + { + stmt.close(); + } + } + finally + { + conn.close(); + } + } + catch (SQLException e) + { + throw new AMQStoreException("Error deleting of configured object with id " + id + " from database: " + e.getMessage(), e); + } + return results; + } + + private void updateConfiguredObject(final ConfiguredObjectRecord configuredObject) throws AMQStoreException + { + if (_stateManager.isInState(State.ACTIVE)) + { + try + { + Connection conn = newAutoCommitConnection(); + try + { + PreparedStatement stmt = conn.prepareStatement(FIND_CONFIGURED_OBJECT); + try + { + stmt.setString(1, configuredObject.getId().toString()); + ResultSet rs = stmt.executeQuery(); + try + { + if (rs.next()) + { + PreparedStatement stmt2 = conn.prepareStatement(UPDATE_CONFIGURED_OBJECTS); + try + { + stmt2.setString(1, configuredObject.getType()); + if (configuredObject.getAttributes() != null) + { + byte[] attributesAsBytes = configuredObject.getAttributes().getBytes(UTF8_CHARSET); + ByteArrayInputStream bis = new ByteArrayInputStream(attributesAsBytes); + stmt2.setBinaryStream(2, bis, attributesAsBytes.length); + } + else + { + stmt2.setNull(2, Types.BLOB); + } + stmt2.setString(3, configuredObject.getId().toString()); + stmt2.execute(); + } + finally + { + stmt2.close(); + } + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + } + finally + { + conn.close(); + } + } + catch (SQLException e) + { + throw new AMQStoreException("Error updating configured object " + configuredObject + " in database: " + e.getMessage(), e); + } + } + } + + private ConfiguredObjectRecord loadConfiguredObject(final UUID id) throws AMQStoreException + { + ConfiguredObjectRecord result = null; + try + { + Connection conn = newAutoCommitConnection(); + try + { + PreparedStatement stmt = conn.prepareStatement(FIND_CONFIGURED_OBJECT); + try + { + stmt.setString(1, id.toString()); + ResultSet rs = stmt.executeQuery(); + try + { + if (rs.next()) + { + String type = rs.getString(1); + Blob blob = rs.getBlob(2); + String attributes = null; + if (blob != null) + { + attributes = blobToString(blob); + } + result = new ConfiguredObjectRecord(id, type, attributes); + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + } + finally + { + conn.close(); + } + } + catch (SQLException e) + { + throw new AMQStoreException("Error loading of configured object with id " + id + " from database: " + + e.getMessage(), e); + } + return result; + } + + private String blobToString(Blob blob) throws SQLException + { + byte[] bytes = blob.getBytes(1, (int)blob.length()); + return new String(bytes, UTF8_CHARSET); + } + + private List loadConfiguredObjects() throws SQLException + { + ArrayList results = new ArrayList(); + Connection conn = newAutoCommitConnection(); + try + { + PreparedStatement stmt = conn.prepareStatement(SELECT_FROM_CONFIGURED_OBJECTS); + try + { + ResultSet rs = stmt.executeQuery(); + try + { + while (rs.next()) + { + String id = rs.getString(1); + String objectType = rs.getString(2); + String attributes = blobToString(rs.getBlob(3)); + results.add(new ConfiguredObjectRecord(UUID.fromString(id), objectType, attributes)); + } + } + finally + { + rs.close(); + } + } + finally + { + stmt.close(); + } + } + finally + { + conn.close(); + } + return results; + } +} \ No newline at end of file diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStoreFactory.java b/java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStoreFactory.java new file mode 100644 index 0000000000..12d7f64a8d --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/store/derby/DerbyMessageStoreFactory.java @@ -0,0 +1,40 @@ +/* + * 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.store.derby; + +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.MessageStoreFactory; + +public class DerbyMessageStoreFactory implements MessageStoreFactory +{ + + @Override + public MessageStore createMessageStore() + { + return new DerbyMessageStore(); + } + + @Override + public String getStoreClassName() + { + return DerbyMessageStore.class.getSimpleName(); + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java index f8a585b562..66825caa24 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java @@ -54,16 +54,12 @@ public interface Subscription void setNoLocal(boolean noLocal); - AMQShortString getConsumerTag(); - long getSubscriptionID(); boolean isSuspended(); boolean hasInterest(QueueEntry msg); - boolean isAutoClose(); - boolean isClosed(); boolean acquires(); @@ -105,11 +101,11 @@ public interface Subscription boolean isActive(); - void confirmAutoClose(); - public void set(String key, Object value); public Object get(String key); boolean isSessionTransactional(); + + void queueEmpty() throws AMQException; } diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java index 32baa17fc7..1f25c215cc 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java @@ -375,7 +375,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage { return getQueue().getConfigStore(); } - + public Long getDelivered() { return _deliveredCount.get(); @@ -810,12 +810,22 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage { 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); diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java index 9a7d69b10d..76d975a789 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java +++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java @@ -109,7 +109,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr public void stateChange(Subscription sub, State oldState, State newState) { - CurrentActor.get().message(SubscriptionMessages.STATE(newState.toString())); + CurrentActor.get().message(SubscriptionMessages.STATE(newState.toString())); } }; private AMQQueue _queue; @@ -199,12 +199,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr CurrentActor.get().message(this, SubscriptionMessages.CREATE(filterLogString, queue.isDurable() && exclusive, filterLogString.length() > 0)); } - - } - public AMQShortString getConsumerTag() - { - return new AMQShortString(_destination); } public boolean isSuspended() @@ -244,12 +239,6 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr return (_filters == null) || _filters.allAllow(entry); } - public boolean isAutoClose() - { - // no such thing in 0-10 - return false; - } - public boolean isClosed() { return getState() == State.CLOSED; @@ -302,7 +291,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr { return getQueue().getConfigStore(); } - + public Long getDelivered() { return _deliveredCount.get(); @@ -409,6 +398,10 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr { deliveryProps.setTimestamp(origDeliveryProps.getTimestamp()); } + if(origDeliveryProps.hasTtl()) + { + deliveryProps.setTtl(origDeliveryProps.getTtl()); + } } @@ -684,7 +677,10 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr { entry.setRedelivered(); entry.routeToAlternate(); - + if(entry.isAcquiredBy(this)) + { + entry.discard(); + } } void release(final QueueEntry entry, final boolean setRedelivered) @@ -816,11 +812,6 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr return getState() == State.ACTIVE; } - public void confirmAutoClose() - { - //No such thing in 0-10 - } - public void set(String key, Object value) { _properties.put(key, value); @@ -1019,6 +1010,10 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr return _session.isTransactional(); } + public void queueEmpty() + { + } + public long getCreateTime() { return _createTime; @@ -1026,7 +1021,7 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr public String toLogString() { - String queueInfo = MessageFormat.format(QUEUE_FORMAT, _queue.getVirtualHost().getName(), + String queueInfo = MessageFormat.format(QUEUE_FORMAT, _queue.getVirtualHost().getName(), _queue.getNameShortString()); String result = "[" + MessageFormat.format(SUBSCRIPTION_FORMAT, getSubscriptionID()) + "(" // queueString is "vh(/{0})/qu({1}) " so need to trim diff --git a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java index 02c4ffa012..5460c89eab 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java +++ b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java @@ -33,6 +33,7 @@ import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.auth.AuthenticationResult; import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; import org.apache.qpid.server.subscription.Subscription_0_10; +import org.apache.qpid.server.virtualhost.State; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.transport.*; import org.slf4j.Logger; @@ -177,6 +178,11 @@ public class ServerConnectionDelegate extends ServerDelegate sconn.setState(Connection.State.CLOSING); sconn.invoke(new ConnectionClose(ConnectionCloseCode.CONNECTION_FORCED, "Permission denied '"+vhostName+"'")); } + else if (vhost.getState() != State.ACTIVE) + { + sconn.setState(Connection.State.CLOSING); + sconn.invoke(new ConnectionClose(ConnectionCloseCode.CONNECTION_FORCED, "Virtual host '"+vhostName+"' is not active")); + } else { sconn.setState(Connection.State.OPEN); @@ -231,23 +237,26 @@ public class ServerConnectionDelegate extends ServerDelegate @Override public void sessionDetach(Connection conn, SessionDetach dtc) { - // To ensure a clean detach, we unregister any remaining subscriptions. Unregister ensures - // that any in-progress delivery (SubFlushRunner/QueueRunner) is completed before the unregister + // To ensure a clean detach, we stop any remaining subscriptions. Stop ensures + // that any in-progress delivery (SubFlushRunner/QueueRunner) is completed before the stop // completes. - unregisterAllSubscriptions(conn, dtc); + stopAllSubscriptions(conn, dtc); + Session ssn = conn.getSession(dtc.getChannel()); + ((ServerSession)ssn).setClose(true); super.sessionDetach(conn, dtc); } - private void unregisterAllSubscriptions(Connection conn, SessionDetach dtc) + 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) { - ssn.unregister(subscription_0_10); + subscription_0_10.stop(); } } + @Override public void sessionAttach(final Connection conn, final SessionAttach atc) { @@ -255,8 +264,7 @@ public class ServerConnectionDelegate extends ServerDelegate if(isSessionNameUnique(atc.getName(), conn)) { - ssn = sessionAttachImpl(conn, atc); - conn.registerSession(ssn); + super.sessionAttach(conn, atc); ((ServerConnection)conn).checkForNotification(); } else @@ -306,4 +314,4 @@ public class ServerConnectionDelegate extends ServerDelegate { return _clientProperties == null ? null : (String) _clientProperties.get(ConnectionStartProperties.VERSION_0_10); } -} \ No newline at end of file +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java index e4268ed2dc..6f979e035e 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java +++ b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java @@ -21,6 +21,11 @@ package org.apache.qpid.server.transport; import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.CHANNEL_FORMAT; +import org.apache.qpid.server.message.InboundMessage; +import org.apache.qpid.server.message.MessageMetaData_0_10; +import org.apache.qpid.server.message.MessageTransferMessage; +import org.apache.qpid.server.txn.RollbackOnlyDtxException; +import org.apache.qpid.server.txn.TimeoutDtxException; import static org.apache.qpid.util.Serial.gt; import java.security.Principal; @@ -44,6 +49,7 @@ import java.util.concurrent.atomic.AtomicLong; import javax.security.auth.Subject; import org.apache.qpid.AMQException; +import org.apache.qpid.AMQStoreException; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.ProtocolEngine; import org.apache.qpid.server.configuration.ConfigStore; @@ -66,25 +72,21 @@ 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.subscription.Subscription_0_10; +import org.apache.qpid.server.txn.AlreadyKnownDtxException; import org.apache.qpid.server.txn.AsyncAutoCommitTransaction; +import org.apache.qpid.server.txn.DistributedTransaction; +import org.apache.qpid.server.txn.DtxNotSelectedException; +import org.apache.qpid.server.txn.IncorrectDtxStateException; +import org.apache.qpid.server.txn.JoinAndResumeDtxException; import org.apache.qpid.server.txn.LocalTransaction; +import org.apache.qpid.server.txn.NotAssociatedDtxException; import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.server.txn.SuspendAndFailDtxException; +import org.apache.qpid.server.txn.UnknownDtxBranchException; import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.transport.Binary; -import org.apache.qpid.transport.Connection; -import org.apache.qpid.transport.MessageCreditUnit; -import org.apache.qpid.transport.MessageFlow; -import org.apache.qpid.transport.MessageFlowMode; -import org.apache.qpid.transport.MessageSetFlowMode; -import org.apache.qpid.transport.MessageStop; -import org.apache.qpid.transport.MessageTransfer; -import org.apache.qpid.transport.Method; -import org.apache.qpid.transport.Range; -import org.apache.qpid.transport.RangeSet; -import org.apache.qpid.transport.RangeSetFactory; -import org.apache.qpid.transport.Session; -import org.apache.qpid.transport.SessionDelegate; +import org.apache.qpid.transport.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -109,7 +111,6 @@ public class ServerSession extends Session private ChannelLogSubject _logSubject; private final AtomicInteger _outstandingCredit = new AtomicInteger(UNLIMITED_CREDIT); - public static interface MessageDispositionChangeListener { public void onAccept(); @@ -133,7 +134,7 @@ public class ServerSession extends Session new ConcurrentSkipListMap(); private ServerTransaction _transaction; - + private final AtomicLong _txnStarts = new AtomicLong(0); private final AtomicLong _txnCommits = new AtomicLong(0); private final AtomicLong _txnRejects = new AtomicLong(0); @@ -152,7 +153,7 @@ public class ServerSession extends Session public ServerSession(Connection connection, SessionDelegate delegate, Binary name, long expiry, ConnectionConfig connConfig) { super(connection, delegate, name, expiry); - _connectionConfig = connConfig; + _connectionConfig = connConfig; _transaction = new AsyncAutoCommitTransaction(this.getMessageStore(),this); _logSubject = new ChannelLogSubject(this); _id = getConfigStore().createId(); @@ -352,14 +353,22 @@ public class ServerSession extends Session } } - public void removeDispositionListener(Method method) + public void removeDispositionListener(Method method) { _messageDispositionListenerMap.remove(method.getId()); } public void onClose() { - _transaction.rollback(); + if(_transaction instanceof LocalTransaction) + { + _transaction.rollback(); + } + else if(_transaction instanceof DistributedTransaction) + { + getVirtualHost().getDtxRegistry().endAssociations(this); + } + for(MessageDispositionChangeListener listener : _messageDispositionListenerMap.values()) { listener.onRelease(true); @@ -372,7 +381,7 @@ public class ServerSession extends Session { task.doTask(this); } - + CurrentActor.get().message(getLogSubject(), ChannelMessages.CLOSE()); } @@ -395,6 +404,9 @@ public class ServerSession extends Session public void onRollback() { + // The client has acknowledge the message and therefore have seen it. + // In the event of rollback, the message must be marked as redelivered. + entry.setRedelivered(); entry.release(); } }); @@ -418,7 +430,7 @@ public class ServerSession extends Session public void unregister(Subscription_0_10 sub) { - _subscriptions.remove(sub.getConsumerTag().toString()); + _subscriptions.remove(sub.getName()); try { sub.getSendLock(); @@ -455,10 +467,99 @@ public class ServerSession extends Session _txnStarts.incrementAndGet(); } + public void selectDtx() + { + _transaction = new DistributedTransaction(this, getMessageStore(), getVirtualHost()); + + } + + + public void startDtx(Xid xid, boolean join, boolean resume) + throws JoinAndResumeDtxException, + UnknownDtxBranchException, + AlreadyKnownDtxException, + DtxNotSelectedException + { + DistributedTransaction distributedTransaction = assertDtxTransaction(); + distributedTransaction.start(xid, join, resume); + } + + + public void endDtx(Xid xid, boolean fail, boolean suspend) + throws NotAssociatedDtxException, + UnknownDtxBranchException, + DtxNotSelectedException, + SuspendAndFailDtxException, TimeoutDtxException + { + DistributedTransaction distributedTransaction = assertDtxTransaction(); + distributedTransaction.end(xid, fail, suspend); + } + + + public long getTimeoutDtx(Xid xid) + throws UnknownDtxBranchException + { + return getVirtualHost().getDtxRegistry().getTimeout(xid); + } + + + public void setTimeoutDtx(Xid xid, long timeout) + throws UnknownDtxBranchException + { + getVirtualHost().getDtxRegistry().setTimeout(xid, timeout); + } + + + public void prepareDtx(Xid xid) + throws UnknownDtxBranchException, + IncorrectDtxStateException, AMQStoreException, RollbackOnlyDtxException, TimeoutDtxException + { + getVirtualHost().getDtxRegistry().prepare(xid); + } + + public void commitDtx(Xid xid, boolean onePhase) + throws UnknownDtxBranchException, + IncorrectDtxStateException, AMQStoreException, RollbackOnlyDtxException, TimeoutDtxException + { + getVirtualHost().getDtxRegistry().commit(xid, onePhase); + } + + + public void rollbackDtx(Xid xid) + throws UnknownDtxBranchException, + IncorrectDtxStateException, AMQStoreException, TimeoutDtxException + { + getVirtualHost().getDtxRegistry().rollback(xid); + } + + + public void forgetDtx(Xid xid) throws UnknownDtxBranchException, IncorrectDtxStateException + { + getVirtualHost().getDtxRegistry().forget(xid); + } + + public List recoverDtx() + { + return getVirtualHost().getDtxRegistry().recover(); + } + + private DistributedTransaction assertDtxTransaction() throws DtxNotSelectedException + { + if(_transaction instanceof DistributedTransaction) + { + return (DistributedTransaction) _transaction; + } + else + { + throw new DtxNotSelectedException(); + } + } + + public void commit() { _transaction.commit(); - + _txnCommits.incrementAndGet(); _txnStarts.incrementAndGet(); decrementOutstandingTxnsIfNecessary(); @@ -467,13 +568,13 @@ public class ServerSession extends Session public void rollback() { _transaction.rollback(); - + _txnRejects.incrementAndGet(); _txnStarts.incrementAndGet(); decrementOutstandingTxnsIfNecessary(); } - + private void incrementOutstandingTxnsIfNecessary() { if(isTransactional()) @@ -483,7 +584,7 @@ public class ServerSession extends Session _txnCount.compareAndSet(0,1); } } - + private void decrementOutstandingTxnsIfNecessary() { if(isTransactional()) @@ -524,7 +625,7 @@ public class ServerSession extends Session { return _txnCount.get(); } - + public Principal getAuthorizedPrincipal() { return getConnection().getAuthorizedPrincipal(); @@ -620,11 +721,6 @@ public class ServerSession extends Session close(); } - public Object getID() - { - return getName(); - } - public AMQConnectionModel getConnectionModel() { return getConnection(); @@ -704,7 +800,7 @@ public class ServerSession extends Session { if(_blockingQueues.remove(queue) && _blockingQueues.isEmpty()) { - if(_blocking.compareAndSet(true,false)) + if(_blocking.compareAndSet(true,false) && !isClosing()) { _actor.message(_logSubject, ChannelMessages.FLOW_REMOVED()); @@ -720,6 +816,14 @@ public class ServerSession extends Session } } + public boolean onSameConnection(InboundMessage inbound) + { + return ((inbound instanceof MessageTransferMessage) + && ((MessageTransferMessage)inbound).getConnectionReference() == getConnection().getReference()) + || ((inbound instanceof MessageMetaData_0_10) + && (((MessageMetaData_0_10)inbound).getConnectionReference())== getConnection().getReference()); + } + public String toLogString() { @@ -746,7 +850,6 @@ public class ServerSession extends Session // unregister subscriptions in order to prevent sending of new messages // to subscriptions with closing session unregisterSubscriptions(); - super.close(); } @@ -759,6 +862,16 @@ public class ServerSession extends Session } } + void stopSubscriptions() + { + final Collection subscriptions = getSubscriptions(); + for (Subscription_0_10 subscription_0_10 : subscriptions) + { + subscription_0_10.stop(); + } + } + + public void receivedComplete() { final Collection subscriptions = getSubscriptions(); @@ -863,17 +976,17 @@ public class ServerSession extends Session return _unfinishedCommandsQueue.isEmpty() ? null : _unfinishedCommandsQueue.getLast(); } - public void recordFuture(final MessageStore.StoreFuture future, final ServerTransaction.Action action) + public void recordFuture(final StoreFuture future, final ServerTransaction.Action action) { _unfinishedCommandsQueue.add(new AsyncCommand(future, action)); } private static class AsyncCommand { - private final MessageStore.StoreFuture _future; + private final StoreFuture _future; private ServerTransaction.Action _action; - public AsyncCommand(final MessageStore.StoreFuture future, final ServerTransaction.Action action) + public AsyncCommand(final StoreFuture future, final ServerTransaction.Action action) { _future = future; _action = action; @@ -900,9 +1013,14 @@ public class ServerSession extends Session } } - @Override + protected void setClose(boolean close) + { + super.setClose(close); + } + public int compareTo(AMQSessionModel session) { - return getId().toString().compareTo(session.getID().toString()); + return getId().compareTo(session.getId()); } + } diff --git a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java index 8e4fb3635b..79a8bc0e4c 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java +++ b/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java @@ -23,6 +23,7 @@ package org.apache.qpid.server.transport; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; +import org.apache.qpid.AMQStoreException; import org.apache.qpid.AMQUnknownExchangeType; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; @@ -39,6 +40,7 @@ import org.apache.qpid.server.flow.WindowCreditManager; import org.apache.qpid.server.logging.messages.ExchangeMessages; import org.apache.qpid.server.message.MessageMetaData_0_10; import org.apache.qpid.server.message.MessageTransferMessage; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.queue.BaseQueue; @@ -47,10 +49,20 @@ import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.security.SecurityManager; 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.subscription.SubscriptionFactoryImpl; import org.apache.qpid.server.subscription.Subscription_0_10; +import org.apache.qpid.server.txn.AlreadyKnownDtxException; +import org.apache.qpid.server.txn.DtxNotSelectedException; +import org.apache.qpid.server.txn.IncorrectDtxStateException; +import org.apache.qpid.server.txn.JoinAndResumeDtxException; +import org.apache.qpid.server.txn.NotAssociatedDtxException; +import org.apache.qpid.server.txn.RollbackOnlyDtxException; 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.virtualhost.VirtualHost; import org.apache.qpid.transport.*; @@ -116,7 +128,7 @@ public class ServerSessionDelegate extends SessionDelegate serverSession.accept(method.getTransfers()); if(!serverSession.isTransactional()) { - serverSession.recordFuture(MessageStore.IMMEDIATE_FUTURE, + serverSession.recordFuture(StoreFuture.IMMEDIATE_FUTURE, new CommandProcessedAction(serverSession, method)); } } @@ -199,6 +211,10 @@ public class ServerSessionDelegate extends SessionDelegate { exception(session,method,ExecutionErrorCode.RESOURCE_LOCKED, "Exclusive Queue: " + queueName + " owned exclusively by another session"); } + else if(queue.isExclusive() && queue.getExclusiveOwningSession() != null && queue.getExclusiveOwningSession() != session) + { + exception(session,method,ExecutionErrorCode.RESOURCE_LOCKED, "Exclusive Queue: " + queueName + " owned exclusively by another session"); + } else { if(queue.isExclusive()) @@ -221,7 +237,6 @@ public class ServerSessionDelegate extends SessionDelegate } }); } - } FlowCreditManager_0_10 creditManager = new WindowCreditManager(0L,0L); @@ -281,6 +296,7 @@ public class ServerSessionDelegate extends SessionDelegate } final MessageMetaData_0_10 messageMetaData = new MessageMetaData_0_10(xfr); + messageMetaData.setConnectionReference(((ServerSession)ssn).getReference()); if (!getVirtualHost(ssn).getSecurityManager().authorisePublish(messageMetaData.isImmediate(), messageMetaData.getRoutingKey(), exchange.getName())) { @@ -342,7 +358,7 @@ public class ServerSessionDelegate extends SessionDelegate } else { - serverSession.recordFuture(MessageStore.IMMEDIATE_FUTURE, new CommandProcessedAction(serverSession, xfr)); + serverSession.recordFuture(StoreFuture.IMMEDIATE_FUTURE, new CommandProcessedAction(serverSession, xfr)); } } @@ -426,6 +442,235 @@ public class ServerSessionDelegate extends SessionDelegate ((ServerSession)session).rollback(); } + @Override + public void dtxSelect(Session session, DtxSelect method) + { + // TODO - check current tx mode + ((ServerSession)session).selectDtx(); + } + + @Override + public void dtxStart(Session session, DtxStart method) + { + XaResult result = new XaResult(); + result.setStatus(DtxXaStatus.XA_OK); + try + { + ((ServerSession)session).startDtx(method.getXid(), method.getJoin(), method.getResume()); + session.executionResult(method.getId(), result); + } + catch(JoinAndResumeDtxException e) + { + exception(session, method, ExecutionErrorCode.COMMAND_INVALID, e.getMessage()); + } + catch(UnknownDtxBranchException e) + { + exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Unknown xid " + method.getXid()); + } + catch(AlreadyKnownDtxException e) + { + exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Xid already started an neither join nor " + + "resume set" + method.getXid()); + } + catch(DtxNotSelectedException e) + { + exception(session, method, ExecutionErrorCode.COMMAND_INVALID, e.getMessage()); + } + + } + + @Override + public void dtxEnd(Session session, DtxEnd method) + { + XaResult result = new XaResult(); + result.setStatus(DtxXaStatus.XA_OK); + try + { + try + { + ((ServerSession)session).endDtx(method.getXid(), method.getFail(), method.getSuspend()); + } + catch (TimeoutDtxException e) + { + result.setStatus(DtxXaStatus.XA_RBTIMEOUT); + } + session.executionResult(method.getId(), result); + } + catch(UnknownDtxBranchException e) + { + exception(session, method, ExecutionErrorCode.ILLEGAL_STATE, e.getMessage()); + } + catch(NotAssociatedDtxException e) + { + exception(session, method, ExecutionErrorCode.ILLEGAL_STATE, e.getMessage()); + } + catch(DtxNotSelectedException e) + { + exception(session, method, ExecutionErrorCode.ILLEGAL_STATE, e.getMessage()); + } + catch(SuspendAndFailDtxException e) + { + exception(session, method, ExecutionErrorCode.COMMAND_INVALID, e.getMessage()); + } + + } + + @Override + public void dtxCommit(Session session, DtxCommit method) + { + XaResult result = new XaResult(); + result.setStatus(DtxXaStatus.XA_OK); + try + { + try + { + ((ServerSession)session).commitDtx(method.getXid(), method.getOnePhase()); + } + catch (RollbackOnlyDtxException e) + { + result.setStatus(DtxXaStatus.XA_RBROLLBACK); + } + catch (TimeoutDtxException e) + { + result.setStatus(DtxXaStatus.XA_RBTIMEOUT); + } + session.executionResult(method.getId(), result); + } + catch(UnknownDtxBranchException e) + { + exception(session, method, ExecutionErrorCode.NOT_FOUND, e.getMessage()); + } + catch(IncorrectDtxStateException e) + { + exception(session, method, ExecutionErrorCode.ILLEGAL_STATE, e.getMessage()); + } + catch(AMQStoreException e) + { + exception(session, method, ExecutionErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public void dtxForget(Session session, DtxForget method) + { + try + { + ((ServerSession)session).forgetDtx(method.getXid()); + } + catch(UnknownDtxBranchException e) + { + exception(session, method, ExecutionErrorCode.NOT_FOUND, e.getMessage()); + } + catch(IncorrectDtxStateException e) + { + exception(session, method, ExecutionErrorCode.ILLEGAL_STATE, e.getMessage()); + } + + } + + @Override + public void dtxGetTimeout(Session session, DtxGetTimeout method) + { + GetTimeoutResult result = new GetTimeoutResult(); + try + { + result.setTimeout(((ServerSession) session).getTimeoutDtx(method.getXid())); + session.executionResult(method.getId(), result); + } + catch(UnknownDtxBranchException e) + { + exception(session, method, ExecutionErrorCode.NOT_FOUND, e.getMessage()); + } + } + + @Override + public void dtxPrepare(Session session, DtxPrepare method) + { + XaResult result = new XaResult(); + result.setStatus(DtxXaStatus.XA_OK); + try + { + try + { + ((ServerSession)session).prepareDtx(method.getXid()); + } + catch (RollbackOnlyDtxException e) + { + result.setStatus(DtxXaStatus.XA_RBROLLBACK); + } + catch (TimeoutDtxException e) + { + result.setStatus(DtxXaStatus.XA_RBTIMEOUT); + } + session.executionResult((int) method.getId(), result); + } + catch(UnknownDtxBranchException e) + { + exception(session, method, ExecutionErrorCode.NOT_FOUND, e.getMessage()); + } + catch(IncorrectDtxStateException e) + { + exception(session, method, ExecutionErrorCode.ILLEGAL_STATE, e.getMessage()); + } + catch(AMQStoreException e) + { + exception(session, method, ExecutionErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public void dtxRecover(Session session, DtxRecover method) + { + RecoverResult result = new RecoverResult(); + List inDoubt = ((ServerSession)session).recoverDtx(); + result.setInDoubt(inDoubt); + session.executionResult(method.getId(), result); + } + + @Override + public void dtxRollback(Session session, DtxRollback method) + { + + XaResult result = new XaResult(); + result.setStatus(DtxXaStatus.XA_OK); + try + { + try + { + ((ServerSession)session).rollbackDtx(method.getXid()); + } + catch (TimeoutDtxException e) + { + result.setStatus(DtxXaStatus.XA_RBTIMEOUT); + } + session.executionResult(method.getId(), result); + } + catch(UnknownDtxBranchException e) + { + exception(session, method, ExecutionErrorCode.NOT_FOUND, e.getMessage()); + } + catch(IncorrectDtxStateException e) + { + exception(session, method, ExecutionErrorCode.ILLEGAL_STATE, e.getMessage()); + } + catch(AMQStoreException e) + { + exception(session, method, ExecutionErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public void dtxSetTimeout(Session session, DtxSetTimeout method) + { + try + { + ((ServerSession)session).setTimeoutDtx(method.getXid(), method.getTimeout()); + } + catch(UnknownDtxBranchException e) + { + exception(session, method, ExecutionErrorCode.NOT_FOUND, e.getMessage()); + } + } @Override public void executionSync(final Session ssn, final ExecutionSync sync) @@ -439,13 +684,12 @@ public class ServerSessionDelegate extends SessionDelegate { String exchangeName = method.getExchange(); VirtualHost virtualHost = getVirtualHost(session); - Exchange exchange = getExchange(session, exchangeName); + ExchangeRegistry exchangeRegistry = getExchangeRegistry(session); //we must check for any unsupported arguments present and throw not-implemented if(method.hasArguments()) { Map args = method.getArguments(); - //QPID-3392: currently we don't support any! if(!args.isEmpty()) { @@ -453,72 +697,113 @@ public class ServerSessionDelegate extends SessionDelegate return; } } - - if(method.getPassive()) + synchronized(exchangeRegistry) { - if(exchange == null) - { - exception(session, method, ExecutionErrorCode.NOT_FOUND, "not-found: exchange-name '"+exchangeName+"'"); + Exchange exchange = getExchange(session, exchangeName); - } - else + if(method.getPassive()) { - if(!exchange.getTypeShortString().toString().equals(method.getType()) && (method.getType() != null && method.getType().length() > 0)) + if(exchange == null) + { + exception(session, method, ExecutionErrorCode.NOT_FOUND, "not-found: exchange-name '" + exchangeName + "'"); + } + else { - exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Attempt to redeclare exchange: " + exchangeName + " of type " + exchange.getTypeShortString() + " to " + method.getType() +"."); + if (!exchange.getTypeShortString().toString().equals(method.getType()) + && (method.getType() != null && method.getType().length() > 0)) + { + exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Attempt to redeclare exchange: " + + exchangeName + " of type " + exchange.getTypeShortString() + " to " + method.getType() + "."); + } } } - - } - else - { - if (exchange == null) + else { - ExchangeRegistry exchangeRegistry = getExchangeRegistry(session); - ExchangeFactory exchangeFactory = virtualHost.getExchangeFactory(); - - - - try + if (exchange == null) { - - exchange = exchangeFactory.createExchange(method.getExchange(), - method.getType(), - method.getDurable(), - method.getAutoDelete()); - - String alternateExchangeName = method.getAlternateExchange(); - if(alternateExchangeName != null && alternateExchangeName.length() != 0) + if (exchangeName.startsWith("amq.")) { - Exchange alternate = getExchange(session, alternateExchangeName); - exchange.setAlternateExchange(alternate); + exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Attempt to declare exchange: " + + exchangeName + " which begins with reserved prefix 'amq.'."); } - - if (exchange.isDurable()) + else if (exchangeName.startsWith("qpid.")) { - DurableConfigurationStore store = virtualHost.getDurableConfigurationStore(); - store.createExchange(exchange); + exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Attempt to declare exchange: " + + exchangeName + " which begins with reserved prefix 'qpid.'."); + } + else + { + ExchangeFactory exchangeFactory = virtualHost.getExchangeFactory(); + try + { + exchange = exchangeFactory.createExchange(method.getExchange(), + method.getType(), + method.getDurable(), + method.getAutoDelete()); + String alternateExchangeName = method.getAlternateExchange(); + boolean validAlternate; + if(alternateExchangeName != null && alternateExchangeName.length() != 0) + { + Exchange alternate = getExchange(session, alternateExchangeName); + if(alternate == null) + { + validAlternate = false; + } + else + { + exchange.setAlternateExchange(alternate); + validAlternate = true; + } + } + else + { + validAlternate = true; + } + if(validAlternate) + { + if (exchange.isDurable()) + { + DurableConfigurationStore store = virtualHost.getMessageStore(); + store.createExchange(exchange); + } + exchangeRegistry.registerExchange(exchange); + } + else + { + exception(session, method, ExecutionErrorCode.NOT_FOUND, + "Unknown alternate exchange " + alternateExchangeName); + } + } + catch(AMQUnknownExchangeType e) + { + exception(session, method, ExecutionErrorCode.NOT_FOUND, "Unknown Exchange Type: " + method.getType()); + } + catch (AMQException e) + { + exception(session, method, e, "Cannot declare exchange '" + exchangeName); + } } - - exchangeRegistry.registerExchange(exchange); - } - catch(AMQUnknownExchangeType e) - { - exception(session, method, ExecutionErrorCode.NOT_FOUND, "Unknown Exchange Type: " + method.getType()); - } - catch (AMQException e) - { - exception(session, method, e, "Cannot declare exchange '" + exchangeName); } - } - else - { - if(!exchange.getTypeShortString().toString().equals(method.getType())) + else { - exception(session, method, ExecutionErrorCode.NOT_ALLOWED, "Attempt to redeclare exchange: " + exchangeName + " of type " + exchange.getTypeShortString() + " to " + method.getType() +"."); + if(!exchange.getTypeShortString().toString().equals(method.getType())) + { + exception(session, method, ExecutionErrorCode.NOT_ALLOWED, + "Attempt to redeclare exchange: " + exchangeName + + " of type " + exchange.getTypeShortString() + + " to " + method.getType() +"."); + } + else if(method.hasAlternateExchange() + && (exchange.getAlternateExchange() == null || + !method.getAlternateExchange().equals(exchange.getAlternateExchange().getName()))) + { + exception(session, method, ExecutionErrorCode.NOT_ALLOWED, + "Attempt to change alternate exchange of: " + exchangeName + + " from " + exchange.getAlternateExchange() + + " to " + method.getAlternateExchange() +"."); + } } } - } } @@ -633,7 +918,7 @@ public class ServerSessionDelegate extends SessionDelegate if (exchange.isDurable() && !exchange.isAutoDelete()) { - DurableConfigurationStore store = virtualHost.getDurableConfigurationStore(); + DurableConfigurationStore store = virtualHost.getMessageStore(); store.removeExchange(exchange); } } @@ -732,10 +1017,7 @@ public class ServerSessionDelegate extends SessionDelegate } else { - AMQShortString routingKey = new AMQShortString(method.getBindingKey()); - FieldTable fieldTable = FieldTable.convertToFieldTable(method.getArguments()); - - if (!exchange.isBound(routingKey, fieldTable, queue)) + if (!exchange.isBound(method.getBindingKey(), method.getArguments(), queue)) { try { @@ -847,12 +1129,6 @@ public class ServerSessionDelegate extends SessionDelegate if(method.hasBindingKey()) { - if(method.hasArguments()) - { - FieldTable args = FieldTable.convertToFieldTable(method.getArguments()); - - result.setArgsNotMatched(!exchange.isBound(new AMQShortString(method.getBindingKey()), args, queue)); - } if(queueMatched) { result.setKeyNotMatched(!exchange.isBound(method.getBindingKey(), queue)); @@ -861,23 +1137,28 @@ public class ServerSessionDelegate extends SessionDelegate { result.setKeyNotMatched(!exchange.isBound(method.getBindingKey())); } + + if(method.hasArguments()) + { + result.setArgsNotMatched(!exchange.isBound(result.getKeyNotMatched() ? null : method.getBindingKey(), method.getArguments(), queueMatched ? queue : null)); + } + } else if (method.hasArguments()) { - // TODO - + result.setArgsNotMatched(!exchange.isBound(null, method.getArguments(), queueMatched ? queue : null)); } - result.setQueueNotMatched(!exchange.isBound(queue)); - } else if(exchange != null && method.hasBindingKey()) { + result.setKeyNotMatched(!exchange.isBound(method.getBindingKey())); + if(method.hasArguments()) { - // TODO + result.setArgsNotMatched(!exchange.isBound(result.getKeyNotMatched() ? null : method.getBindingKey(), method.getArguments(), queue)); } - result.setKeyNotMatched(!exchange.isBound(method.getBindingKey())); + } @@ -886,11 +1167,15 @@ public class ServerSessionDelegate extends SessionDelegate { if(method.hasArguments()) { - // TODO + result.setArgsNotMatched(!exchange.isBound(method.getBindingKey(), method.getArguments(), null)); } result.setKeyNotMatched(!exchange.isBound(method.getBindingKey())); } + else if(exchange != null && method.hasArguments()) + { + result.setArgsNotMatched(!exchange.isBound(null, method.getArguments(), null)); + } session.executionResult((int) method.getId(), result); @@ -914,7 +1199,7 @@ public class ServerSessionDelegate extends SessionDelegate { VirtualHost virtualHost = getVirtualHost(session); - DurableConfigurationStore store = virtualHost.getDurableConfigurationStore(); + DurableConfigurationStore store = virtualHost.getMessageStore(); String queueName = method.getQueue(); AMQQueue queue; @@ -1104,8 +1389,8 @@ public class ServerSessionDelegate extends SessionDelegate { String owner = body.getExclusive() ? session.getClientID() : null; - final AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueName, body.getDurable(), owner, body.getAutoDelete(), - body.getExclusive(), virtualHost, body.getArguments()); + final AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(UUIDGenerator.generateUUID(), queueName, body.getDurable(), owner, + body.getAutoDelete(), body.getExclusive(), virtualHost, body.getArguments()); return queue; } @@ -1134,6 +1419,10 @@ public class ServerSessionDelegate extends SessionDelegate { exception(session,method,ExecutionErrorCode.RESOURCE_LOCKED, "Exclusive Queue: " + queueName + " owned exclusively by another session"); } + else if(queue.isExclusive() && queue.getExclusiveOwningSession() != null && queue.getExclusiveOwningSession() != session) + { + exception(session,method,ExecutionErrorCode.RESOURCE_LOCKED, "Exclusive Queue: " + queueName + " owned exclusively by another session"); + } else if (method.getIfEmpty() && !queue.isEmpty()) { exception(session, method, ExecutionErrorCode.PRECONDITION_FAILED, "Queue " + queueName + " not empty"); @@ -1153,7 +1442,7 @@ public class ServerSessionDelegate extends SessionDelegate queue.delete(); if (queue.isDurable() && !queue.isAutoDelete()) { - DurableConfigurationStore store = virtualHost.getDurableConfigurationStore(); + DurableConfigurationStore store = virtualHost.getMessageStore(); store.removeQueue(queue); } } @@ -1280,8 +1569,9 @@ public class ServerSessionDelegate extends SessionDelegate ServerSession serverSession = (ServerSession)session; - serverSession.unregisterSubscriptions(); + serverSession.stopSubscriptions(); serverSession.onClose(); + serverSession.unregisterSubscriptions(); } @Override diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/AlreadyKnownDtxException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/AlreadyKnownDtxException.java new file mode 100644 index 0000000000..faa4ec592f --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/AlreadyKnownDtxException.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.txn; + +import org.apache.qpid.transport.Xid; + +public class AlreadyKnownDtxException extends DtxException +{ + public AlreadyKnownDtxException(Xid id) + { + super("Xid " + id + " cannot be started as it is already known"); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/AsyncAutoCommitTransaction.java b/java/broker/src/main/java/org/apache/qpid/server/txn/AsyncAutoCommitTransaction.java index a062c6732f..d446434d24 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/txn/AsyncAutoCommitTransaction.java +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/AsyncAutoCommitTransaction.java @@ -29,7 +29,8 @@ 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.MessageStore.StoreFuture; +import org.apache.qpid.server.store.StoreFuture; +import org.apache.qpid.server.store.Transaction; import java.util.Collection; import java.util.List; @@ -71,16 +72,16 @@ public class AsyncAutoCommitTransaction implements ServerTransaction */ public void addPostTransactionAction(final Action immediateAction) { - addFuture(MessageStore.IMMEDIATE_FUTURE, immediateAction); + addFuture(StoreFuture.IMMEDIATE_FUTURE, immediateAction); } public void dequeue(BaseQueue queue, EnqueableMessage message, Action postTransactionAction) { - MessageStore.Transaction txn = null; + Transaction txn = null; try { - MessageStore.StoreFuture future; + StoreFuture future; if(message.isPersistent() && queue.isDurable()) { if (_logger.isDebugEnabled()) @@ -96,7 +97,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction } else { - future = MessageStore.IMMEDIATE_FUTURE; + future = StoreFuture.IMMEDIATE_FUTURE; } addFuture(future, postTransactionAction); postTransactionAction = null; @@ -113,7 +114,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction } - private void addFuture(final MessageStore.StoreFuture future, final Action action) + private void addFuture(final StoreFuture future, final Action action) { if(action != null) { @@ -130,7 +131,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction public void dequeue(Collection queueEntries, Action postTransactionAction) { - MessageStore.Transaction txn = null; + Transaction txn = null; try { for(QueueEntry entry : queueEntries) @@ -154,7 +155,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction } } - MessageStore.StoreFuture future; + StoreFuture future; if(txn != null) { future = txn.commitTranAsync(); @@ -162,7 +163,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction } else { - future = MessageStore.IMMEDIATE_FUTURE; + future = StoreFuture.IMMEDIATE_FUTURE; } addFuture(future, postTransactionAction); postTransactionAction = null; @@ -182,10 +183,10 @@ public class AsyncAutoCommitTransaction implements ServerTransaction public void enqueue(BaseQueue queue, EnqueableMessage message, Action postTransactionAction) { - MessageStore.Transaction txn = null; + Transaction txn = null; try { - MessageStore.StoreFuture future; + StoreFuture future; if(message.isPersistent() && queue.isDurable()) { if (_logger.isDebugEnabled()) @@ -200,7 +201,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction } else { - future = MessageStore.IMMEDIATE_FUTURE; + future = StoreFuture.IMMEDIATE_FUTURE; } addFuture(future, postTransactionAction); postTransactionAction = null; @@ -220,7 +221,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction public void enqueue(List queues, EnqueableMessage message, Action postTransactionAction, long currentTime) { - MessageStore.Transaction txn = null; + Transaction txn = null; try { @@ -246,7 +247,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction } } - MessageStore.StoreFuture future; + StoreFuture future; if (txn != null) { future = txn.commitTranAsync(); @@ -254,7 +255,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction } else { - future = MessageStore.IMMEDIATE_FUTURE; + future = StoreFuture.IMMEDIATE_FUTURE; } addFuture(future, postTransactionAction); postTransactionAction = null; @@ -278,7 +279,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction { if(immediatePostTransactionAction != null) { - addFuture(MessageStore.IMMEDIATE_FUTURE, new Action() + addFuture(StoreFuture.IMMEDIATE_FUTURE, new Action() { public void postCommit() { @@ -305,7 +306,7 @@ public class AsyncAutoCommitTransaction implements ServerTransaction return false; } - private void rollbackIfNecessary(Action postTransactionAction, MessageStore.Transaction txn) + private void rollbackIfNecessary(Action postTransactionAction, Transaction txn) { if (txn != null) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java b/java/broker/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java index 597797b5f8..e5a7df6880 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java @@ -29,6 +29,7 @@ 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 java.util.Collection; import java.util.List; @@ -67,7 +68,7 @@ public class AutoCommitTransaction implements ServerTransaction public void dequeue(BaseQueue queue, EnqueableMessage message, Action postTransactionAction) { - MessageStore.Transaction txn = null; + Transaction txn = null; try { if(message.isPersistent() && queue.isDurable()) @@ -99,7 +100,7 @@ public class AutoCommitTransaction implements ServerTransaction public void dequeue(Collection queueEntries, Action postTransactionAction) { - MessageStore.Transaction txn = null; + Transaction txn = null; try { for(QueueEntry entry : queueEntries) @@ -146,7 +147,7 @@ public class AutoCommitTransaction implements ServerTransaction public void enqueue(BaseQueue queue, EnqueableMessage message, Action postTransactionAction) { - MessageStore.Transaction txn = null; + Transaction txn = null; try { if(message.isPersistent() && queue.isDurable()) @@ -179,7 +180,7 @@ public class AutoCommitTransaction implements ServerTransaction public void enqueue(List queues, EnqueableMessage message, Action postTransactionAction, long currentTime) { - MessageStore.Transaction txn = null; + Transaction txn = null; try { @@ -247,7 +248,7 @@ public class AutoCommitTransaction implements ServerTransaction return false; } - private void rollbackIfNecessary(Action postTransactionAction, MessageStore.Transaction txn) + private void rollbackIfNecessary(Action postTransactionAction, Transaction txn) { if (txn != null) { diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/DistributedTransaction.java b/java/broker/src/main/java/org/apache/qpid/server/txn/DistributedTransaction.java new file mode 100644 index 0000000000..05d0110e9b --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/DistributedTransaction.java @@ -0,0 +1,247 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.txn; + +import org.apache.qpid.server.message.EnqueableMessage; +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.Transaction; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.transport.Xid; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class DistributedTransaction implements ServerTransaction +{ + + private final AutoCommitTransaction _autoCommitTransaction; + + private volatile Transaction _transaction; + + private long _txnStartTime = 0L; + + private DtxBranch _branch; + private AMQSessionModel _session; + private VirtualHost _vhost; + + + public DistributedTransaction(AMQSessionModel session, MessageStore store, VirtualHost vhost) + { + _session = session; + _vhost = vhost; + _autoCommitTransaction = new AutoCommitTransaction(vhost.getMessageStore()); + } + + public long getTransactionStartTime() + { + return _txnStartTime; + } + + public void addPostTransactionAction(Action postTransactionAction) + { + if(_branch != null) + { + _branch.addPostTransactionAcion(postTransactionAction); + } + else + { + _autoCommitTransaction.addPostTransactionAction(postTransactionAction); + } + } + + public void dequeue(BaseQueue queue, EnqueableMessage message, Action postTransactionAction) + { + if(_branch != null) + { + _branch.dequeue(queue, message); + _branch.addPostTransactionAcion(postTransactionAction); + } + else + { + _autoCommitTransaction.dequeue(queue, message, postTransactionAction); + } + } + + public void dequeue(Collection messages, Action postTransactionAction) + { + if(_branch != null) + { + for(QueueEntry entry : messages) + { + _branch.dequeue(entry.getQueue(), entry.getMessage()); + } + _branch.addPostTransactionAcion(postTransactionAction); + } + else + { + _autoCommitTransaction.dequeue(messages, postTransactionAction); + } + } + + public void enqueue(BaseQueue queue, EnqueableMessage message, Action postTransactionAction) + { + if(_branch != null) + { + _branch.enqueue(queue, message); + _branch.addPostTransactionAcion(postTransactionAction); + enqueue(Collections.singletonList(queue), message, postTransactionAction, System.currentTimeMillis()); + } + else + { + _autoCommitTransaction.enqueue(queue, message, postTransactionAction); + } + } + + public void enqueue(List queues, EnqueableMessage message, + Action postTransactionAction, long currentTime) + { + if(_branch != null) + { + for(BaseQueue queue : queues) + { + _branch.enqueue(queue, message); + } + _branch.addPostTransactionAcion(postTransactionAction); + } + else + { + _autoCommitTransaction.enqueue(queues, message, postTransactionAction, currentTime); + } + } + + public void commit() + { + throw new IllegalStateException("Cannot call tx.commit() on a distributed transaction"); + } + + public void commit(Runnable immediatePostTransactionAction) + { + throw new IllegalStateException("Cannot call tx.commit() on a distributed transaction"); + } + + public void rollback() + { + throw new IllegalStateException("Cannot call tx.rollback() on a distributed transaction"); + } + + public boolean isTransactional() + { + return _branch != null; + } + + public void start(Xid id, boolean join, boolean resume) + throws UnknownDtxBranchException, AlreadyKnownDtxException, JoinAndResumeDtxException + { + if(join && resume) + { + throw new JoinAndResumeDtxException(id); + } + + DtxBranch branch = _vhost.getDtxRegistry().getBranch(id); + + if(branch == null) + { + if(join || resume) + { + throw new UnknownDtxBranchException(id); + } + branch = new DtxBranch(id,_vhost.getMessageStore(), _vhost); + if(_vhost.getDtxRegistry().registerBranch(branch)) + { + _branch = branch; + branch.associateSession(_session); + } + else + { + throw new AlreadyKnownDtxException(id); + } + } + else + { + if(join) + { + branch.associateSession(_session); + } + else if(resume) + { + branch.resumeSession(_session); + } + else + { + throw new AlreadyKnownDtxException(id); + } + _branch = branch; + } + } + + public void end(Xid id, boolean fail, boolean suspend) + throws UnknownDtxBranchException, NotAssociatedDtxException, SuspendAndFailDtxException, TimeoutDtxException + { + DtxBranch branch = _vhost.getDtxRegistry().getBranch(id); + + if(suspend && fail) + { + branch.disassociateSession(_session); + _branch = null; + throw new SuspendAndFailDtxException(id); + } + + + if(branch == null) + { + throw new UnknownDtxBranchException(id); + } + else + { + if(!branch.isAssociated(_session)) + { + throw new NotAssociatedDtxException(id); + } + if(branch.expired() || branch.getState() == DtxBranch.State.TIMEDOUT) + { + branch.disassociateSession(_session); + throw new TimeoutDtxException(id); + } + + if(suspend) + { + branch.suspendSession(_session); + } + else + { + if(fail) + { + branch.setState(DtxBranch.State.ROLLBACK_ONLY); + } + branch.disassociateSession(_session); + } + + _branch = null; + + } + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/DtxBranch.java b/java/broker/src/main/java/org/apache/qpid/server/txn/DtxBranch.java new file mode 100644 index 0000000000..3ac71fc6a6 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/DtxBranch.java @@ -0,0 +1,349 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.txn; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ScheduledFuture; + +import org.apache.log4j.Logger; +import org.apache.qpid.AMQStoreException; +import org.apache.qpid.server.message.EnqueableMessage; +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.virtualhost.VirtualHost; +import org.apache.qpid.transport.Xid; + +public class DtxBranch +{ + private static final Logger _logger = Logger.getLogger(DtxBranch.class); + + private final Xid _xid; + private final List _postTransactionActions = new ArrayList(); + private State _state = State.ACTIVE; + private long _timeout; + private Map _associatedSessions = new HashMap(); + private final List _enqueueRecords = new ArrayList(); + private final List _dequeueRecords = new ArrayList(); + + private Transaction _transaction; + private long _expiration; + private VirtualHost _vhost; + private ScheduledFuture _timeoutFuture; + private MessageStore _store; + + + public enum State + { + ACTIVE, + PREPARED, + TIMEDOUT, + SUSPENDED, + FORGOTTEN, + HEUR_COM, + HEUR_RB, + ROLLBACK_ONLY + } + + + public DtxBranch(Xid xid, MessageStore store, VirtualHost vhost) + { + _xid = xid; + _store = store; + _vhost = vhost; + } + + public Xid getXid() + { + return _xid; + } + + public State getState() + { + return _state; + } + + public void setState(State state) + { + _state = state; + } + + public long getTimeout() + { + return _timeout; + } + + public void setTimeout(long timeout) + { + if(_timeoutFuture != null) + { + _timeoutFuture.cancel(false); + } + _timeout = timeout; + _expiration = timeout == 0 ? 0 : System.currentTimeMillis() + timeout; + + if(_timeout == 0) + { + _timeoutFuture = null; + } + else + { + _timeoutFuture = _vhost.scheduleTask(_timeout, new Runnable() + { + public void run() + { + setState(State.TIMEDOUT); + try + { + rollback(); + } + catch (AMQStoreException e) + { + _logger.error("Unexpected error when attempting to rollback XA transaction ("+ + _xid + ") due to timeout", e); + throw new RuntimeException(e); + } + } + }); + } + } + + public boolean expired() + { + return _timeout != 0 && _expiration < System.currentTimeMillis(); + } + + public synchronized boolean isAssociated(AMQSessionModel session) + { + return _associatedSessions.containsKey(session); + } + + public synchronized boolean hasAssociatedSessions() + { + return !_associatedSessions.isEmpty(); + } + + + public synchronized boolean hasAssociatedActiveSessions() + { + if(hasAssociatedSessions()) + { + for(State state : _associatedSessions.values()) + { + if(state != State.SUSPENDED) + { + return true; + } + } + } + return false; + } + + public synchronized void clearAssociations() + { + _associatedSessions.clear(); + } + + synchronized boolean associateSession(AMQSessionModel associatedSession) + { + return _associatedSessions.put(associatedSession, State.ACTIVE) != null; + } + + synchronized boolean disassociateSession(AMQSessionModel associatedSession) + { + return _associatedSessions.remove(associatedSession) != null; + } + + public synchronized boolean resumeSession(AMQSessionModel session) + { + if(_associatedSessions.containsKey(session) && _associatedSessions.get(session) == State.SUSPENDED) + { + _associatedSessions.put(session, State.ACTIVE); + return true; + } + return false; + } + + public synchronized boolean suspendSession(AMQSessionModel session) + { + if(_associatedSessions.containsKey(session) && _associatedSessions.get(session) == State.ACTIVE) + { + _associatedSessions.put(session, State.SUSPENDED); + return true; + } + return false; + } + + public void prepare() throws AMQStoreException + { + + Transaction txn = _store.newTransaction(); + txn.recordXid(_xid.getFormat(), + _xid.getGlobalId(), + _xid.getBranchId(), + _enqueueRecords.toArray(new Record[_enqueueRecords.size()]), + _dequeueRecords.toArray(new Record[_dequeueRecords.size()])); + txn.commitTran(); + + prePrepareTransaction(); + } + + public synchronized void rollback() throws AMQStoreException + { + if(_timeoutFuture != null) + { + _timeoutFuture.cancel(false); + _timeoutFuture = null; + } + + + if(_transaction != null) + { + // prepare has previously been called + + Transaction txn = _store.newTransaction(); + txn.removeXid(_xid.getFormat(), _xid.getGlobalId(), _xid.getBranchId()); + txn.commitTran(); + + _transaction.abortTran(); + } + + for(ServerTransaction.Action action : _postTransactionActions) + { + action.onRollback(); + } + _postTransactionActions.clear(); + } + + public void commit() throws AMQStoreException + { + if(_timeoutFuture != null) + { + _timeoutFuture.cancel(false); + _timeoutFuture = null; + } + + if(_transaction == null) + { + prePrepareTransaction(); + } + else + { + _transaction.removeXid(_xid.getFormat(), _xid.getGlobalId(), _xid.getBranchId()); + } + _transaction.commitTran(); + + for(ServerTransaction.Action action : _postTransactionActions) + { + action.postCommit(); + } + _postTransactionActions.clear(); + } + + public void prePrepareTransaction() throws AMQStoreException + { + _transaction = _store.newTransaction(); + + for(Record enqueue : _enqueueRecords) + { + if(enqueue.isDurable()) + { + _transaction.enqueueMessage(enqueue.getQueue(), enqueue.getMessage()); + } + } + + + for(Record enqueue : _dequeueRecords) + { + if(enqueue.isDurable()) + { + _transaction.dequeueMessage(enqueue.getQueue(), enqueue.getMessage()); + } + } + } + + + public void addPostTransactionAcion(ServerTransaction.Action postTransactionAction) + { + _postTransactionActions.add(postTransactionAction); + } + + + public void dequeue(BaseQueue queue, EnqueableMessage message) + { + _dequeueRecords.add(new Record(queue, message)); + } + + + public void enqueue(BaseQueue queue, EnqueableMessage message) + { + _enqueueRecords.add(new Record(queue, message)); + } + + private static final class Record implements Transaction.Record + { + private final BaseQueue _queue; + private final EnqueableMessage _message; + + public Record(BaseQueue queue, EnqueableMessage message) + { + _queue = queue; + _message = message; + } + + public BaseQueue getQueue() + { + return _queue; + } + + public EnqueableMessage getMessage() + { + return _message; + } + + public boolean isDurable() + { + return _message.isPersistent() && _queue.isDurable(); + } + } + + + public void close() + { + if(_transaction != null) + { + try + { + _state = null; + _transaction.abortTran(); + } + catch(AMQStoreException e) + { + _logger.error("Error while closing XA branch", e); + } + } + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/DtxException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/DtxException.java new file mode 100644 index 0000000000..d18d0cb68b --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/DtxException.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.txn; + +public class DtxException extends Exception +{ + public DtxException() + { + } + + public DtxException(String message) + { + super(message); + } + + public DtxException(String message, Throwable cause) + { + super(message, cause); + } + + public DtxException(Throwable cause) + { + super(cause); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/DtxNotSelectedException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/DtxNotSelectedException.java new file mode 100644 index 0000000000..c1289b1fdd --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/DtxNotSelectedException.java @@ -0,0 +1,30 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.txn; + +public class DtxNotSelectedException extends DtxException +{ + public DtxNotSelectedException() + { + super("Distribution transactions have not been selected on this session"); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/DtxRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/txn/DtxRegistry.java new file mode 100644 index 0000000000..5c54c1164f --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/DtxRegistry.java @@ -0,0 +1,333 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.apache.qpid.server.txn; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.apache.qpid.AMQStoreException; +import org.apache.qpid.server.protocol.AMQSessionModel; +import org.apache.qpid.transport.Xid; + +public class DtxRegistry +{ + private final Map _branches = new HashMap(); + + + private static final class ComparableXid + { + private final Xid _xid; + + private ComparableXid(Xid xid) + { + _xid = xid; + } + + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(o == null || getClass() != o.getClass()) + { + return false; + } + + ComparableXid that = (ComparableXid) o; + + return compareBytes(_xid.getBranchId(), that._xid.getBranchId()) + && compareBytes(_xid.getGlobalId(), that._xid.getGlobalId()); + } + + private static boolean compareBytes(byte[] a, byte[] b) + { + if(a.length != b.length) + { + return false; + } + for(int i = 0; i < a.length; i++) + { + if(a[i] != b[i]) + { + return false; + } + } + return true; + } + + + @Override + public int hashCode() + { + int result = 0; + for(int i = 0; i < _xid.getGlobalId().length; i++) + { + result = 31 * result + (int) _xid.getGlobalId()[i]; + } + for(int i = 0; i < _xid.getBranchId().length; i++) + { + result = 31 * result + (int) _xid.getBranchId()[i]; + } + + return result; + } + } + + public synchronized DtxBranch getBranch(Xid xid) + { + return _branches.get(new ComparableXid(xid)); + } + + public synchronized boolean registerBranch(DtxBranch branch) + { + ComparableXid xid = new ComparableXid(branch.getXid()); + if(!_branches.containsKey(xid)) + { + _branches.put(xid, branch); + return true; + } + return false; + } + + synchronized boolean unregisterBranch(DtxBranch branch) + { + return (_branches.remove(new ComparableXid(branch.getXid())) != null); + } + + public void commit(Xid id, boolean onePhase) + throws IncorrectDtxStateException, UnknownDtxBranchException, AMQStoreException, RollbackOnlyDtxException, TimeoutDtxException + { + DtxBranch branch = getBranch(id); + if(branch != null) + { + synchronized (branch) + { + if(!branch.hasAssociatedActiveSessions()) + { + branch.clearAssociations(); + + if(branch.expired() || branch.getState() == DtxBranch.State.TIMEDOUT) + { + unregisterBranch(branch); + throw new TimeoutDtxException(id); + } + else if(branch.getState() == DtxBranch.State.ROLLBACK_ONLY) + { + throw new RollbackOnlyDtxException(id); + } + else if(onePhase && branch.getState() == DtxBranch.State.PREPARED) + { + throw new IncorrectDtxStateException("Cannot call one-phase commit on a prepared branch", id); + } + else if(!onePhase && branch.getState() != DtxBranch.State.PREPARED) + { + throw new IncorrectDtxStateException("Cannot call two-phase commit on a non-prepared branch", + id); + } + branch.commit(); + branch.setState(DtxBranch.State.FORGOTTEN); + unregisterBranch(branch); + } + else + { + throw new IncorrectDtxStateException("Branch was still associated with a session", id); + } + } + } + else + { + throw new UnknownDtxBranchException(id); + } + } + + public synchronized void prepare(Xid id) + throws UnknownDtxBranchException, + IncorrectDtxStateException, AMQStoreException, RollbackOnlyDtxException, TimeoutDtxException + { + DtxBranch branch = getBranch(id); + if(branch != null) + { + synchronized (branch) + { + if(!branch.hasAssociatedActiveSessions()) + { + branch.clearAssociations(); + + if(branch.expired() || branch.getState() == DtxBranch.State.TIMEDOUT) + { + unregisterBranch(branch); + throw new TimeoutDtxException(id); + } + else if(branch.getState() != DtxBranch.State.ACTIVE + && branch.getState() != DtxBranch.State.ROLLBACK_ONLY) + { + throw new IncorrectDtxStateException("Cannot prepare a transaction in state " + + branch.getState(), id); + } + else + { + branch.prepare(); + branch.setState(DtxBranch.State.PREPARED); + } + } + else + { + throw new IncorrectDtxStateException("Branch still has associated sessions", id); + } + } + } + else + { + throw new UnknownDtxBranchException(id); + } + } + + public void rollback(Xid id) + throws IncorrectDtxStateException, + UnknownDtxBranchException, + AMQStoreException, TimeoutDtxException + { + + DtxBranch branch = getBranch(id); + if(branch != null) + { + synchronized (branch) + { + if(branch.expired() || branch.getState() == DtxBranch.State.TIMEDOUT) + { + unregisterBranch(branch); + throw new TimeoutDtxException(id); + } + if(!branch.hasAssociatedActiveSessions()) + { + branch.clearAssociations(); + branch.rollback(); + branch.setState(DtxBranch.State.FORGOTTEN); + unregisterBranch(branch); + } + else + { + throw new IncorrectDtxStateException("Branch was still associated with a session", id); + } + } + } + else + { + throw new UnknownDtxBranchException(id); + } + } + + + public void forget(Xid id) throws UnknownDtxBranchException, IncorrectDtxStateException + { + DtxBranch branch = getBranch(id); + if(branch != null) + { + synchronized (branch) + { + if(!branch.hasAssociatedSessions()) + { + if(branch.getState() != DtxBranch.State.HEUR_COM && branch.getState() != DtxBranch.State.HEUR_RB) + { + throw new IncorrectDtxStateException("Branch should not be forgotten - " + + "it is not heuristically complete", id); + } + branch.setState(DtxBranch.State.FORGOTTEN); + unregisterBranch(branch); + } + else + { + throw new IncorrectDtxStateException("Branch was still associated with a session", id); + } + } + } + else + { + throw new UnknownDtxBranchException(id); + } + } + + public long getTimeout(Xid id) throws UnknownDtxBranchException + { + DtxBranch branch = getBranch(id); + if(branch != null) + { + return branch.getTimeout(); + } + else + { + throw new UnknownDtxBranchException(id); + } + } + + public void setTimeout(Xid id, long timeout) throws UnknownDtxBranchException + { + DtxBranch branch = getBranch(id); + if(branch != null) + { + branch.setTimeout(timeout); + } + else + { + throw new UnknownDtxBranchException(id); + } + } + + public synchronized List recover() + { + List inDoubt = new ArrayList(); + for(DtxBranch branch : _branches.values()) + { + if(branch.getState() == DtxBranch.State.PREPARED) + { + inDoubt.add(branch.getXid()); + } + } + return inDoubt; + } + + public synchronized void endAssociations(AMQSessionModel session) + { + for(DtxBranch branch : _branches.values()) + { + if(branch.isAssociated(session)) + { + branch.setState(DtxBranch.State.ROLLBACK_ONLY); + branch.disassociateSession(session); + } + } + } + + + public synchronized void close() + { + for(DtxBranch branch : _branches.values()) + { + branch.close(); + } + _branches.clear(); + } + +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/IncorrectDtxStateException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/IncorrectDtxStateException.java new file mode 100644 index 0000000000..45f094e4b9 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/IncorrectDtxStateException.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.txn; + +import org.apache.qpid.transport.Xid; + +public class IncorrectDtxStateException extends DtxException +{ + public IncorrectDtxStateException(String message, Xid id) + { + super(message + " (xid: " + id + ")"); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/JoinAndResumeDtxException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/JoinAndResumeDtxException.java new file mode 100644 index 0000000000..a25e5a4ed6 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/JoinAndResumeDtxException.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.txn; + +import org.apache.qpid.transport.Xid; + +public class JoinAndResumeDtxException extends DtxException +{ + public JoinAndResumeDtxException(Xid id) + { + super("Cannot start a branch with both join and resume set " + id); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java b/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java index 9b61f7543f..11401ebd65 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java @@ -29,6 +29,7 @@ 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 java.util.ArrayList; import java.util.Collection; @@ -46,7 +47,7 @@ public class LocalTransaction implements ServerTransaction private final List _postTransactionActions = new ArrayList(); - private volatile MessageStore.Transaction _transaction; + private volatile Transaction _transaction; private MessageStore _transactionLog; private long _txnStartTime = 0L; diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/NotAssociatedDtxException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/NotAssociatedDtxException.java new file mode 100644 index 0000000000..de070546a7 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/NotAssociatedDtxException.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.txn; + +import org.apache.qpid.transport.Xid; + +public class NotAssociatedDtxException extends DtxException +{ + public NotAssociatedDtxException(Xid id) + { + super("Xid " + id + " not associated with the current session"); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/RollbackOnlyDtxException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/RollbackOnlyDtxException.java new file mode 100644 index 0000000000..6cf12d8631 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/RollbackOnlyDtxException.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.txn; + +import org.apache.qpid.transport.Xid; + +public class RollbackOnlyDtxException extends DtxException +{ + public RollbackOnlyDtxException(Xid id) + { + super("Transaction " + id + " may only be rolled back"); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java b/java/broker/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java index 7617544451..c568ae67aa 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java @@ -20,13 +20,12 @@ */ package org.apache.qpid.server.txn; +import java.util.Collection; +import java.util.List; import org.apache.qpid.server.message.EnqueableMessage; import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.queue.QueueEntry; -import java.util.Collection; -import java.util.List; - /** * The ServerTransaction interface allows a set enqueue/dequeue operations to be diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/SuspendAndFailDtxException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/SuspendAndFailDtxException.java new file mode 100644 index 0000000000..228844fd63 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/SuspendAndFailDtxException.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.txn; + +import org.apache.qpid.transport.Xid; + +public class SuspendAndFailDtxException extends DtxException +{ +public SuspendAndFailDtxException(Xid id) +{ + super("Cannot end a branch with both suspend and fail set " + id); +} +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/TimeoutDtxException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/TimeoutDtxException.java new file mode 100644 index 0000000000..50f7708d8a --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/TimeoutDtxException.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.txn; + +import org.apache.qpid.transport.Xid; + +public class TimeoutDtxException extends DtxException +{ + public TimeoutDtxException(Xid id) + { + super("Transaction " + id + " has timed-out and may only be rolled back"); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/UnknownDtxBranchException.java b/java/broker/src/main/java/org/apache/qpid/server/txn/UnknownDtxBranchException.java new file mode 100644 index 0000000000..c23e518365 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/txn/UnknownDtxBranchException.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.txn; + +import org.apache.qpid.transport.Xid; + +public class UnknownDtxBranchException extends DtxException +{ + public UnknownDtxBranchException(Xid id) + { + super("Unknown xid " + id); + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/util/MapJsonSerializer.java b/java/broker/src/main/java/org/apache/qpid/server/util/MapJsonSerializer.java new file mode 100644 index 0000000000..2d9ba38555 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/util/MapJsonSerializer.java @@ -0,0 +1,69 @@ +/* + * + * 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; + +import java.io.StringWriter; +import java.util.Map; + +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.type.TypeReference; + +public class MapJsonSerializer +{ + private static final TypeReference> MAP_TYPE_REFERENCE = new TypeReference>() + { + }; + + private ObjectMapper _mapper; + + public MapJsonSerializer() + { + _mapper = new ObjectMapper(); + } + + public String serialize(Map attributeMap) + { + StringWriter stringWriter = new StringWriter(); + try + { + _mapper.writeValue(stringWriter, attributeMap); + } + catch (Exception e) + { + throw new RuntimeException("Failure to serialize map:" + attributeMap, e); + } + return stringWriter.toString(); + } + + public Map deserialize(String json) + { + Map attributesMap = null; + try + { + attributesMap = _mapper.readValue(json, MAP_TYPE_REFERENCE); + } + catch (Exception e) + { + throw new RuntimeException("Failure to deserialize json:" + json, e); + } + return attributesMap; + } +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/State.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/State.java new file mode 100644 index 0000000000..fb50b3e289 --- /dev/null +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/State.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.virtualhost; + +public enum State +{ + INITIALISING, + ACTIVE, + PASSIVE, + STOPPED +} diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java index afded3416e..489b985222 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java @@ -20,6 +20,9 @@ */ package org.apache.qpid.server.virtualhost; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ScheduledFuture; import org.apache.qpid.common.Closeable; import org.apache.qpid.server.binding.BindingFactory; import org.apache.qpid.server.configuration.ConfigStore; @@ -30,16 +33,14 @@ import org.apache.qpid.server.exchange.ExchangeFactory; import org.apache.qpid.server.exchange.ExchangeRegistry; import org.apache.qpid.server.federation.BrokerLink; import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.protocol.v1_0.LinkRegistry; import org.apache.qpid.server.queue.QueueRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.security.SecurityManager; -import org.apache.qpid.server.security.auth.manager.AuthenticationManager; import org.apache.qpid.server.stats.StatisticsGatherer; import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.store.MessageStore; - -import java.util.Map; -import java.util.UUID; +import org.apache.qpid.server.txn.DtxRegistry; public interface VirtualHost extends DurableConfigurationStore.Source, VirtualHostConfig, Closeable, StatisticsGatherer { @@ -57,10 +58,6 @@ public interface VirtualHost extends DurableConfigurationStore.Source, VirtualHo MessageStore getMessageStore(); - DurableConfigurationStore getDurableConfigurationStore(); - - AuthenticationManager getAuthenticationManager(); - SecurityManager getSecurityManager(); void close(); @@ -96,5 +93,13 @@ public interface VirtualHost extends DurableConfigurationStore.Source, VirtualHo ConfigStore getConfigStore(); + DtxRegistry getDtxRegistry(); + void removeBrokerConnection(BrokerLink brokerLink); + + LinkRegistry getLinkRegistry(String remoteContainerId); + + ScheduledFuture scheduleTask(long delay, Runnable timeoutTask); + + State getState(); } diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java index 266d23af97..e956806823 100755 --- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostConfigRecoveryHandler.java @@ -20,9 +20,17 @@ */ package org.apache.qpid.server.virtualhost; -import org.apache.log4j.Logger; +import java.io.DataInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; +import java.util.UUID; +import org.apache.log4j.Logger; import org.apache.qpid.AMQException; +import org.apache.qpid.AMQStoreException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.binding.BindingFactory; @@ -32,29 +40,27 @@ import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.messages.TransactionLogMessages; import org.apache.qpid.server.logging.subjects.MessageStoreLogSubject; import org.apache.qpid.server.message.AMQMessage; +import org.apache.qpid.server.message.AbstractServerMessageImpl; import org.apache.qpid.server.message.EnqueableMessage; import org.apache.qpid.server.message.MessageTransferMessage; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; +import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.store.ConfigurationRecoveryHandler; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.MessageStoreRecoveryHandler; import org.apache.qpid.server.store.StoredMessage; +import org.apache.qpid.server.store.Transaction; import org.apache.qpid.server.store.TransactionLogRecoveryHandler; import org.apache.qpid.server.store.TransactionLogResource; +import org.apache.qpid.server.txn.DtxBranch; +import org.apache.qpid.server.txn.DtxRegistry; +import org.apache.qpid.server.txn.ServerTransaction; +import org.apache.qpid.transport.Xid; +import org.apache.qpid.transport.util.Functions; import org.apache.qpid.util.ByteBufferInputStream; -import java.io.DataInputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; -import java.util.UUID; - public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHandler, ConfigurationRecoveryHandler.QueueRecoveryHandler, ConfigurationRecoveryHandler.ExchangeRecoveryHandler, @@ -63,20 +69,19 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa MessageStoreRecoveryHandler, MessageStoreRecoveryHandler.StoredMessageRecoveryHandler, TransactionLogRecoveryHandler, - TransactionLogRecoveryHandler.QueueEntryRecoveryHandler + TransactionLogRecoveryHandler.QueueEntryRecoveryHandler, + TransactionLogRecoveryHandler.DtxRecordRecoveryHandler { private static final Logger _logger = Logger.getLogger(VirtualHostConfigRecoveryHandler.class); - private final VirtualHost _virtualHost; private MessageStoreLogSubject _logSubject; - private List _actions; private MessageStore _store; private final Map _queueRecoveries = new TreeMap(); - private Map _recoveredMessages = new HashMap(); + private Map _recoveredMessages = new HashMap(); private Map _unusedMessages = new HashMap(); @@ -88,14 +93,14 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa public VirtualHostConfigRecoveryHandler begin(MessageStore store) { - _logSubject = new MessageStoreLogSubject(_virtualHost,store); + _logSubject = new MessageStoreLogSubject(_virtualHost,store.getClass().getSimpleName()); _store = store; CurrentActor.get().message(_logSubject, TransactionLogMessages.RECOVERY_START(null, false)); return this; } - public void queue(String queueName, String owner, boolean exclusive, FieldTable arguments) + public void queue(UUID id, String queueName, String owner, boolean exclusive, FieldTable arguments) { try { @@ -103,7 +108,7 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa if (q == null) { - q = AMQQueueFactory.createAMQQueueImpl(queueName, true, owner, false, exclusive, _virtualHost, + q = AMQQueueFactory.createAMQQueueImpl(id, queueName, true, owner, false, exclusive, _virtualHost, FieldTable.convertToMap(arguments)); _virtualHost.getQueueRegistry().registerQueue(q); } @@ -125,7 +130,7 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa return this; } - public void exchange(String exchangeName, String type, boolean autoDelete) + public void exchange(UUID id, String exchangeName, String type, boolean autoDelete) { try { @@ -134,7 +139,7 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa exchange = _virtualHost.getExchangeRegistry().getExchange(exchangeNameSS); if (exchange == null) { - exchange = _virtualHost.getExchangeFactory().createExchange(exchangeNameSS, new AMQShortString(type), true, autoDelete, 0); + exchange = _virtualHost.getExchangeFactory().createExchange(id, exchangeNameSS, new AMQShortString(type), true, autoDelete, 0); _virtualHost.getExchangeRegistry().registerExchange(exchange); } } @@ -158,7 +163,7 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa public void message(StoredMessage message) { - ServerMessage serverMessage; + AbstractServerMessageImpl serverMessage; switch(message.getMetaData().getType()) { case META_DATA_0_8: @@ -193,47 +198,178 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa { } - private static final class ProcessAction + public void dtxRecord(long format, byte[] globalId, byte[] branchId, + Transaction.Record[] enqueues, + Transaction.Record[] dequeues) { - private final AMQQueue _queue; - private final AMQMessage _message; - - public ProcessAction(AMQQueue queue, AMQMessage message) + Xid id = new Xid(format, globalId, branchId); + DtxRegistry dtxRegistry = _virtualHost.getDtxRegistry(); + DtxBranch branch = dtxRegistry.getBranch(id); + if(branch == null) { - _queue = queue; - _message = message; + branch = new DtxBranch(id, _store, _virtualHost); + dtxRegistry.registerBranch(branch); } + for(Transaction.Record record : enqueues) + { + final AMQQueue queue = _virtualHost.getQueueRegistry().getQueue(record.getQueue().getId()); + if(queue != null) + { + final long messageId = record.getMessage().getMessageNumber(); + final AbstractServerMessageImpl message = _recoveredMessages.get(messageId); + _unusedMessages.remove(messageId); + + if(message != null) + { + message.incrementReference(); - public void process() + branch.enqueue(queue,message); + + branch.addPostTransactionAcion(new ServerTransaction.Action() + { + + public void postCommit() + { + try + { + + queue.enqueue(message, true, null); + message.decrementReference(); + } + catch (AMQException e) + { + _logger.error("Unable to enqueue message " + message.getMessageNumber() + " into " + + "queue " + queue.getName() + " (from XA transaction)", e); + throw new RuntimeException(e); + } + } + + public void onRollback() + { + message.decrementReference(); + } + }); + } + else + { + StringBuilder xidString = xidAsString(id); + CurrentActor.get().message(_logSubject, + TransactionLogMessages.XA_INCOMPLETE_MESSAGE(xidString.toString(), + Long.toString(messageId))); + + } + + } + else + { + StringBuilder xidString = xidAsString(id); + CurrentActor.get().message(_logSubject, + TransactionLogMessages.XA_INCOMPLETE_QUEUE(xidString.toString(), + record.getQueue().getId().toString())); + + } + } + for(Transaction.Record record : dequeues) { - try + final AMQQueue queue = _virtualHost.getQueueRegistry().getQueue(record.getQueue().getId()); + if(queue != null) { - _queue.enqueue(_message); + final long messageId = record.getMessage().getMessageNumber(); + final AbstractServerMessageImpl message = _recoveredMessages.get(messageId); + _unusedMessages.remove(messageId); + + if(message != null) + { + final QueueEntry entry = queue.getMessageOnTheQueue(messageId); + + entry.acquire(); + + branch.dequeue(queue, message); + + branch.addPostTransactionAcion(new ServerTransaction.Action() + { + + public void postCommit() + { + entry.discard(); + } + + public void onRollback() + { + entry.release(); + } + }); + } + else + { + StringBuilder xidString = xidAsString(id); + CurrentActor.get().message(_logSubject, + TransactionLogMessages.XA_INCOMPLETE_MESSAGE(xidString.toString(), + Long.toString(messageId))); + + } + } - catch(AMQException e) + else { - throw new RuntimeException(e); + StringBuilder xidString = xidAsString(id); + CurrentActor.get().message(_logSubject, + TransactionLogMessages.XA_INCOMPLETE_QUEUE(xidString.toString(), + record.getQueue().getId().toString())); } + } + try + { + branch.setState(DtxBranch.State.PREPARED); + branch.prePrepareTransaction(); + } + catch (AMQStoreException e) + { + _logger.error("Unexpected database exception when attempting to prepare a recovered XA transaction " + + xidAsString(id), e); + throw new RuntimeException(e); + } + } + + private static StringBuilder xidAsString(Xid id) + { + return new StringBuilder("(") + .append(id.getFormat()) + .append(',') + .append(Functions.str(id.getGlobalId())) + .append(',') + .append(Functions.str(id.getBranchId())) + .append(')'); } - public void binding(String exchangeName, String queueName, String bindingKey, ByteBuffer buf) + public void completeDtxRecordRecovery() + { + for(StoredMessage m : _unusedMessages.values()) + { + _logger.warn("Message id " + m.getMessageNumber() + " in store, but not in any queue - removing...."); + m.remove(); + } + CurrentActor.get().message(_logSubject, TransactionLogMessages.RECOVERY_COMPLETE(null, false)); + } + + @Override + public void binding(UUID bindingId, UUID exchangeId, UUID queueId, String bindingKey, ByteBuffer buf) { - _actions = new ArrayList(); try { - Exchange exchange = _virtualHost.getExchangeRegistry().getExchange(exchangeName); + Exchange exchange = _virtualHost.getExchangeRegistry().getExchange(exchangeId); if (exchange == null) { - _logger.error("Unknown exchange: " + exchangeName + ", cannot bind queue : " + queueName); + _logger.error("Unknown exchange id " + exchangeId + ", cannot bind queue with id " + queueId); return; } - - AMQQueue queue = _virtualHost.getQueueRegistry().getQueue(new AMQShortString(queueName)); + + AMQQueue queue = _virtualHost.getQueueRegistry().getQueue(queueId); if (queue == null) { - _logger.error("Unknown queue: " + queueName + ", cannot be bound to exchange: " + exchangeName); + _logger.error("Unknown queue id " + queueId + ", cannot be bound to exchange: " + exchange.getName()); } else { @@ -257,10 +393,10 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa if(bf.getBinding(bindingKey, queue, exchange, argumentMap) == null) { - _logger.info("Restoring binding: (Exchange: " + exchange.getNameShortString() + ", Queue: " + queueName + _logger.info("Restoring binding: (Exchange: " + exchange.getNameShortString() + ", Queue: " + queue.getName() + ", Routing Key: " + bindingKey + ", Arguments: " + argumentsFT + ")"); - bf.restoreBinding(bindingKey, queue, exchange, argumentMap); + bf.restoreBinding(bindingId, bindingKey, queue, exchange, argumentMap); } } } @@ -282,16 +418,14 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa } - public void queueEntry(final String queueName, long messageId) + public void queueEntry(final UUID queueId, long messageId) { - AMQShortString queueNameShortString = new AMQShortString(queueName); - - AMQQueue queue = _virtualHost.getQueueRegistry().getQueue(queueNameShortString); - + AMQQueue queue = _virtualHost.getQueueRegistry().getQueue(queueId); try { if(queue != null) { + String queueName = queue.getName(); ServerMessage message = _recoveredMessages.get(messageId); _unusedMessages.remove(messageId); @@ -301,7 +435,7 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa if (_logger.isDebugEnabled()) { - _logger.debug("On recovery, delivering " + message.getMessageNumber() + " to " + queue.getNameShortString()); + _logger.debug("On recovery, delivering " + message.getMessageNumber() + " to " + queueName); } Integer count = _queueRecoveries.get(queueName); @@ -316,23 +450,23 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa } else { - _logger.warn("Message id " + messageId + " referenced in log as enqueued in queue " + queue.getNameShortString() + " is unknown, entry will be discarded"); - MessageStore.Transaction txn = _store.newTransaction(); + _logger.warn("Message id " + messageId + " referenced in log as enqueued in queue " + queueName + " is unknown, entry will be discarded"); + Transaction txn = _store.newTransaction(); txn.dequeueMessage(queue, new DummyMessage(messageId)); txn.commitTranAsync(); } } else { - _logger.warn("Message id " + messageId + " in log references queue " + queueName + " which is not in the configuration, entry will be discarded"); - MessageStore.Transaction txn = _store.newTransaction(); + _logger.warn("Message id " + messageId + " in log references queue with id " + queueId + " which is not in the configuration, entry will be discarded"); + Transaction txn = _store.newTransaction(); TransactionLogResource mockQueue = new TransactionLogResource() { - - public String getResourceName() + @Override + public UUID getId() { - return queueName; + return queueId; } }; txn.dequeueMessage(mockQueue, new DummyMessage(messageId)); @@ -344,20 +478,11 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa { throw new RuntimeException(e); } - - - } - public void completeQueueEntryRecovery() + public DtxRecordRecoveryHandler completeQueueEntryRecovery() { - for(StoredMessage m : _unusedMessages.values()) - { - _logger.warn("Message id " + m.getMessageNumber() + " in store, but not in any queue - removing...."); - m.remove(); - } - for(Map.Entry entry : _queueRecoveries.entrySet()) { CurrentActor.get().message(_logSubject, TransactionLogMessages.RECOVERED(entry.getValue(), entry.getKey())); @@ -365,7 +490,9 @@ public class VirtualHostConfigRecoveryHandler implements ConfigurationRecoveryHa CurrentActor.get().message(_logSubject, TransactionLogMessages.RECOVERY_COMPLETE(entry.getKey(), true)); } - CurrentActor.get().message(_logSubject, TransactionLogMessages.RECOVERY_COMPLETE(null, false)); + + + return this; } private static class DummyMessage implements EnqueableMessage diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java index 51b60f7980..9b113525d4 100644 --- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java +++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java @@ -20,14 +20,11 @@ */ package org.apache.qpid.server.virtualhost; -import org.apache.commons.configuration.Configuration; + import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; - import org.apache.qpid.AMQException; -import org.apache.qpid.AMQStoreException; import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; import org.apache.qpid.server.AMQBrokerManagerMBean; import org.apache.qpid.server.binding.BindingFactory; import org.apache.qpid.server.configuration.BrokerConfig; @@ -44,225 +41,189 @@ import org.apache.qpid.server.exchange.DefaultExchangeRegistry; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.exchange.ExchangeFactory; import org.apache.qpid.server.exchange.ExchangeRegistry; -import org.apache.qpid.server.federation.Bridge; import org.apache.qpid.server.federation.BrokerLink; -import org.apache.qpid.server.logging.LogSubject; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.messages.VirtualHostMessages; import org.apache.qpid.server.logging.subjects.MessageStoreLogSubject; import org.apache.qpid.server.management.AMQManagedObject; import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.protocol.v1_0.LinkRegistry; 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.AMQQueueFactory; import org.apache.qpid.server.queue.DefaultQueueRegistry; import org.apache.qpid.server.queue.QueueRegistry; -import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.security.SecurityManager; -import org.apache.qpid.server.security.auth.manager.AuthenticationManager; import org.apache.qpid.server.stats.StatisticsCounter; -import org.apache.qpid.server.store.ConfigurationRecoveryHandler; -import org.apache.qpid.server.store.DurableConfigurationStore; +import org.apache.qpid.server.store.Event; +import org.apache.qpid.server.store.EventListener; import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.MessageStoreFactory; +import org.apache.qpid.server.store.OperationalLoggingListener; +import org.apache.qpid.server.txn.DtxRegistry; import org.apache.qpid.server.virtualhost.plugins.VirtualHostPlugin; import org.apache.qpid.server.virtualhost.plugins.VirtualHostPluginFactory; +import javax.management.JMException; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; -import java.util.LinkedList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; + public class VirtualHostImpl implements VirtualHost { private static final Logger _logger = Logger.getLogger(VirtualHostImpl.class); + private static final int HOUSEKEEPING_SHUTDOWN_TIMEOUT = 5; + + private final UUID _id; + private final String _name; - private ConnectionRegistry _connectionRegistry; + private final long _createTime = System.currentTimeMillis(); - private QueueRegistry _queueRegistry; + private final ConcurrentHashMap _links = new ConcurrentHashMap(); - private ExchangeRegistry _exchangeRegistry; + private final ScheduledThreadPoolExecutor _houseKeepingTasks; - private ExchangeFactory _exchangeFactory; + private final IApplicationRegistry _appRegistry; - private MessageStore _messageStore; + private final SecurityManager _securityManager; - private VirtualHostMBean _virtualHostMBean; + private final BrokerConfig _brokerConfig; - private AMQBrokerManagerMBean _brokerMBean; + private final VirtualHostConfiguration _vhostConfig; - private final AuthenticationManager _authenticationManager; + private final VirtualHostMBean _virtualHostMBean; - private SecurityManager _securityManager; + private final AMQBrokerManagerMBean _brokerMBean; - private final ScheduledThreadPoolExecutor _houseKeepingTasks; - private final IApplicationRegistry _appRegistry; - private VirtualHostConfiguration _configuration; - private DurableConfigurationStore _durableConfigurationStore; - private BindingFactory _bindingFactory; - private BrokerConfig _broker; - private UUID _id; + private final QueueRegistry _queueRegistry; - private boolean _statisticsEnabled = false; - private StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived; + private final ExchangeRegistry _exchangeRegistry; - private final long _createTime = System.currentTimeMillis(); - private final ConcurrentHashMap _links = new ConcurrentHashMap(); - private static final int HOUSEKEEPING_SHUTDOWN_TIMEOUT = 5; + private final ExchangeFactory _exchangeFactory; - public IConnectionRegistry getConnectionRegistry() - { - return _connectionRegistry; - } + private final ConnectionRegistry _connectionRegistry; - public VirtualHostConfiguration getConfiguration() - { - return _configuration; - } + private final BindingFactory _bindingFactory; - public UUID getId() - { - return _id; - } + private final DtxRegistry _dtxRegistry; - public VirtualHostConfigType getConfigType() - { - return VirtualHostConfigType.getInstance(); - } + private final MessageStore _messageStore; - public ConfiguredObject getParent() - { - return getBroker(); - } + private State _state = State.INITIALISING; - public boolean isDurable() - { - return false; - } + private boolean _statisticsEnabled = false; - /** - * Virtual host JMX MBean class. - * - * This has some of the methods implemented from management intrerface for exchanges. Any - * implementaion of an Exchange MBean should extend this class. - */ - public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtualHost - { - public VirtualHostMBean() throws NotCompliantMBeanException - { - super(ManagedVirtualHost.class, ManagedVirtualHost.TYPE); - } + private StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived; - public String getObjectInstanceName() - { - return ObjectName.quote(_name); - } + private final Map _linkRegistry = new HashMap(); - public String getName() + public VirtualHostImpl(IApplicationRegistry appRegistry, VirtualHostConfiguration hostConfig) throws Exception + { + if (hostConfig == null) { - return _name; + throw new IllegalArgumentException("HostConfig cannot be null"); } - public VirtualHostImpl getVirtualHost() + if (hostConfig.getName() == null || hostConfig.getName().length() == 0) { - return VirtualHostImpl.this; + throw new IllegalArgumentException("Illegal name (" + hostConfig.getName() + ") for virtualhost."); } - } - public VirtualHostImpl(IApplicationRegistry appRegistry, VirtualHostConfiguration hostConfig, MessageStore store) throws Exception - { - if (hostConfig == null) - { - throw new IllegalArgumentException("HostConfig cannot be null"); - } - _appRegistry = appRegistry; - _broker = _appRegistry.getBroker(); - _configuration = hostConfig; - _name = _configuration.getName(); + _brokerConfig = _appRegistry.getBroker(); + _vhostConfig = hostConfig; + _name = _vhostConfig.getName(); + _dtxRegistry = new DtxRegistry(); _id = _appRegistry.getConfigStore().createId(); CurrentActor.get().message(VirtualHostMessages.CREATED(_name)); - if (_name == null || _name.length() == 0) - { - throw new IllegalArgumentException("Illegal name (" + _name + ") for virtualhost."); - } - - _securityManager = new SecurityManager(_appRegistry.getSecurityManager()); - _securityManager.configureHostPlugins(_configuration); - _virtualHostMBean = new VirtualHostMBean(); + _securityManager = new SecurityManager(_appRegistry.getSecurityManager()); + _securityManager.configureHostPlugins(_vhostConfig); _connectionRegistry = new ConnectionRegistry(); - _houseKeepingTasks = new ScheduledThreadPoolExecutor(_configuration.getHouseKeepingThreadCount()); + _houseKeepingTasks = new ScheduledThreadPoolExecutor(_vhostConfig.getHouseKeepingThreadCount()); _queueRegistry = new DefaultQueueRegistry(this); _exchangeFactory = new DefaultExchangeFactory(this); - _exchangeFactory.initialise(_configuration); + _exchangeFactory.initialise(_vhostConfig); _exchangeRegistry = new DefaultExchangeRegistry(this); - StartupRoutingTable configFileRT = new StartupRoutingTable(); + _bindingFactory = new BindingFactory(this); - _durableConfigurationStore = configFileRT; + _brokerMBean = new AMQBrokerManagerMBean(_virtualHostMBean); - // This needs to be after the RT has been defined as it creates the default durable exchanges. - _exchangeRegistry.initialise(); + _messageStore = initialiseMessageStore(hostConfig.getMessageStoreFactoryClass()); - _bindingFactory = new BindingFactory(this); + configureMessageStore(hostConfig); - initialiseModel(_configuration); + activateNonHAMessageStore(); - if (store != null) - { - _messageStore = store; - if(store instanceof DurableConfigurationStore) - { - _durableConfigurationStore = (DurableConfigurationStore) store; - } - } - else - { - initialiseMessageStore(hostConfig); - } - - _authenticationManager = ApplicationRegistry.getInstance().getAuthenticationManager(); - - _brokerMBean = new AMQBrokerManagerMBean(_virtualHostMBean); - _brokerMBean.register(); - initialiseHouseKeeping(hostConfig.getHousekeepingCheckPeriod()); - initialiseStatistics(); } + public IConnectionRegistry getConnectionRegistry() + { + return _connectionRegistry; + } + + public VirtualHostConfiguration getConfiguration() + { + return _vhostConfig; + } + + public UUID getId() + { + return _id; + } + + public VirtualHostConfigType getConfigType() + { + return VirtualHostConfigType.getInstance(); + } + + public ConfiguredObject getParent() + { + return getBroker(); + } + + public boolean isDurable() + { + return false; + } + /** * Initialise a housekeeping task to iterate over queues cleaning expired messages with no consumers * and checking for idle or open transactions that have exceeded the permitted thresholds. * * @param period */ - private void initialiseHouseKeeping(long period) + private void initialiseHouseKeeping(long period) { + if (period != 0L) { - - scheduleHouseKeepingTask(period, new VirtualHostHouseKeepingTask()); - Map plugins = - ApplicationRegistry.getInstance().getPluginManager().getVirtualHostPlugins(); + Map plugins = _appRegistry.getPluginManager().getVirtualHostPlugins(); if (plugins != null) { @@ -292,50 +253,30 @@ public class VirtualHostImpl implements VirtualHost } } - private class VirtualHostHouseKeepingTask extends HouseKeepingTask + private void shutdownHouseKeeping() { - public VirtualHostHouseKeepingTask() - { - super(VirtualHostImpl.this); - } + _houseKeepingTasks.shutdown(); - public void execute() + try + { + if (!_houseKeepingTasks.awaitTermination(HOUSEKEEPING_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS)) + { + _houseKeepingTasks.shutdownNow(); + } + } + catch (InterruptedException e) + { + _logger.warn("Interrupted during Housekeeping shutdown:", e); + Thread.currentThread().interrupt(); + } + } + + private void removeHouseKeepingTasks() + { + BlockingQueue taskQueue = _houseKeepingTasks.getQueue(); + for (final Runnable runnable : taskQueue) { - for (AMQQueue q : _queueRegistry.getQueues()) - { - _logger.debug("Checking message status for queue: " - + q.getName()); - try - { - q.checkMessageStatus(); - } - catch (Exception e) - { - _logger.error("Exception in housekeeping for queue: " - + q.getNameShortString().toString(), e); - //Don't throw exceptions as this will stop the - // house keeping task from running. - } - } - for (AMQConnectionModel connection : getConnectionRegistry().getConnections()) - { - _logger.debug("Checking for long running open transactions on connection " + connection); - for (AMQSessionModel session : connection.getSessionModels()) - { - _logger.debug("Checking for long running open transactions on session " + session); - try - { - session.checkTransactionStatus(_configuration.getTransactionTimeoutOpenWarn(), - _configuration.getTransactionTimeoutOpenClose(), - _configuration.getTransactionTimeoutIdleWarn(), - _configuration.getTransactionTimeoutIdleClose()); - } - catch (Exception e) - { - _logger.error("Exception in housekeeping for connection: " + connection.toString(), e); - } - } - } + _houseKeepingTasks.remove(runnable); } } @@ -351,6 +292,11 @@ public class VirtualHostImpl implements VirtualHost TimeUnit.MILLISECONDS); } + public ScheduledFuture scheduleTask(long delay, Runnable task) + { + return _houseKeepingTasks.schedule(task, delay, TimeUnit.MILLISECONDS); + } + public long getHouseKeepingTaskCount() { return _houseKeepingTasks.getTaskCount(); @@ -378,65 +324,58 @@ public class VirtualHostImpl implements VirtualHost } - private void initialiseMessageStore(VirtualHostConfiguration hostConfig) throws Exception + private MessageStore initialiseMessageStore(final String messageStoreFactoryClass) throws Exception { - String messageStoreClass = hostConfig.getMessageStoreClass(); - - Class clazz = Class.forName(messageStoreClass); - Object o = clazz.newInstance(); - + final Class clazz = Class.forName(messageStoreFactoryClass); + final Object o = clazz.newInstance(); - - if (!(o instanceof MessageStore)) + if (!(o instanceof MessageStoreFactory)) { - throw new ClassCastException("Message store class must implement " + MessageStore.class + ". Class " + clazz + - " does not."); + throw new ClassCastException("Message store factory class must implement " + MessageStoreFactory.class + + ". Class " + clazz + " does not."); } - MessageStore messageStore = (MessageStore) o; - VirtualHostConfigRecoveryHandler recoveryHandler = new VirtualHostConfigRecoveryHandler(this); - MessageStoreLogSubject storeLogSubject = new MessageStoreLogSubject(this, messageStore); + final MessageStoreFactory messageStoreFactory = (MessageStoreFactory) o; + final MessageStore messageStore = messageStoreFactory.createMessageStore(); + final MessageStoreLogSubject storeLogSubject = new MessageStoreLogSubject(this, messageStoreFactory.getStoreClassName()); + OperationalLoggingListener.listen(messageStore, storeLogSubject); + messageStore.addEventListener(new BeforeActivationListener(), Event.BEFORE_ACTIVATE); + messageStore.addEventListener(new AfterActivationListener(), Event.AFTER_ACTIVATE); + messageStore.addEventListener(new BeforeCloseListener(), Event.BEFORE_CLOSE); + messageStore.addEventListener(new BeforePassivationListener(), Event.BEFORE_PASSIVATE); - if(messageStore instanceof DurableConfigurationStore) - { - DurableConfigurationStore durableConfigurationStore = (DurableConfigurationStore) messageStore; - - durableConfigurationStore.configureConfigStore(this.getName(), - recoveryHandler, - hostConfig.getStoreConfiguration(), - storeLogSubject); + return messageStore; + } - _durableConfigurationStore = durableConfigurationStore; - } + private void configureMessageStore(VirtualHostConfiguration hostConfig) throws Exception + { - messageStore.configureMessageStore(this.getName(), - recoveryHandler, - hostConfig.getStoreConfiguration(), - storeLogSubject); - messageStore.configureTransactionLog(this.getName(), - recoveryHandler, - hostConfig.getStoreConfiguration(), - storeLogSubject); + VirtualHostConfigRecoveryHandler recoveryHandler = new VirtualHostConfigRecoveryHandler(this); - _messageStore = messageStore; + // TODO perhaps pass config on construction?? + _messageStore.configureConfigStore(getName(), recoveryHandler, hostConfig.getStoreConfiguration()); + _messageStore.configureMessageStore(getName(), recoveryHandler, recoveryHandler, hostConfig.getStoreConfiguration()); + } + private void activateNonHAMessageStore() throws Exception + { + _messageStore.activate(); } private void initialiseModel(VirtualHostConfiguration config) throws ConfigurationException, AMQException { _logger.debug("Loading configuration for virtualhost: " + config.getName()); - List exchangeNames = config.getExchanges(); + List exchangeNames = config.getExchanges(); - for (Object exchangeNameObj : exchangeNames) + for (String exchangeName : exchangeNames) { - String exchangeName = String.valueOf(exchangeNameObj); configureExchange(config.getExchangeConfiguration(exchangeName)); } - String[] queueNames = config.getQueueNames(); + String[] queueNames = config.getQueueNames(); for (Object queueNameObj : queueNames) { @@ -447,45 +386,45 @@ public class VirtualHostImpl implements VirtualHost private void configureExchange(ExchangeConfiguration exchangeConfiguration) throws AMQException { - AMQShortString exchangeName = new AMQShortString(exchangeConfiguration.getName()); + AMQShortString exchangeName = new AMQShortString(exchangeConfiguration.getName()); Exchange exchange; exchange = _exchangeRegistry.getExchange(exchangeName); if (exchange == null) { - AMQShortString type = new AMQShortString(exchangeConfiguration.getType()); - boolean durable = exchangeConfiguration.getDurable(); - boolean autodelete = exchangeConfiguration.getAutoDelete(); + AMQShortString type = new AMQShortString(exchangeConfiguration.getType()); + boolean durable = exchangeConfiguration.getDurable(); + boolean autodelete = exchangeConfiguration.getAutoDelete(); Exchange newExchange = _exchangeFactory.createExchange(exchangeName, type, durable, autodelete, 0); _exchangeRegistry.registerExchange(newExchange); if (newExchange.isDurable()) { - _durableConfigurationStore.createExchange(newExchange); + _messageStore.createExchange(newExchange); } } } private void configureQueue(QueueConfiguration queueConfiguration) throws AMQException, ConfigurationException { - AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueConfiguration, this); + AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueConfiguration, this); String queueName = queue.getName(); - if (queue.isDurable()) - { - getDurableConfigurationStore().createQueue(queue); - } + if (queue.isDurable()) + { + getMessageStore().createQueue(queue); + } //get the exchange name (returns default exchange name if none was specified) - String exchangeName = queueConfiguration.getExchange(); + String exchangeName = queueConfiguration.getExchange(); Exchange exchange = _exchangeRegistry.getExchange(exchangeName); - if (exchange == null) - { + if (exchange == null) + { throw new ConfigurationException("Attempt to bind queue '" + queueName + "' to unknown exchange:" + exchangeName); - } + } Exchange defaultExchange = _exchangeRegistry.getDefaultExchange(); @@ -531,17 +470,12 @@ public class VirtualHostImpl implements VirtualHost public BrokerConfig getBroker() { - return _broker; + return _brokerConfig; } public String getFederationTag() { - return _broker.getFederationTag(); - } - - public void setBroker(final BrokerConfig broker) - { - _broker = broker; + return _brokerConfig.getFederationTag(); } public long getCreateTime() @@ -569,16 +503,6 @@ public class VirtualHostImpl implements VirtualHost return _messageStore; } - public DurableConfigurationStore getDurableConfigurationStore() - { - return _durableConfigurationStore; - } - - public AuthenticationManager getAuthenticationManager() - { - return _authenticationManager; - } - public SecurityManager getSecurityManager() { return _securityManager; @@ -588,34 +512,8 @@ public class VirtualHostImpl implements VirtualHost { //Stop Connections _connectionRegistry.close(); - - //Stop the Queues processing - if (_queueRegistry != null) - { - for (AMQQueue queue : _queueRegistry.getQueues()) - { - queue.stop(); - } - } - - //Stop Housekeeping - if (_houseKeepingTasks != null) - { - _houseKeepingTasks.shutdown(); - - try - { - if (!_houseKeepingTasks.awaitTermination(HOUSEKEEPING_SHUTDOWN_TIMEOUT, TimeUnit.SECONDS)) - { - _houseKeepingTasks.shutdownNow(); - } - } - catch (InterruptedException e) - { - _logger.warn("Interrupted during Housekeeping shutdown:" + e.getMessage()); - // Swallowing InterruptedException ok as we are shutting down. - } - } + _queueRegistry.stopAllAndUnregisterMBeans(); + _dtxRegistry.close(); //Close MessageStore if (_messageStore != null) @@ -627,10 +525,12 @@ public class VirtualHostImpl implements VirtualHost } catch (Exception e) { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. + _logger.error("Failed to close message store", e); } } + _state = State.STOPPED; + CurrentActor.get().message(VirtualHostMessages.CLOSED()); } @@ -742,7 +642,6 @@ public class VirtualHostImpl implements VirtualHost return blink; } - public void createBrokerConnection(final String transport, final String host, final int port, @@ -779,149 +678,182 @@ public class VirtualHostImpl implements VirtualHost } } + public synchronized LinkRegistry getLinkRegistry(String remoteContainerId) + { + LinkRegistry linkRegistry = _linkRegistry.get(remoteContainerId); + if(linkRegistry == null) + { + linkRegistry = new LinkRegistry(); + _linkRegistry.put(remoteContainerId, linkRegistry); + } + return linkRegistry; + } + public ConfigStore getConfigStore() { return getApplicationRegistry().getConfigStore(); } + public DtxRegistry getDtxRegistry() + { + return _dtxRegistry; + } + + @Override + public String toString() + { + return _name; + } + + @Override + public State getState() + { + return _state; + } + + /** - * Temporary Startup RT class to record the creation of persistent queues / exchanges. - * + * Virtual host JMX MBean class. * - * This is so we can replay the creation of queues/exchanges in to the real _RT after it has been loaded. - * This should be removed after the _RT has been fully split from the the TL + * This has some of the methods implemented from management interface for exchanges. Any + * Implementation of an Exchange MBean should extend this class. */ - private static class StartupRoutingTable implements DurableConfigurationStore + public class VirtualHostMBean extends AMQManagedObject implements ManagedVirtualHost { - private List exchange = new LinkedList(); - private List queue = new LinkedList(); - private List bindings = new LinkedList(); - private List links = new LinkedList(); - private List bridges = new LinkedList(); - - public void configure(VirtualHost virtualHost, String base, VirtualHostConfiguration config) throws Exception + public VirtualHostMBean() throws NotCompliantMBeanException { + super(ManagedVirtualHost.class, ManagedVirtualHost.TYPE); } - public void close() throws Exception + public String getObjectInstanceName() { + return ObjectName.quote(_name); } - public void removeMessage(Long messageId) throws AMQException + public String getName() { - //To change body of implemented methods use File | Settings | File Templates. + return _name; } - public void configureConfigStore(String name, - ConfigurationRecoveryHandler recoveryHandler, - Configuration config, - LogSubject logSubject) throws Exception + public VirtualHostImpl getVirtualHost() { - //To change body of implemented methods use File | Settings | File Templates. + return VirtualHostImpl.this; } + } - public void createExchange(Exchange exchange) throws AMQStoreException + private final class BeforeActivationListener implements EventListener + { + @Override + public void event(Event event) { - if (exchange.isDurable()) + try { - this.exchange.add(exchange); - } - } - - public void removeExchange(Exchange exchange) throws AMQStoreException - { - } - - public void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException - { - if (exchange.isDurable() && queue.isDurable()) + _exchangeRegistry.initialise(); + initialiseModel(_vhostConfig); + } catch (Exception e) { - bindings.add(new CreateBindingTuple(exchange, routingKey, queue, args)); + throw new RuntimeException("Failed to initialise virtual host after state change", e); } } + } - public void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException - { - } - - public void createQueue(AMQQueue queue) throws AMQStoreException - { - createQueue(queue, null); - } - - public void createQueue(AMQQueue queue, FieldTable arguments) throws AMQStoreException + private final class AfterActivationListener implements EventListener + { + @Override + public void event(Event event) { - if (queue.isDurable()) + initialiseHouseKeeping(_vhostConfig.getHousekeepingCheckPeriod()); + try { - this.queue.add(new CreateQueueTuple(queue, arguments)); + _brokerMBean.register(); + } catch (JMException e) + { + throw new RuntimeException("Failed to register virtual host mbean for virtual host " + getName(), e); } - } - public void removeQueue(AMQQueue queue) throws AMQStoreException - { + _state = State.ACTIVE; } + } + public class BeforePassivationListener implements EventListener + { - private static class CreateQueueTuple + @Override + public void event(Event event) { - private AMQQueue queue; - private FieldTable arguments; + _connectionRegistry.close(IConnectionRegistry.VHOST_PASSIVATE_REPLY_TEXT); + _brokerMBean.unregister(); + removeHouseKeepingTasks(); - public CreateQueueTuple(AMQQueue queue, FieldTable arguments) - { - this.queue = queue; - this.arguments = arguments; - } + _queueRegistry.stopAllAndUnregisterMBeans(); + _exchangeRegistry.clearAndUnregisterMbeans(); + _dtxRegistry.close(); + + _state = State.PASSIVE; } + } - private static class CreateBindingTuple + private final class BeforeCloseListener implements EventListener + { + @Override + public void event(Event event) { - private AMQQueue queue; - private FieldTable arguments; - private Exchange exchange; - private AMQShortString routingKey; - - public CreateBindingTuple(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) - { - this.exchange = exchange; - this.routingKey = routingKey; - this.queue = queue; - arguments = args; - } + _brokerMBean.unregister(); + shutdownHouseKeeping(); } + } - public void updateQueue(AMQQueue queue) throws AMQStoreException + private class VirtualHostHouseKeepingTask extends HouseKeepingTask + { + public VirtualHostHouseKeepingTask() { + super(VirtualHostImpl.this); } - public void createBrokerLink(final BrokerLink link) throws AMQStoreException + public void execute() { - if(link.isDurable()) + for (AMQQueue q : _queueRegistry.getQueues()) { - links.add(link); + if (_logger.isDebugEnabled()) + { + _logger.debug("Checking message status for queue: " + + q.getName()); + } + try + { + q.checkMessageStatus(); + } catch (Exception e) + { + _logger.error("Exception in housekeeping for queue: " + + q.getNameShortString().toString(), e); + //Don't throw exceptions as this will stop the + // house keeping task from running. + } } - } - - public void deleteBrokerLink(final BrokerLink link) throws AMQStoreException - { - } - - public void createBridge(final Bridge bridge) throws AMQStoreException - { - if(bridge.isDurable()) + for (AMQConnectionModel connection : getConnectionRegistry().getConnections()) { - bridges.add(bridge); + if (_logger.isDebugEnabled()) + { + _logger.debug("Checking for long running open transactions on connection " + connection); + } + for (AMQSessionModel session : connection.getSessionModels()) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Checking for long running open transactions on session " + session); + } + try + { + session.checkTransactionStatus(_vhostConfig.getTransactionTimeoutOpenWarn(), + _vhostConfig.getTransactionTimeoutOpenClose(), + _vhostConfig.getTransactionTimeoutIdleWarn(), + _vhostConfig.getTransactionTimeoutIdleClose()); + } catch (Exception e) + { + _logger.error("Exception in housekeeping for connection: " + connection.toString(), e); + } + } } } - - public void deleteBridge(final Bridge bridge) throws AMQStoreException - { - } - } - - @Override - public String toString() - { - return _name; } } diff --git a/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java b/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java index d34d1bbef3..5c500771c2 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/AMQBrokerManagerMBeanTest.java @@ -37,6 +37,7 @@ import org.apache.qpid.server.queue.QueueRegistry; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.qpid.server.store.TestableMemoryMessageStoreFactory; import org.apache.qpid.server.util.TestApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostImpl; @@ -165,7 +166,7 @@ public class AMQBrokerManagerMBeanTest extends QpidTestCase XMLConfiguration configXml = new XMLConfiguration(); configXml.addProperty("virtualhosts.virtualhost(-1).name", "test"); - configXml.addProperty("virtualhosts.virtualhost(-1).test.store.class", TestableMemoryMessageStore.class.getName()); + configXml.addProperty("virtualhosts.virtualhost(-1).test.store.factoryclass", TestableMemoryMessageStoreFactory.class.getName()); ServerConfiguration configuration = new ServerConfiguration(configXml); diff --git a/java/broker/src/test/java/org/apache/qpid/server/AMQChannelTest.java b/java/broker/src/test/java/org/apache/qpid/server/AMQChannelTest.java new file mode 100644 index 0000000000..fc6cbcb248 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/AMQChannelTest.java @@ -0,0 +1,52 @@ +/* + * + * 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; + +import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.protocol.InternalTestProtocolSession; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.util.InternalBrokerBaseCase; +import org.apache.qpid.server.virtualhost.VirtualHost; + +public class AMQChannelTest extends InternalBrokerBaseCase +{ + private VirtualHost _virtualHost; + private AMQProtocolSession _protocolSession; + + @Override + public void setUp() throws Exception + { + super.setUp(); + _virtualHost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHosts().iterator().next(); + _protocolSession = new InternalTestProtocolSession(_virtualHost); + } + + public void testCompareTo() throws Exception + { + AMQChannel channel1 = new AMQChannel(_protocolSession, 1, _virtualHost.getMessageStore()); + + // create a channel with the same channelId but on a different session + AMQChannel channel2 = new AMQChannel(new InternalTestProtocolSession(_virtualHost), 1, _virtualHost.getMessageStore()); + assertFalse("Unexpected compare result", channel1.compareTo(channel2) == 0); + assertEquals("Unexpected compare result", 0, channel1.compareTo(channel1)); + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/configuration/MockConnectionConfig.java b/java/broker/src/test/java/org/apache/qpid/server/configuration/MockConnectionConfig.java index 73e0bc5d27..c0777d2f8f 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/configuration/MockConnectionConfig.java +++ b/java/broker/src/test/java/org/apache/qpid/server/configuration/MockConnectionConfig.java @@ -1,3 +1,23 @@ +/* + * + * 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.configuration; import java.util.UUID; diff --git a/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java b/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java index 50e7f0588b..c4c93acfb6 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/configuration/VirtualHostConfigurationTest.java @@ -27,6 +27,7 @@ import org.apache.qpid.server.queue.AMQPriorityQueue; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.qpid.server.store.TestableMemoryMessageStoreFactory; import org.apache.qpid.server.util.InternalBrokerBaseCase; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -161,7 +162,7 @@ public class VirtualHostConfigurationTest extends InternalBrokerBaseCase getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + "Extra.queues(-1).queue(-1).name", "r2d2"); getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + "Extra.queues.queue.r2d2.deadLetterQueues", "true"); getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + "Extra.queues(-1).queue(-1).name", "c3p0"); - getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + "Extra.store.class", TestableMemoryMessageStore.class.getName()); + getConfigXml().addProperty("virtualhosts.virtualhost." + getName() + "Extra.store.factoryclass", TestableMemoryMessageStoreFactory.class.getName()); // Start the broker now. super.createBroker(); diff --git a/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java b/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java index 27a0462e09..afd8fd9ed2 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java +++ b/java/broker/src/test/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java @@ -20,8 +20,19 @@ */ package org.apache.qpid.server.exchange; -import org.apache.log4j.Logger; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicLong; +import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; @@ -31,11 +42,11 @@ import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.FieldTableFactory; import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.server.binding.Binding; -import org.apache.qpid.server.binding.BindingFactory; import org.apache.qpid.server.message.AMQMessage; import org.apache.qpid.server.message.AMQMessageHeader; import org.apache.qpid.server.message.MessageMetaData; import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.BaseQueue; @@ -44,23 +55,10 @@ import org.apache.qpid.server.queue.MockStoredMessage; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.queue.SimpleAMQQueue; import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.store.DurableConfigurationStore; -import org.apache.qpid.server.store.MemoryMessageStore; import org.apache.qpid.server.store.StoredMessage; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.util.InternalBrokerBaseCase; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicLong; - public class AbstractHeadersExchangeTestBase extends InternalBrokerBaseCase { private static final Logger _log = Logger.getLogger(AbstractHeadersExchangeTestBase.class); @@ -68,24 +66,6 @@ public class AbstractHeadersExchangeTestBase extends InternalBrokerBaseCase private final HeadersExchange exchange = new HeadersExchange(); protected final Set queues = new HashSet(); - - - /** - * Not used in this test, just there to stub out the routing calls - */ - private MemoryMessageStore _store = new MemoryMessageStore(); - - - private BindingFactory bindingFactory = new BindingFactory(new DurableConfigurationStore.Source() - { - - public DurableConfigurationStore getDurableConfigurationStore() - { - return _store; - } - }, - exchange); - private int count; public void testDoNothing() @@ -103,7 +83,6 @@ public class AbstractHeadersExchangeTestBase extends InternalBrokerBaseCase protected void unbind(TestQueue queue, String... bindings) throws AMQException { String queueName = queue.getName(); - //TODO - check this exchange.onUnbind(new Binding(null,queueName, queue, exchange, getHeadersMap(bindings))); } @@ -123,7 +102,7 @@ public class AbstractHeadersExchangeTestBase extends InternalBrokerBaseCase protected int route(Message m) throws AMQException { - m.getIncomingMessage().headersReceived(); + m.getIncomingMessage().headersReceived(System.currentTimeMillis()); m.route(exchange); if(m.getIncomingMessage().allContentReceived()) { @@ -297,7 +276,7 @@ public class AbstractHeadersExchangeTestBase extends InternalBrokerBaseCase public TestQueue(AMQShortString name) throws AMQException { - super(name, false, new AMQShortString("test"), true, false,ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"), Collections.EMPTY_MAP); + super(UUIDGenerator.generateUUID(), name, false, new AMQShortString("test"), true, false,ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"), Collections.EMPTY_MAP); ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test").getQueueRegistry().registerQueue(this); } @@ -538,12 +517,6 @@ public class AbstractHeadersExchangeTestBase extends InternalBrokerBaseCase } - public AMQMessage getUnderlyingMessage() - { - return Message.this; - } - - public ContentHeaderBody getContentHeader() { try diff --git a/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java b/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java index 1fac4afe29..9034bf9c3a 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java @@ -26,6 +26,7 @@ import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.management.common.mbeans.ManagedExchange; import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.queue.QueueRegistry; @@ -52,7 +53,7 @@ public class ExchangeMBeanTest extends InternalBrokerBaseCase public void testGeneralProperties() throws Exception { DirectExchange exchange = new DirectExchange(); - exchange.initialise(_virtualHost, ExchangeDefaults.DIRECT_EXCHANGE_NAME, false, 0, true); + exchange.initialise(UUIDGenerator.generateUUID(), _virtualHost, ExchangeDefaults.DIRECT_EXCHANGE_NAME, false, 0, true); ManagedObject managedObj = exchange.getManagedObject(); ManagedExchange mbean = (ManagedExchange)managedObj; @@ -67,7 +68,7 @@ public class ExchangeMBeanTest extends InternalBrokerBaseCase public void testDirectExchangeMBean() throws Exception { DirectExchange exchange = new DirectExchange(); - exchange.initialise(_virtualHost, ExchangeDefaults.DIRECT_EXCHANGE_NAME, false, 0, true); + exchange.initialise(UUIDGenerator.generateUUID(), _virtualHost, ExchangeDefaults.DIRECT_EXCHANGE_NAME, false, 0, true); ManagedObject managedObj = exchange.getManagedObject(); ManagedExchange mbean = (ManagedExchange)managedObj; @@ -82,7 +83,7 @@ public class ExchangeMBeanTest extends InternalBrokerBaseCase public void testTopicExchangeMBean() throws Exception { TopicExchange exchange = new TopicExchange(); - exchange.initialise(_virtualHost,ExchangeDefaults.TOPIC_EXCHANGE_NAME, false, 0, true); + exchange.initialise(UUIDGenerator.generateUUID(), _virtualHost,ExchangeDefaults.TOPIC_EXCHANGE_NAME, false, 0, true); ManagedObject managedObj = exchange.getManagedObject(); ManagedExchange mbean = (ManagedExchange)managedObj; @@ -97,7 +98,7 @@ public class ExchangeMBeanTest extends InternalBrokerBaseCase public void testHeadersExchangeMBean() throws Exception { HeadersExchange exchange = new HeadersExchange(); - exchange.initialise(_virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true); + exchange.initialise(UUIDGenerator.generateUUID(), _virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true); ManagedObject managedObj = exchange.getManagedObject(); ManagedExchange mbean = (ManagedExchange)managedObj; @@ -119,7 +120,7 @@ public class ExchangeMBeanTest extends InternalBrokerBaseCase public void testHeadersExchangeMBeanMatchPropertyNoValue() throws Exception { HeadersExchange exchange = new HeadersExchange(); - exchange.initialise(_virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true); + exchange.initialise(UUIDGenerator.generateUUID(), _virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true); ManagedObject managedObj = exchange.getManagedObject(); ManagedExchange mbean = (ManagedExchange)managedObj; @@ -137,7 +138,7 @@ public class ExchangeMBeanTest extends InternalBrokerBaseCase public void testInvalidHeaderBindingMalformed() throws Exception { HeadersExchange exchange = new HeadersExchange(); - exchange.initialise(_virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true); + exchange.initialise(UUIDGenerator.generateUUID(), _virtualHost,ExchangeDefaults.HEADERS_EXCHANGE_NAME, false, 0, true); ManagedObject managedObj = exchange.getManagedObject(); ManagedExchange mbean = (ManagedExchange)managedObj; diff --git a/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java b/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java index dfa31e131f..00c8a18d9f 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/exchange/TopicExchangeTest.java @@ -350,7 +350,7 @@ public class TopicExchangeTest extends InternalBrokerBaseCase private int routeMessage(final IncomingMessage message) throws AMQException { - MessageMetaData mmd = message.headersReceived(); + MessageMetaData mmd = message.headersReceived(System.currentTimeMillis()); message.setStoredMessage(_store.addMessage(mmd)); message.enqueue(_exchange.route(message)); diff --git a/java/broker/src/test/java/org/apache/qpid/server/logging/messages/MessageStoreMessagesTest.java b/java/broker/src/test/java/org/apache/qpid/server/logging/messages/MessageStoreMessagesTest.java index cc032a0430..3377573b9d 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/logging/messages/MessageStoreMessagesTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/logging/messages/MessageStoreMessagesTest.java @@ -29,12 +29,10 @@ public class MessageStoreMessagesTest extends AbstractTestMessages { public void testMessageStoreCreated() { - String name = "DerbyMessageStore"; - - _logMessage = MessageStoreMessages.CREATED(name); + _logMessage = MessageStoreMessages.CREATED(); List log = performLog(); - String[] expected = {"Created :", name}; + String[] expected = {"Created"}; validateLogMessage(log, "MST-1001", expected); } @@ -70,56 +68,4 @@ public class MessageStoreMessagesTest extends AbstractTestMessages validateLogMessage(log, "MST-1004", expected); } -/* - public void testMessageStoreRecoveryStart_withQueue() - { - String queueName = "testQueue"; - - _logMessage = MessageStoreMessages.RECOVERY_START(queueName, true); - List log = performLog(); - - String[] expected = {"Recovery Start :", queueName}; - - validateLogMessage(log, "MST-1004", expected); - } - - public void testMessageStoreRecovered() - { - String queueName = "testQueue"; - Integer messasgeCount = 2000; - - _logMessage = MessageStoreMessages.MST_RECOVERED(messasgeCount, queueName); - List log = performLog(); - - // Here we use MessageFormat to ensure the messasgeCount of 2000 is - // reformated for display as '2,000' - String[] expected = {"Recovered ", - MessageFormat.format("{0,number}", messasgeCount), - "messages for queue", queueName}; - - validateLogMessage(log, "MST-1005", expected); - } - - public void testMessageStoreRecoveryComplete() - { - _logMessage = MessageStoreMessages.MST_RECOVERY_COMPLETE(null,false); - List log = performLog(); - - String[] expected = {"Recovery Complete"}; - - validateLogMessage(log, "MST-1006", expected); - } - - public void testMessageStoreRecoveryComplete_withQueue() - { - String queueName = "testQueue"; - - _logMessage = MessageStoreMessages.MST_RECOVERY_COMPLETE(queueName, true); - List log = performLog(); - - String[] expected = {"Recovery Complete :", queueName}; - - validateLogMessage(log, "MST-1006", expected); - } - */ } diff --git a/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/MessageStoreLogSubjectTest.java b/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/MessageStoreLogSubjectTest.java index 158fb667a9..c62b24c3b9 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/MessageStoreLogSubjectTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/logging/subjects/MessageStoreLogSubjectTest.java @@ -37,13 +37,13 @@ public class MessageStoreLogSubjectTest extends AbstractTestLogSubject _testVhost = ApplicationRegistry.getInstance().getVirtualHostRegistry(). getVirtualHost("test"); - _subject = new MessageStoreLogSubject(_testVhost, _testVhost.getMessageStore()); + _subject = new MessageStoreLogSubject(_testVhost, _testVhost.getMessageStore().getClass().getSimpleName()); } /** * Validate that the logged Subject message is as expected: * MESSAGE [Blank][vh(/test)/ms(MemoryMessageStore)] - * @param message the message whos format needs validation + * @param message the message who's format needs validation */ @Override protected void validateLogStatement(String message) diff --git a/java/broker/src/test/java/org/apache/qpid/server/plugins/OsgiSystemPackageUtilTest.java b/java/broker/src/test/java/org/apache/qpid/server/plugins/OsgiSystemPackageUtilTest.java index d64d1212b0..267545c656 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/plugins/OsgiSystemPackageUtilTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/plugins/OsgiSystemPackageUtilTest.java @@ -73,11 +73,11 @@ public class OsgiSystemPackageUtilTest extends QpidTestCase _map.put("org.apache.qpid.xyz", "1.0.0"); _map.put("org.abc", "1.2.3"); - _util = new OsgiSystemPackageUtil(new Version("0.15"), _map); + _util = new OsgiSystemPackageUtil(new Version("0.17"), _map); final String systemPackageString = _util.getFormattedSystemPackageString(); - assertEquals("org.abc; version=1.2.3, org.apache.qpid.xyz; version=0.15.0", systemPackageString); + assertEquals("org.abc; version=1.2.3, org.apache.qpid.xyz; version=0.17.0", systemPackageString); } public void testWithQpidPackageWithoutQpidReleaseNumberSet() throws Exception diff --git a/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java b/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java index 71d5211470..fe9bcc57a6 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java @@ -30,7 +30,7 @@ import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.store.MessageStore; -import org.apache.qpid.server.store.SkeletonMessageStore; +import org.apache.qpid.server.store.TestableMemoryMessageStore; import org.apache.qpid.server.util.InternalBrokerBaseCase; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -45,7 +45,7 @@ public class AMQProtocolSessionMBeanTest extends InternalBrokerBaseCase /** Used for debugging. */ private static final Logger log = Logger.getLogger(AMQProtocolSessionMBeanTest.class); - private MessageStore _messageStore = new SkeletonMessageStore(); + private MessageStore _messageStore = new TestableMemoryMessageStore(); private AMQProtocolEngine _protocolSession; private AMQChannel _channel; private AMQProtocolSessionMBean _mbean; diff --git a/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java b/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java index db37cc0965..96c67941f9 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java +++ b/java/broker/src/test/java/org/apache/qpid/server/protocol/InternalTestProtocolSession.java @@ -34,6 +34,7 @@ import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal; import org.apache.qpid.server.subscription.ClientDeliveryMethod; import org.apache.qpid.server.subscription.Subscription; +import org.apache.qpid.server.subscription.SubscriptionImpl; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.transport.TestNetworkConnection; @@ -239,12 +240,12 @@ public class InternalTestProtocolSession extends AMQProtocolEngine implements Pr _channelDelivers.put(_channelId, consumers); } - LinkedList consumerDelivers = consumers.get(sub.getConsumerTag()); + LinkedList consumerDelivers = consumers.get(((SubscriptionImpl)sub).getConsumerTag()); if (consumerDelivers == null) { consumerDelivers = new LinkedList(); - consumers.put(sub.getConsumerTag(), consumerDelivers); + consumers.put(((SubscriptionImpl)sub).getConsumerTag(), consumerDelivers); } consumerDelivers.add(new DeliveryPair(deliveryTag, (AMQMessage)entry.getMessage())); diff --git a/java/broker/src/test/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngineFactoryTest.java b/java/broker/src/test/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngineFactoryTest.java index d8b5cd02cf..6081be8efd 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngineFactoryTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/protocol/MultiVersionProtocolEngineFactoryTest.java @@ -35,6 +35,7 @@ import java.util.Set; public class MultiVersionProtocolEngineFactoryTest extends QpidTestCase { + protected void setUp() throws Exception { super.setUp(); @@ -93,7 +94,20 @@ public class MultiVersionProtocolEngineFactoryTest extends QpidTestCase (byte) 0, (byte) 10 }; - + + + private static final byte[] AMQP_1_0_0_HEADER = + new byte[] { + (byte)'A', + (byte)'M', + (byte)'Q', + (byte)'P', + (byte) 0, + (byte) 1, + (byte) 0, + (byte) 0 + }; + private byte[] getAmqpHeader(final AmqpProtocolVersion version) { switch(version) @@ -106,6 +120,8 @@ public class MultiVersionProtocolEngineFactoryTest extends QpidTestCase return AMQP_0_9_1_HEADER; case v0_10: return AMQP_0_10_HEADER; + case v1_0_0: + return AMQP_1_0_0_HEADER; default: fail("unknown AMQP version, appropriate header must be added for new protocol version"); return null; diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java index 91e35f07de..25d35aab16 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java @@ -35,6 +35,8 @@ import org.apache.qpid.server.subscription.SubscriptionFactoryImpl; import org.apache.qpid.server.util.InternalBrokerBaseCase; import javax.management.Notification; + +import java.nio.ByteBuffer; import java.util.ArrayList; /** This class tests all the alerts an AMQQueue can throw based on threshold values of different parameters */ @@ -300,7 +302,7 @@ public class AMQQueueAlertTest extends InternalBrokerBaseCase messages[i] = message(false, size); ArrayList qs = new ArrayList(); qs.add(getQueue()); - metaData[i] = messages[i].headersReceived(); + metaData[i] = messages[i].headersReceived(System.currentTimeMillis()); messages[i].setStoredMessage(getMessageStore().addMessage(metaData[i])); messages[i].enqueue(qs); @@ -309,30 +311,29 @@ public class AMQQueueAlertTest extends InternalBrokerBaseCase for (int i = 0; i < messageCount; i++) { - messages[i].addContentBodyFrame( - new ContentChunk() - { - - private byte[] _data = new byte[(int)size]; + ContentChunk contentChunk = new ContentChunk() + { + private byte[] _data = new byte[(int)size]; - public int getSize() - { - return (int) size; - } + public int getSize() + { + return (int) size; + } - public byte[] getData() - { - return _data; - } + public byte[] getData() + { + return _data; + } - public void reduceToFit() - { + public void reduceToFit() + { + } + }; - } - }); + messages[i].addContentBodyFrame(contentChunk); + messages[i].getStoredMessage().addContent(0, ByteBuffer.wrap(contentChunk.getData())); getQueue().enqueue(new AMQMessage(messages[i].getStoredMessage())); - } } diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java index 337ff194c3..e123a968a4 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueFactoryTest.java @@ -20,6 +20,8 @@ */ package org.apache.qpid.server.queue; +import java.util.UUID; + import org.apache.commons.configuration.XMLConfiguration; import org.apache.qpid.AMQException; @@ -35,6 +37,7 @@ import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.actors.TestLogActor; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.qpid.server.store.TestableMemoryMessageStoreFactory; import org.apache.qpid.server.util.TestApplicationRegistry; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.test.utils.QpidTestCase; @@ -53,7 +56,7 @@ public class AMQQueueFactoryTest extends QpidTestCase XMLConfiguration configXml = new XMLConfiguration(); configXml.addProperty("virtualhosts.virtualhost(-1).name", getName()); - configXml.addProperty("virtualhosts.virtualhost(-1)."+getName()+".store.class", TestableMemoryMessageStore.class.getName()); + configXml.addProperty("virtualhosts.virtualhost(-1)."+getName()+".store.factoryclass", TestableMemoryMessageStoreFactory.class.getName()); ServerConfiguration configuration = new ServerConfiguration(configXml); diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java index 933eb3b84f..45933e7064 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java @@ -27,6 +27,7 @@ import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.BasicContentHeaderProperties; import org.apache.qpid.framing.ContentBody; import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.abstraction.ContentChunk; import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.management.common.mbeans.ManagedQueue; import org.apache.qpid.server.AMQChannel; @@ -45,6 +46,7 @@ import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeDataSupport; import javax.management.openmbean.TabularData; import java.io.IOException; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -457,15 +459,16 @@ public class AMQQueueMBeanTest extends InternalBrokerBaseCase currentMessage.enqueue(qs); // route header - MessageMetaData mmd = currentMessage.headersReceived(); - currentMessage.setStoredMessage(getMessageStore().addMessage(mmd)); + MessageMetaData mmd = currentMessage.headersReceived(System.currentTimeMillis()); - // Add the body so we have something to test later - currentMessage.addContentBodyFrame( - getSession().getMethodRegistry() - .getProtocolVersionMethodConverter() - .convertToContentChunk( - new ContentBody(new byte[(int) MESSAGE_SIZE]))); + // Add the message to the store so we have something to test later + currentMessage.setStoredMessage(getMessageStore().addMessage(mmd)); + ContentChunk chunk = getSession().getMethodRegistry() + .getProtocolVersionMethodConverter() + .convertToContentChunk( + new ContentBody(new byte[(int) MESSAGE_SIZE])); + currentMessage.addContentBodyFrame(chunk); + currentMessage.getStoredMessage().addContent(0, ByteBuffer.wrap(chunk.getData())); AMQMessage m = new AMQMessage(currentMessage.getStoredMessage()); for(BaseQueue q : currentMessage.getDestinationQueues()) diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AckTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AckTest.java index 1b35dfe34f..409b9fd92e 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/AckTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AckTest.java @@ -20,7 +20,6 @@ */ package org.apache.qpid.server.queue; -import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; @@ -37,7 +36,7 @@ import org.apache.qpid.server.protocol.AMQProtocolSession; import org.apache.qpid.server.protocol.InternalTestProtocolSession; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.store.StoredMessage; -import org.apache.qpid.server.store.TestMemoryMessageStore; +import org.apache.qpid.server.store.TestableMemoryMessageStore; import org.apache.qpid.server.subscription.Subscription; import org.apache.qpid.server.subscription.SubscriptionFactoryImpl; import org.apache.qpid.server.txn.AutoCommitTransaction; @@ -53,13 +52,11 @@ import java.util.Set; */ public class AckTest extends InternalBrokerBaseCase { - private static final Logger _log = Logger.getLogger(AckTest.class); - private Subscription _subscription; private AMQProtocolSession _protocolSession; - private TestMemoryMessageStore _messageStore; + private TestableMemoryMessageStore _messageStore; private AMQChannel _channel; @@ -73,7 +70,7 @@ public class AckTest extends InternalBrokerBaseCase { super.setUp(); _virtualHost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"); - _messageStore = new TestMemoryMessageStore(); + _messageStore = new TestableMemoryMessageStore(); _protocolSession = new InternalTestProtocolSession(_virtualHost); _channel = new AMQChannel(_protocolSession,5, _messageStore /*dont need exchange registry*/); @@ -143,7 +140,7 @@ public class AckTest extends InternalBrokerBaseCase ArrayList qs = new ArrayList(); qs.add(_queue); msg.enqueue(qs); - MessageMetaData mmd = msg.headersReceived(); + MessageMetaData mmd = msg.headersReceived(System.currentTimeMillis()); final StoredMessage storedMessage = _messageStore.addMessage(mmd); msg.setStoredMessage(storedMessage); final AMQMessage message = new AMQMessage(storedMessage); @@ -180,7 +177,7 @@ public class AckTest extends InternalBrokerBaseCase } catch (InterruptedException e) { - e.printStackTrace(); //TODO. + Thread.currentThread().interrupt(); } } diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java b/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java index 34dc5b4428..afaa417415 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/MockAMQQueue.java @@ -34,7 +34,6 @@ 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.txn.ServerTransaction; import org.apache.qpid.server.virtualhost.VirtualHost; import java.util.List; @@ -357,12 +356,12 @@ public class MockAMQQueue implements AMQQueue return null; } - public void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, ServerTransaction storeContext) + public void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName) { } - public void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, ServerTransaction storeContext) + public void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName) { } diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/MockStoredMessage.java b/java/broker/src/test/java/org/apache/qpid/server/queue/MockStoredMessage.java index fba3851507..950d59bef5 100755 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/MockStoredMessage.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/MockStoredMessage.java @@ -25,7 +25,7 @@ import org.apache.qpid.framing.ContentHeaderBody; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.abstraction.MessagePublishInfo; import org.apache.qpid.server.message.MessageMetaData; -import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.StoreFuture; import org.apache.qpid.server.store.StoredMessage; import java.nio.ByteBuffer; @@ -105,9 +105,9 @@ public class MockStoredMessage implements StoredMessage return buf; } - public MessageStore.StoreFuture flushToStore() + public StoreFuture flushToStore() { - return MessageStore.IMMEDIATE_FUTURE; + return StoreFuture.IMMEDIATE_FUTURE; } public void remove() diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/PriorityQueueListTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/PriorityQueueListTest.java new file mode 100644 index 0000000000..e8c0470915 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/PriorityQueueListTest.java @@ -0,0 +1,117 @@ +/* + * + * 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 static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.qpid.server.message.AMQMessageHeader; +import org.apache.qpid.server.message.MessageReference; +import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.test.utils.QpidTestCase; + +public class PriorityQueueListTest extends QpidTestCase +{ + private static final byte[] PRIORITIES = {4, 5, 5, 4}; + PriorityQueueList _list = new PriorityQueueList(null, 10); + + private QueueEntry _priority4message1; + private QueueEntry _priority4message2; + private QueueEntry _priority5message1; + private QueueEntry _priority5message2; + + protected void setUp() + { + QueueEntry[] entries = new QueueEntry[PRIORITIES.length]; + + for (int i = 0; i < PRIORITIES.length; i++) + { + ServerMessage message = mock(ServerMessage.class); + AMQMessageHeader header = mock(AMQMessageHeader.class); + @SuppressWarnings({ "rawtypes", "unchecked" }) + MessageReference ref = mock(MessageReference.class); + + when(message.getMessageHeader()).thenReturn(header); + when(message.newReference()).thenReturn(ref); + when(ref.getMessage()).thenReturn(message); + when(header.getPriority()).thenReturn(PRIORITIES[i]); + + entries[i] = _list.add(message); + } + + _priority4message1 = entries[0]; + _priority4message2 = entries[3]; + _priority5message1 = entries[1]; + _priority5message2 = entries[2]; + } + + public void testPriorityQueueEntryCompareToItself() + { + //check messages compare to themselves properly + assertEquals("message should compare 'equal' to itself", + 0, _priority4message1.compareTo(_priority4message1)); + + assertEquals("message should compare 'equal' to itself", + 0, _priority5message2.compareTo(_priority5message2)); + } + + public void testPriorityQueueEntryCompareToSamePriority() + { + //check messages with the same priority are ordered properly + assertEquals("first message should be 'earlier' than second message of the same priority", + -1, _priority4message1.compareTo(_priority4message2)); + + assertEquals("first message should be 'earlier' than second message of the same priority", + -1, _priority5message1.compareTo(_priority5message2)); + + //and in reverse + assertEquals("second message should be 'later' than first message of the same priority", + 1, _priority4message2.compareTo(_priority4message1)); + + assertEquals("second message should be 'later' than first message of the same priority", + 1, _priority5message2.compareTo(_priority5message1)); + } + + public void testPriorityQueueEntryCompareToDifferentPriority() + { + //check messages with higher priority are ordered 'earlier' than those with lower priority + assertEquals("first message with priority 5 should be 'earlier' than first message of priority 4", + -1, _priority5message1.compareTo(_priority4message1)); + assertEquals("first message with priority 5 should be 'earlier' than second message of priority 4", + -1, _priority5message1.compareTo(_priority4message2)); + + assertEquals("second message with priority 5 should be 'earlier' than first message of priority 4", + -1, _priority5message2.compareTo(_priority4message1)); + assertEquals("second message with priority 5 should be 'earlier' than second message of priority 4", + -1, _priority5message2.compareTo(_priority4message2)); + + //and in reverse + assertEquals("first message with priority 4 should be 'later' than first message of priority 5", + 1, _priority4message1.compareTo(_priority5message1)); + assertEquals("first message with priority 4 should be 'later' than second message of priority 5", + 1, _priority4message1.compareTo(_priority5message2)); + + assertEquals("second message with priority 4 should be 'later' than first message of priority 5", + 1, _priority4message2.compareTo(_priority5message1)); + assertEquals("second message with priority 4 should be 'later' than second message of priority 5", + 1, _priority4message2.compareTo(_priority5message2)); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryListTestBase.java b/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryListTestBase.java index 7f81aaed51..4b40c3b552 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryListTestBase.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/QueueEntryListTestBase.java @@ -31,6 +31,7 @@ public abstract class QueueEntryListTestBase extends TestCase { protected static final AMQQueue _testQueue = new MockAMQQueue("test"); public abstract QueueEntryList getTestList(); + public abstract QueueEntryList getTestList(boolean newList); public abstract long getExpectedFirstMsgId(); public abstract int getExpectedListLength(); public abstract ServerMessage getTestMessageToAdd() throws AMQException; @@ -188,4 +189,36 @@ public abstract class QueueEntryListTestBase extends TestCase .getMessage().getMessageNumber(), third.getMessage().getMessageNumber()); } + /** + * Tests that after the last node of the list is marked deleted but has not yet been removed, + * the iterator still ignores it and returns that it is 'atTail()' and can't 'advance()' + * + * @see QueueEntryListTestBase#getTestList() + * @see QueueEntryListTestBase#getExpectedListLength() + */ + public void testIteratorIgnoresDeletedFinalNode() throws Exception + { + QueueEntryList list = getTestList(true); + int i = 0; + + QueueEntry queueEntry1 = list.add(new MockAMQMessage(i++)); + QueueEntry queueEntry2 = list.add(new MockAMQMessage(i++)); + + assertSame(queueEntry2, list.next(queueEntry1)); + assertNull(list.next(queueEntry2)); + + //'delete' the 2nd QueueEntry + assertTrue("Deleting node should have succeeded", queueEntry2.delete()); + + QueueEntryIterator iter = list.iterator(); + + //verify the iterator isn't 'atTail', can advance, and returns the 1st QueueEntry + assertFalse("Iterator should not have been 'atTail'", iter.atTail()); + assertTrue("Iterator should have been able to advance", iter.advance()); + assertSame("Iterator returned unexpected QueueEntry", queueEntry1, iter.getNode()); + + //verify the iterator is atTail() and can't advance + assertTrue("Iterator should have been 'atTail'", iter.atTail()); + assertFalse("Iterator should not have been able to advance", iter.advance()); + } } diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java index c345384e28..52ad4a7c5b 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleAMQQueueTest.java @@ -37,15 +37,16 @@ import org.apache.qpid.server.exchange.DirectExchange; import org.apache.qpid.server.message.AMQMessage; import org.apache.qpid.server.message.MessageMetaData; 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.registry.ApplicationRegistry; import org.apache.qpid.server.store.StoredMessage; import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.qpid.server.store.TestableMemoryMessageStoreFactory; import org.apache.qpid.server.subscription.MockSubscription; import org.apache.qpid.server.subscription.Subscription; 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.InternalBrokerBaseCase; import org.apache.qpid.server.virtualhost.VirtualHost; @@ -62,7 +63,6 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase protected SimpleAMQQueue _queue; protected VirtualHost _virtualHost; - protected TestableMemoryMessageStore _store = new TestableMemoryMessageStore(); protected AMQShortString _qname = new AMQShortString("qname"); protected AMQShortString _owner = new AMQShortString("owner"); protected AMQShortString _routingKey = new AMQShortString("routing key"); @@ -107,7 +107,9 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase ApplicationRegistry applicationRegistry = (ApplicationRegistry)ApplicationRegistry.getInstance(); PropertiesConfiguration env = new PropertiesConfiguration(); - _virtualHost = new VirtualHostImpl(ApplicationRegistry.getInstance(), new VirtualHostConfiguration(getClass().getName(), env), _store); + final VirtualHostConfiguration vhostConfig = new VirtualHostConfiguration(getClass().getName(), env); + vhostConfig.setMessageStoreFactoryClass(TestableMemoryMessageStoreFactory.class.getName()); + _virtualHost = new VirtualHostImpl(ApplicationRegistry.getInstance(), vhostConfig); applicationRegistry.getVirtualHostRegistry().registerVirtualHost(_virtualHost); _queue = (SimpleAMQQueue) AMQQueueFactory.createAMQQueueImpl(_qname, false, _owner, false, false, _virtualHost, _arguments); @@ -136,7 +138,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase } try { - _queue = new SimpleAMQQueue(_qname, false, _owner, false, false,null, Collections.EMPTY_MAP); + _queue = new SimpleAMQQueue(UUIDGenerator.generateUUID(), _qname, false, _owner, false,false, null, Collections.EMPTY_MAP); assertNull("Queue was created", _queue); } catch (IllegalArgumentException e) @@ -478,7 +480,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase public void testAutoDeleteQueue() throws Exception { _queue.stop(); - _queue = new SimpleAMQQueue(_qname, false, null, true, false, _virtualHost, Collections.EMPTY_MAP); + _queue = new SimpleAMQQueue(UUIDGenerator.generateUUID(), _qname, false, null, true, false, _virtualHost, Collections.EMPTY_MAP); _queue.setDeleteOnNoConsumers(true); _queue.registerSubscription(_subscription, false); AMQMessage message = createMessage(new Long(25)); @@ -634,12 +636,13 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase // Send persistent message qs.add(_queue); - MessageMetaData metaData = msg.headersReceived(); - StoredMessage handle = _store.addMessage(metaData); + MessageMetaData metaData = msg.headersReceived(System.currentTimeMillis()); + TestableMemoryMessageStore store = (TestableMemoryMessageStore) _virtualHost.getMessageStore(); + StoredMessage handle = store.addMessage(metaData); msg.setStoredMessage(handle); - ServerTransaction txn = new AutoCommitTransaction(_store); + ServerTransaction txn = new AutoCommitTransaction(store); txn.enqueue(qs, msg, new ServerTransaction.Action() { @@ -654,7 +657,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase }, 0L); // Check that it is enqueued - AMQQueue data = _store.getMessages().get(1L); + AMQQueue data = store.getMessages().get(1L); assertNull(data); // Dequeue message @@ -665,7 +668,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase _queue.dequeue(entry,null); // Check that it is dequeued - data = _store.getMessages().get(1L); + data = store.getMessages().get(1L); assertNull(data); } @@ -689,8 +692,8 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase public void testProcessQueueWithUniqueSelectors() throws Exception { TestSimpleQueueEntryListFactory factory = new TestSimpleQueueEntryListFactory(); - SimpleAMQQueue testQueue = new SimpleAMQQueue("testQueue", false, "testOwner",false, - false, _virtualHost, factory, null) + SimpleAMQQueue testQueue = new SimpleAMQQueue(UUIDGenerator.generateUUID(), "testQueue", false,"testOwner", + false, false, _virtualHost, factory, null) { @Override public void deliverAsync(Subscription sub) @@ -839,7 +842,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase /** * Tests that dequeued message is not copied as part of invocation of - * {@link SimpleAMQQueue#copyMessagesToAnotherQueue(long, long, String, ServerTransaction)} + * {@link SimpleAMQQueue#copyMessagesToAnotherQueue(long, long, String)} */ public void testCopyMessagesWithDequeuedEntry() { @@ -856,14 +859,8 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase // create another queue SimpleAMQQueue queue = createQueue(anotherQueueName); - // create transaction - ServerTransaction txn = new LocalTransaction(_queue.getVirtualHost().getMessageStore()); - // copy messages into another queue - _queue.copyMessagesToAnotherQueue(0, messageNumber, anotherQueueName, txn); - - // commit transaction - txn.commit(); + _queue.copyMessagesToAnotherQueue(0, messageNumber, anotherQueueName); // get messages on another queue List entries = queue.getMessagesOnTheQueue(); @@ -889,7 +886,7 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase /** * Tests that dequeued message is not moved as part of invocation of - * {@link SimpleAMQQueue#moveMessagesToAnotherQueue(long, long, String, ServerTransaction)} + * {@link SimpleAMQQueue#moveMessagesToAnotherQueue(long, long, String)} */ public void testMovedMessagesWithDequeuedEntry() { @@ -906,14 +903,8 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase // create another queue SimpleAMQQueue queue = createQueue(anotherQueueName); - // create transaction - ServerTransaction txn = new LocalTransaction(_queue.getVirtualHost().getMessageStore()); - // move messages into another queue - _queue.moveMessagesToAnotherQueue(0, messageNumber, anotherQueueName, txn); - - // commit transaction - txn.commit(); + _queue.moveMessagesToAnotherQueue(0, messageNumber, anotherQueueName); // get messages on another queue List entries = queue.getMessagesOnTheQueue(); @@ -1038,8 +1029,8 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase int dequeueMessageIndex = 1; // create queue with overridden method deliverAsync - SimpleAMQQueue testQueue = new SimpleAMQQueue(new AMQShortString("test"), false, - new AMQShortString("testOwner"), false, false, _virtualHost, null) + SimpleAMQQueue testQueue = new SimpleAMQQueue(UUIDGenerator.generateUUID(), new AMQShortString("test"), + false, new AMQShortString("testOwner"), false, false, _virtualHost, null) { @Override public void deliverAsync(Subscription sub) @@ -1109,8 +1100,8 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase public void testEqueueDequeuedEntry() { // create a queue where each even entry is considered a dequeued - SimpleAMQQueue queue = new SimpleAMQQueue(new AMQShortString("test"), false, new AMQShortString("testOwner"), - false, false, _virtualHost, new QueueEntryListFactory() + SimpleAMQQueue queue = new SimpleAMQQueue(UUIDGenerator.generateUUID(), new AMQShortString("test"), false, + new AMQShortString("testOwner"), false, false, _virtualHost, new QueueEntryListFactory() { public QueueEntryList createQueueEntryList(AMQQueue queue) { @@ -1187,8 +1178,8 @@ public class SimpleAMQQueueTest extends InternalBrokerBaseCase public void testActiveConsumerCount() throws Exception { - final SimpleAMQQueue queue = new SimpleAMQQueue(new AMQShortString("testActiveConsumerCount"), false, new AMQShortString("testOwner"), - false, false, _virtualHost, new SimpleQueueEntryList.Factory(), null); + final SimpleAMQQueue queue = new SimpleAMQQueue(UUIDGenerator.generateUUID(), new AMQShortString("testActiveConsumerCount"), false, + new AMQShortString("testOwner"), false, false, _virtualHost, new SimpleQueueEntryList.Factory(), null); //verify adding an active subscription increases the count final MockSubscription subscription1 = new MockSubscription(); diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryImplTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryImplTest.java index d8d78bbb84..ac18bbe687 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryImplTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryImplTest.java @@ -1,3 +1,23 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ package org.apache.qpid.server.queue; import org.apache.qpid.AMQException; diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java index 55c6143124..caf1eea09f 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/SimpleQueueEntryListTest.java @@ -23,6 +23,7 @@ package org.apache.qpid.server.queue; import org.apache.qpid.AMQException; import org.apache.qpid.server.message.AMQMessage; import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.queue.SimpleQueueEntryList.QueueEntryIteratorImpl; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -59,11 +60,24 @@ public class SimpleQueueEntryListTest extends QueueEntryListTestBase System.clearProperty(SCAVENGE_PROP); } } - + @Override public QueueEntryList getTestList() { - return _sqel; + return getTestList(false); + } + + @Override + public QueueEntryList getTestList(boolean newList) + { + if(newList) + { + return new SimpleQueueEntryList(_testQueue); + } + else + { + return _sqel; + } } @Override @@ -216,5 +230,4 @@ public class SimpleQueueEntryListTest extends QueueEntryListTestBase next = next.getNextValidEntry(); assertNull("The next entry after the last should be null", next); } - } diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/SortedQueueEntryListTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/SortedQueueEntryListTest.java index 794888f780..38b12f8250 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/queue/SortedQueueEntryListTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/queue/SortedQueueEntryListTest.java @@ -77,9 +77,23 @@ public class SortedQueueEntryListTest extends QueueEntryListTestBase } + @Override public QueueEntryList getTestList() { - return _sqel; + return getTestList(false); + } + + @Override + public QueueEntryList getTestList(boolean newList) + { + if(newList) + { + return new SelfValidatingSortedQueueEntryList(_testQueue, "KEY"); + } + else + { + return _sqel; + } } public int getExpectedListLength() diff --git a/java/broker/src/test/java/org/apache/qpid/server/registry/ApplicationRegistryShutdownTest.java b/java/broker/src/test/java/org/apache/qpid/server/registry/ApplicationRegistryShutdownTest.java index 6a8a6dc2d0..9ff8f0a531 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/registry/ApplicationRegistryShutdownTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/registry/ApplicationRegistryShutdownTest.java @@ -80,11 +80,10 @@ public class ApplicationRegistryShutdownTest extends InternalBrokerBaseCase } } - // Not using isEmpty as that is not in Java 5 - assertTrue("No new SASL mechanisms added by initialisation.", additions.size() != 0 ); + assertFalse("No new SASL mechanisms added by initialisation.", additions.isEmpty()); //Close the registry which will perform the close the AuthenticationManager - getRegistry().close(); + stopBroker(); //Validate that the SASL plugFins have been removed. Provider[] providersAfterClose = Security.getProviders(); diff --git a/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java b/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java index ba0a715001..bef03057ec 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java @@ -22,13 +22,19 @@ package org.apache.qpid.server.security.auth.rmi; import junit.framework.TestCase; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.XMLConfiguration; +import org.apache.qpid.server.configuration.ServerConfiguration; import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin; +import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.security.auth.AuthenticationResult; import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.util.TestApplicationRegistry; import javax.management.remote.JMXPrincipal; import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; import java.util.Collections; @@ -47,7 +53,7 @@ public class RMIPasswordAuthenticatorTest extends TestCase protected void setUp() throws Exception { _rmipa = new RMIPasswordAuthenticator(); - + _credentials = new String[] {USERNAME, PASSWORD}; } @@ -114,8 +120,19 @@ public class RMIPasswordAuthenticatorTest extends TestCase /** * Tests case where authentication manager is not set. */ - public void testNullAuthenticationManager() + public void testNullAuthenticationManager() throws Exception { + ServerConfiguration serverConfig = new ServerConfiguration(new XMLConfiguration()); + TestApplicationRegistry reg = new TestApplicationRegistry(serverConfig) + { + @Override + protected AuthenticationManager createAuthenticationManager() throws ConfigurationException + { + return null; + } + }; + ApplicationRegistry.initialise(reg); + try { _rmipa.authenticate(_credentials); @@ -126,6 +143,10 @@ public class RMIPasswordAuthenticatorTest extends TestCase assertEquals("Unexpected exception message", RMIPasswordAuthenticator.UNABLE_TO_LOOKUP, se.getMessage()); } + finally + { + ApplicationRegistry.remove(); + } } /** @@ -254,6 +275,11 @@ public class RMIPasswordAuthenticatorTest extends TestCase return new AuthenticationResult(AuthenticationStatus.CONTINUE); } } + + public CallbackHandler getHandler(String mechanism) + { + return null; + } }; } } diff --git a/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslServerTestCase.java b/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslServerTestCase.java index c3671d6a87..f5247634ac 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslServerTestCase.java +++ b/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslServerTestCase.java @@ -54,7 +54,7 @@ public abstract class SaslServerTestCase extends TestCase } catch (SaslException e) { - assertEquals("Authentication failed", e.getCause().getMessage()); + assertTrue(e.getMessage().contains("Authentication failed")); exceptionCaught = true; } if (!exceptionCaught) diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/DurableConfigurationStoreTest.java b/java/broker/src/test/java/org/apache/qpid/server/store/DurableConfigurationStoreTest.java new file mode 100644 index 0000000000..a1cbb2cbc8 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/store/DurableConfigurationStoreTest.java @@ -0,0 +1,377 @@ +package org.apache.qpid.server.store; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.isA; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.times; + +import java.io.File; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.apache.commons.configuration.Configuration; +import org.apache.qpid.AMQStoreException; +import org.apache.qpid.common.AMQPFilterTypes; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.FieldTable; +import org.apache.qpid.server.binding.Binding; +import org.apache.qpid.server.exchange.Exchange; +import org.apache.qpid.server.logging.SystemOutMessageLogger; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.actors.TestLogActor; +import org.apache.qpid.server.logging.subjects.TestBlankSubject; +import org.apache.qpid.server.message.EnqueableMessage; +import org.apache.qpid.server.model.UUIDGenerator; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.MockStoredMessage; +import org.apache.qpid.server.store.ConfigurationRecoveryHandler; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.MessageStoreRecoveryHandler; +import org.apache.qpid.server.store.TransactionLogRecoveryHandler; +import org.apache.qpid.server.store.ConfigurationRecoveryHandler.BindingRecoveryHandler; +import org.apache.qpid.server.store.ConfigurationRecoveryHandler.ExchangeRecoveryHandler; +import org.apache.qpid.server.store.ConfigurationRecoveryHandler.QueueRecoveryHandler; +import org.apache.qpid.server.store.MessageStoreRecoveryHandler.StoredMessageRecoveryHandler; +import org.apache.qpid.server.store.Transaction.Record; +import org.apache.qpid.server.store.derby.DerbyMessageStoreFactory; +import org.apache.qpid.test.utils.QpidTestCase; +import org.apache.qpid.util.FileUtils; + +public class DurableConfigurationStoreTest extends QpidTestCase +{ + private static final String EXCHANGE_NAME = "exchangeName"; + private String _storePath; + private String _storeName; + private MessageStore _store; + private Configuration _configuration; + + private ConfigurationRecoveryHandler _recoveryHandler; + private QueueRecoveryHandler _queueRecoveryHandler; + private ExchangeRecoveryHandler _exchangeRecoveryHandler; + private BindingRecoveryHandler _bindingRecoveryHandler; + private ConfigurationRecoveryHandler.BrokerLinkRecoveryHandler _linkRecoveryHandler; + private MessageStoreRecoveryHandler _messageStoreRecoveryHandler; + private StoredMessageRecoveryHandler _storedMessageRecoveryHandler; + private TransactionLogRecoveryHandler _logRecoveryHandler; + private TransactionLogRecoveryHandler.QueueEntryRecoveryHandler _queueEntryRecoveryHandler; + private TransactionLogRecoveryHandler.DtxRecordRecoveryHandler _dtxRecordRecoveryHandler; + + private Exchange _exchange = mock(Exchange.class); + private static final String ROUTING_KEY = "routingKey"; + private static final String QUEUE_NAME = "queueName"; + private FieldTable _bindingArgs; + private UUID _queueId; + private UUID _exchangeId; + + public void setUp() throws Exception + { + super.setUp(); + + _queueId = UUIDGenerator.generateUUID(); + _exchangeId = UUIDGenerator.generateUUID(); + + _storeName = getName(); + _storePath = TMP_FOLDER + "/" + _storeName; + FileUtils.delete(new File(_storePath), true); + setTestSystemProperty("QPID_WORK", TMP_FOLDER); + _configuration = mock(Configuration.class); + _recoveryHandler = mock(ConfigurationRecoveryHandler.class); + _queueRecoveryHandler = mock(QueueRecoveryHandler.class); + _exchangeRecoveryHandler = mock(ExchangeRecoveryHandler.class); + _bindingRecoveryHandler = mock(BindingRecoveryHandler.class); + _storedMessageRecoveryHandler = mock(StoredMessageRecoveryHandler.class); + _logRecoveryHandler = mock(TransactionLogRecoveryHandler.class); + _messageStoreRecoveryHandler = mock(MessageStoreRecoveryHandler.class); + _queueEntryRecoveryHandler = mock(TransactionLogRecoveryHandler.QueueEntryRecoveryHandler.class); + _dtxRecordRecoveryHandler = mock(TransactionLogRecoveryHandler.DtxRecordRecoveryHandler.class); + + when(_messageStoreRecoveryHandler.begin()).thenReturn(_storedMessageRecoveryHandler); + when(_recoveryHandler.begin(isA(MessageStore.class))).thenReturn(_queueRecoveryHandler); + when(_queueRecoveryHandler.completeQueueRecovery()).thenReturn(_exchangeRecoveryHandler); + when(_exchangeRecoveryHandler.completeExchangeRecovery()).thenReturn(_bindingRecoveryHandler); + when(_bindingRecoveryHandler.completeBindingRecovery()).thenReturn(_linkRecoveryHandler); + when(_logRecoveryHandler.begin(any(MessageStore.class))).thenReturn(_queueEntryRecoveryHandler); + when(_queueEntryRecoveryHandler.completeQueueEntryRecovery()).thenReturn(_dtxRecordRecoveryHandler); + when(_exchange.getNameShortString()).thenReturn(AMQShortString.valueOf(EXCHANGE_NAME)); + when(_exchange.getId()).thenReturn(_exchangeId); + when(_configuration.getString(eq(MessageStoreConstants.ENVIRONMENT_PATH_PROPERTY), anyString())).thenReturn( + _storePath); + + _bindingArgs = new FieldTable(); + AMQShortString argKey = AMQPFilterTypes.JMS_SELECTOR.getValue(); + String argValue = "some selector expression"; + _bindingArgs.put(argKey, argValue); + + reopenStore(); + } + + public void tearDown() throws Exception + { + FileUtils.delete(new File(_storePath), true); + super.tearDown(); + } + + public void testCreateExchange() throws Exception + { + Exchange exchange = createTestExchange(); + _store.createExchange(exchange); + + reopenStore(); + verify(_exchangeRecoveryHandler).exchange(_exchangeId, getName(), getName() + "Type", true); + } + + public void testRemoveExchange() throws Exception + { + Exchange exchange = createTestExchange(); + _store.createExchange(exchange); + + _store.removeExchange(exchange); + + reopenStore(); + verify(_exchangeRecoveryHandler, never()).exchange(any(UUID.class), anyString(), anyString(), anyBoolean()); + } + + public void testBindQueue() throws Exception + { + AMQQueue queue = createTestQueue(QUEUE_NAME, "queueOwner", false); + Binding binding = new Binding(UUIDGenerator.generateUUID(), ROUTING_KEY, queue, _exchange, + FieldTable.convertToMap(_bindingArgs)); + _store.bindQueue(binding); + + reopenStore(); + + ByteBuffer argsAsBytes = ByteBuffer.wrap(_bindingArgs.getDataAsBytes()); + + verify(_bindingRecoveryHandler).binding(binding.getId(), _exchange.getId(), queue.getId(), ROUTING_KEY, argsAsBytes); + } + + public void testUnbindQueue() throws Exception + { + AMQQueue queue = createTestQueue(QUEUE_NAME, "queueOwner", false); + Binding binding = new Binding(UUIDGenerator.generateUUID(), ROUTING_KEY, queue, _exchange, + FieldTable.convertToMap(_bindingArgs)); + _store.bindQueue(binding); + + _store.unbindQueue(binding); + reopenStore(); + + verify(_bindingRecoveryHandler, never()).binding(any(UUID.class), any(UUID.class), any(UUID.class), anyString(), + isA(ByteBuffer.class)); + } + + public void testCreateQueueAMQQueue() throws Exception + { + AMQQueue queue = createTestQueue(getName(), getName() + "Owner", true); + _store.createQueue(queue); + + reopenStore(); + verify(_queueRecoveryHandler).queue(_queueId, getName(), getName() + "Owner", true, null); + } + + public void testCreateQueueAMQQueueFieldTable() throws Exception + { + AMQQueue queue = createTestQueue(getName(), getName() + "Owner", true); + Map attributes = new HashMap(); + attributes.put("x-qpid-dlq-enabled", Boolean.TRUE); + attributes.put("x-qpid-maximum-delivery-count", new Integer(10)); + + FieldTable arguments = FieldTable.convertToFieldTable(attributes); + _store.createQueue(queue, arguments); + + reopenStore(); + verify(_queueRecoveryHandler).queue(_queueId, getName(), getName() + "Owner", true, arguments); + } + + public void testUpdateQueue() throws Exception + { + // create queue + AMQQueue queue = createTestQueue(getName(), getName() + "Owner", true); + Map attributes = new HashMap(); + attributes.put("x-qpid-dlq-enabled", Boolean.TRUE); + attributes.put("x-qpid-maximum-delivery-count", new Integer(10)); + FieldTable arguments = FieldTable.convertToFieldTable(attributes); + _store.createQueue(queue, arguments); + + // update the queue to have exclusive=false + queue = createTestQueue(getName(), getName() + "Owner", false); + _store.updateQueue(queue); + + reopenStore(); + verify(_queueRecoveryHandler).queue(_queueId, getName(), getName() + "Owner", false, arguments); + } + + public void testRemoveQueue() throws Exception + { + // create queue + AMQQueue queue = createTestQueue(getName(), getName() + "Owner", true); + Map attributes = new HashMap(); + attributes.put("x-qpid-dlq-enabled", Boolean.TRUE); + attributes.put("x-qpid-maximum-delivery-count", new Integer(10)); + FieldTable arguments = FieldTable.convertToFieldTable(attributes); + _store.createQueue(queue, arguments); + + // remove queue + _store.removeQueue(queue); + reopenStore(); + verify(_queueRecoveryHandler, never()).queue(any(UUID.class), anyString(), anyString(), anyBoolean(), + any(FieldTable.class)); + } + + private AMQQueue createTestQueue(String queueName, String queueOwner, boolean exclusive) throws AMQStoreException + { + AMQQueue queue = mock(AMQQueue.class); + when(queue.getName()).thenReturn(queueName); + when(queue.getNameShortString()).thenReturn(AMQShortString.valueOf(queueName)); + when(queue.getOwner()).thenReturn(AMQShortString.valueOf(queueOwner)); + when(queue.isExclusive()).thenReturn(exclusive); + when(queue.getId()).thenReturn(_queueId); + return queue; + } + + private Exchange createTestExchange() + { + Exchange exchange = mock(Exchange.class); + when(exchange.getNameShortString()).thenReturn(AMQShortString.valueOf(getName())); + when(exchange.getName()).thenReturn(getName()); + when(exchange.getTypeShortString()).thenReturn(AMQShortString.valueOf(getName() + "Type")); + when(exchange.isAutoDelete()).thenReturn(true); + when(exchange.getId()).thenReturn(_exchangeId); + return exchange; + } + + private void reopenStore() throws Exception + { + if (_store != null) + { + _store.close(); + } + _store = createStore(); + + _store.configureConfigStore(_storeName, _recoveryHandler, _configuration); + _store.configureMessageStore(_storeName, _messageStoreRecoveryHandler, _logRecoveryHandler, _configuration); + _store.activate(); + } + + protected MessageStore createStore() throws Exception + { + String storeFactoryClass = System.getProperty(MS_FACTORY_CLASS_NAME_KEY); + if (storeFactoryClass == null) + { + storeFactoryClass = DerbyMessageStoreFactory.class.getName(); + } + CurrentActor.set(new TestLogActor(new SystemOutMessageLogger())); + MessageStoreFactory factory = (MessageStoreFactory) Class.forName(storeFactoryClass).newInstance(); + return factory.createMessageStore(); + } + + public void testRecordXid() throws Exception + { + Record enqueueRecord = getTestRecord(1); + Record dequeueRecord = getTestRecord(2); + Record[] enqueues = { enqueueRecord }; + Record[] dequeues = { dequeueRecord }; + byte[] globalId = new byte[] { 1 }; + byte[] branchId = new byte[] { 2 }; + + Transaction transaction = _store.newTransaction(); + transaction.recordXid(1l, globalId, branchId, enqueues, dequeues); + transaction.commitTran(); + reopenStore(); + verify(_dtxRecordRecoveryHandler).dtxRecord(1l, globalId, branchId, enqueues, dequeues); + + transaction = _store.newTransaction(); + transaction.removeXid(1l, globalId, branchId); + transaction.commitTran(); + + reopenStore(); + verify(_dtxRecordRecoveryHandler, times(1)).dtxRecord(1l, globalId, branchId, enqueues, dequeues); + } + + private Record getTestRecord(long messageNumber) + { + UUID queueId1 = UUIDGenerator.generateUUID(); + TransactionLogResource queue1 = mock(TransactionLogResource.class); + when(queue1.getId()).thenReturn(queueId1); + EnqueableMessage message1 = mock(EnqueableMessage.class); + when(message1.isPersistent()).thenReturn(true); + when(message1.getMessageNumber()).thenReturn(messageNumber); + when(message1.getStoredMessage()).thenReturn(new MockStoredMessage(messageNumber)); + Record enqueueRecord = new TestRecord(queue1, message1); + return enqueueRecord; + } + + private static class TestRecord implements Record + { + private TransactionLogResource _queue; + private EnqueableMessage _message; + + public TestRecord(TransactionLogResource queue, EnqueableMessage message) + { + super(); + _queue = queue; + _message = message; + } + + @Override + public TransactionLogResource getQueue() + { + return _queue; + } + + @Override + public EnqueableMessage getMessage() + { + return _message; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((_message == null) ? 0 : new Long(_message.getMessageNumber()).hashCode()); + result = prime * result + ((_queue == null) ? 0 : _queue.getId().hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (!(obj instanceof Record)) + { + return false; + } + Record other = (Record) obj; + if (_message == null && other.getMessage() != null) + { + return false; + } + if (_queue == null && other.getQueue() != null) + { + return false; + } + if (_message.getMessageNumber() != other.getMessage().getMessageNumber()) + { + return false; + } + return _queue.getId().equals(other.getQueue().getId()); + } + + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/EventManagerTest.java b/java/broker/src/test/java/org/apache/qpid/server/store/EventManagerTest.java new file mode 100644 index 0000000000..2be79c5839 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/store/EventManagerTest.java @@ -0,0 +1,72 @@ +/* + * 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.store; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.apache.qpid.server.store.Event.AFTER_ACTIVATE; +import static org.apache.qpid.server.store.Event.BEFORE_ACTIVATE; +import junit.framework.TestCase; + +public class EventManagerTest extends TestCase +{ + private EventManager _eventManager = new EventManager(); + private EventListener _mockListener = mock(EventListener.class); + + public void testEventListenerFires() + { + _eventManager.addEventListener(_mockListener, BEFORE_ACTIVATE); + _eventManager.notifyEvent(BEFORE_ACTIVATE); + verify(_mockListener).event(BEFORE_ACTIVATE); + } + + public void testEventListenerDoesntFire() + { + _eventManager.addEventListener(_mockListener, BEFORE_ACTIVATE); + _eventManager.notifyEvent(AFTER_ACTIVATE); + verifyZeroInteractions(_mockListener); + } + + public void testEventListenerFiresMulitpleTimes() + { + _eventManager.addEventListener(_mockListener, BEFORE_ACTIVATE); + _eventManager.addEventListener(_mockListener, AFTER_ACTIVATE); + + _eventManager.notifyEvent(BEFORE_ACTIVATE); + verify(_mockListener).event(BEFORE_ACTIVATE); + + _eventManager.notifyEvent(AFTER_ACTIVATE); + verify(_mockListener).event(AFTER_ACTIVATE); + } + + public void testMultipleListenersFireForSameEvent() + { + final EventListener mockListener1 = mock(EventListener.class); + final EventListener mockListener2 = mock(EventListener.class); + + _eventManager.addEventListener(mockListener1, BEFORE_ACTIVATE); + _eventManager.addEventListener(mockListener2, BEFORE_ACTIVATE); + _eventManager.notifyEvent(BEFORE_ACTIVATE); + + verify(mockListener1).event(BEFORE_ACTIVATE); + verify(mockListener2).event(BEFORE_ACTIVATE); + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java b/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java index 5fc074d877..3fb0776083 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/store/MessageStoreTest.java @@ -39,6 +39,7 @@ import org.apache.qpid.server.exchange.ExchangeType; import org.apache.qpid.server.exchange.TopicExchange; import org.apache.qpid.server.message.AMQMessage; import org.apache.qpid.server.message.MessageMetaData; +import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.queue.AMQPriorityQueue; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; @@ -58,6 +59,7 @@ import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; /** * This tests the MessageStores by using the available interfaces. @@ -101,7 +103,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase String storePath = System.getProperty("QPID_WORK") + "/" + getName(); _config = new PropertiesConfiguration(); - _config.addProperty("store.class", getTestProfileMessageStoreClassName()); + _config.addProperty("store.factoryclass", getTestProfileMessageStoreFactoryClassName()); _config.addProperty("store.environment-path", storePath); cleanup(new File(storePath)); @@ -298,7 +300,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase 1, queueRegistry.getQueues().size()); //test that removing the queue means it is not recovered next time - getVirtualHost().getDurableConfigurationStore().removeQueue(queueRegistry.getQueue(durableQueueName)); + getVirtualHost().getMessageStore().removeQueue(queueRegistry.getQueue(durableQueueName)); reloadVirtualHost(); @@ -351,7 +353,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase origExchangeCount + 1, exchangeRegistry.getExchangeNames().size()); //test that removing the exchange means it is not recovered next time - getVirtualHost().getDurableConfigurationStore().removeExchange(exchangeRegistry.getExchange(directExchangeName)); + getVirtualHost().getMessageStore().removeExchange(exchangeRegistry.getExchange(directExchangeName)); reloadVirtualHost(); @@ -600,7 +602,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase currentMessage.setExpiration(); - MessageMetaData mmd = currentMessage.headersReceived(); + MessageMetaData mmd = currentMessage.headersReceived(System.currentTimeMillis()); currentMessage.setStoredMessage(getVirtualHost().getMessageStore().addMessage(mmd)); currentMessage.getStoredMessage().flushToStore(); currentMessage.route(); @@ -707,7 +709,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase if (queue.isDurable() && !queue.isAutoDelete()) { - getVirtualHost().getDurableConfigurationStore().createQueue(queue, queueArguments); + getVirtualHost().getMessageStore().createQueue(queue, queueArguments); } } catch (AMQException e) @@ -739,7 +741,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase try { - exchange = type.newInstance(getVirtualHost(), name, durable, 0, false); + exchange = type.newInstance(UUIDGenerator.generateUUID(), getVirtualHost(), name, durable, 0, false); } catch (AMQException e) { @@ -751,7 +753,7 @@ public class MessageStoreTest extends InternalBrokerBaseCase getVirtualHost().getExchangeRegistry().registerExchange(exchange); if (durable) { - getVirtualHost().getDurableConfigurationStore().createExchange(exchange); + getVirtualHost().getMessageStore().createExchange(exchange); } } catch (AMQException e) diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/OperationalLoggingListenerTest.java b/java/broker/src/test/java/org/apache/qpid/server/store/OperationalLoggingListenerTest.java new file mode 100644 index 0000000000..42746f9119 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/store/OperationalLoggingListenerTest.java @@ -0,0 +1,181 @@ +package org.apache.qpid.server.store; + +import java.util.ArrayList; +import java.util.List; +import junit.framework.TestCase; +import org.apache.qpid.server.logging.LogActor; +import org.apache.qpid.server.logging.LogMessage; +import org.apache.qpid.server.logging.LogSubject; +import org.apache.qpid.server.logging.RootMessageLogger; +import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.messages.ConfigStoreMessages; +import org.apache.qpid.server.logging.messages.MessageStoreMessages; +import org.apache.qpid.server.logging.messages.TransactionLogMessages; + +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +public class OperationalLoggingListenerTest extends TestCase +{ + + + public static final String STORE_LOCATION = "The moon!"; + + protected void setUp() throws Exception + { + super.setUp(); + + } + + public void testOperationalLoggingWithStoreLocation() throws Exception + { + TestMessageStore messageStore = new TestMessageStore(); + LogSubject logSubject = LOG_SUBJECT; + + OperationalLoggingListener.listen(messageStore, logSubject); + + performTests(messageStore, true); + + } + + public void testOperationalLogging() throws Exception + { + TestMessageStore messageStore = new TestMessageStore(); + LogSubject logSubject = LOG_SUBJECT; + + OperationalLoggingListener.listen(messageStore, logSubject); + + performTests(messageStore, false); + } + + private void performTests(TestMessageStore messageStore, boolean setStoreLocation) + { + final List messages = new ArrayList(); + + CurrentActor.set(new TestActor(messages)); + + if(setStoreLocation) + { + messageStore.setStoreLocation(STORE_LOCATION); + } + + + messageStore.attainState(State.CONFIGURING); + assertEquals("Unexpected number of operational log messages on configuring", 1, messages.size()); + assertEquals(messages.remove(0).toString(), ConfigStoreMessages.CREATED().toString()); + + messageStore.attainState(State.CONFIGURED); + assertEquals("Unexpected number of operational log messages on CONFIGURED", setStoreLocation ? 3 : 2, messages.size()); + assertEquals(messages.remove(0).toString(), MessageStoreMessages.CREATED().toString()); + assertEquals(messages.remove(0).toString(), TransactionLogMessages.CREATED().toString()); + if(setStoreLocation) + { + assertEquals(messages.remove(0).toString(), MessageStoreMessages.STORE_LOCATION(STORE_LOCATION).toString()); + } + + messageStore.attainState(State.RECOVERING); + assertEquals("Unexpected number of operational log messages on RECOVERING", 1, messages.size()); + assertEquals(messages.remove(0).toString(), MessageStoreMessages.RECOVERY_START().toString()); + + + messageStore.attainState(State.ACTIVE); + assertEquals("Unexpected number of operational log messages on ACTIVE", 1, messages.size()); + assertEquals(messages.remove(0).toString(), MessageStoreMessages.RECOVERY_COMPLETE().toString()); + + messageStore.attainState(State.CLOSING); + assertEquals("Unexpected number of operational log messages on CLOSING", 0, messages.size()); + + messageStore.attainState(State.CLOSED); + assertEquals("Unexpected number of operational log messages on CLOSED", 1, messages.size()); + assertEquals(messages.remove(0).toString(), MessageStoreMessages.CLOSED().toString()); + } + + @Override + protected void tearDown() throws Exception + { + super.tearDown(); + CurrentActor.remove(); + } + + + private static final LogSubject LOG_SUBJECT = new LogSubject() + { + public String toLogString() + { + return ""; + } + }; + + private static final class TestMessageStore extends NullMessageStore + { + + private final EventManager _eventManager = new EventManager(); + private final StateManager _stateManager = new StateManager(_eventManager); + private String _storeLocation; + + public void attainState(State state) + { + _stateManager.attainState(state); + } + + @Override + public String getStoreLocation() + { + return _storeLocation; + } + + public void setStoreLocation(String storeLocation) + { + _storeLocation = storeLocation; + } + + @Override + public void addEventListener(EventListener eventListener, Event... events) + { + _eventManager.addEventListener(eventListener, events); + } + } + + private static class TestActor implements LogActor + { + private final List _messages; + + public TestActor(List messages) + { + _messages = messages; + } + + public void message(LogSubject subject, LogMessage message) + { + _messages.add(message); + } + + public void message(LogMessage message) + { + _messages.add(message); + } + + public RootMessageLogger getRootMessageLogger() + { + return null; + } + + public String getLogMessage() + { + return null; + } + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/ReferenceCountingTest.java b/java/broker/src/test/java/org/apache/qpid/server/store/ReferenceCountingTest.java index 2ffa157ca8..4aa023a25c 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/store/ReferenceCountingTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/store/ReferenceCountingTest.java @@ -34,12 +34,12 @@ import org.apache.qpid.test.utils.QpidTestCase; */ public class ReferenceCountingTest extends QpidTestCase { - private TestMemoryMessageStore _store; + private TestableMemoryMessageStore _store; protected void setUp() throws Exception { - _store = new TestMemoryMessageStore(); + _store = new TestableMemoryMessageStore(); } /** diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/SkeletonMessageStore.java b/java/broker/src/test/java/org/apache/qpid/server/store/SkeletonMessageStore.java deleted file mode 100644 index 88b9acffe0..0000000000 --- a/java/broker/src/test/java/org/apache/qpid/server/store/SkeletonMessageStore.java +++ /dev/null @@ -1,157 +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.store; - -import org.apache.commons.configuration.Configuration; - -import org.apache.qpid.AMQStoreException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.FieldTable; -import org.apache.qpid.server.exchange.Exchange; -import org.apache.qpid.server.logging.LogSubject; -import org.apache.qpid.server.message.EnqueableMessage; -import org.apache.qpid.server.queue.AMQQueue; - -/** - * A message store that does nothing. Designed to be used in tests that do not want to use any message store - * functionality. - */ -public class SkeletonMessageStore implements MessageStore -{ - public void configureConfigStore(String name, - ConfigurationRecoveryHandler recoveryHandler, - Configuration config, - LogSubject logSubject) throws Exception - { - } - - public void configureMessageStore(String name, - MessageStoreRecoveryHandler recoveryHandler, - Configuration config, - LogSubject logSubject) throws Exception - { - } - - public void close() throws Exception - { - } - - public StoredMessage addMessage(M metaData) - { - return null; - } - - - public void createExchange(Exchange exchange) throws AMQStoreException - { - - } - - public void removeExchange(Exchange exchange) throws AMQStoreException - { - - } - - public void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException - { - - } - - public void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQStoreException - { - - } - - public void createQueue(AMQQueue queue) throws AMQStoreException - { - } - - public void createQueue(AMQQueue queue, FieldTable arguments) throws AMQStoreException - { - } - - public boolean isPersistent() - { - return false; - } - - public void removeQueue(final AMQQueue queue) throws AMQStoreException - { - - } - - public void configureTransactionLog(String name, - TransactionLogRecoveryHandler recoveryHandler, - Configuration storeConfiguration, - LogSubject logSubject) throws Exception - { - - } - - public Transaction newTransaction() - { - return new Transaction() - { - - public void enqueueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException - { - - } - - public void dequeueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException - { - - } - - public void commitTran() throws AMQStoreException - { - - } - - public StoreFuture commitTranAsync() throws AMQStoreException - { - return new StoreFuture() - { - public boolean isComplete() - { - return true; - } - - public void waitForCompletion() - { - - } - }; - } - - public void abortTran() throws AMQStoreException - { - - } - }; - } - - public void updateQueue(AMQQueue queue) throws AMQStoreException - { - - } - -} diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/StateManagerTest.java b/java/broker/src/test/java/org/apache/qpid/server/store/StateManagerTest.java new file mode 100644 index 0000000000..97c88ca1d3 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/store/StateManagerTest.java @@ -0,0 +1,195 @@ +/* + * + * 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.store; + + +import java.util.EnumSet; +import junit.framework.TestCase; + +public class StateManagerTest extends TestCase implements EventListener +{ + + private StateManager _manager; + private Event _event; + + public void setUp() throws Exception + { + super.setUp(); + _manager = new StateManager(this); + } + + public void testInitialState() + { + assertEquals(State.INITIAL, _manager.getState()); + } + + public void testStateTransitionAllowed() + { + assertEquals(State.INITIAL, _manager.getState()); + + _manager.stateTransition(State.INITIAL, State.CONFIGURING); + assertEquals(State.CONFIGURING, _manager.getState()); + } + + public void testStateTransitionDisallowed() + { + assertEquals(State.INITIAL, _manager.getState()); + + try + { + _manager.stateTransition(State.ACTIVE, State.CLOSING); + fail("Exception not thrown"); + } + catch (IllegalStateException e) + { + // PASS + } + assertEquals(State.INITIAL, _manager.getState()); + } + + public void testIsInState() + { + assertEquals(State.INITIAL, _manager.getState()); + assertFalse(_manager.isInState(State.ACTIVE)); + assertTrue(_manager.isInState(State.INITIAL)); + } + + public void testIsNotInState() + { + assertEquals(State.INITIAL, _manager.getState()); + assertTrue(_manager.isNotInState(State.ACTIVE)); + assertFalse(_manager.isNotInState(State.INITIAL)); + } + + public void testCheckInState() + { + assertEquals(State.INITIAL, _manager.getState()); + + try + { + _manager.checkInState(State.ACTIVE); + fail("Exception not thrown"); + } + catch (IllegalStateException e) + { + // PASS + } + assertEquals(State.INITIAL, _manager.getState()); + } + + public void testValidStateTransitions() + { + assertEquals(State.INITIAL, _manager.getState()); + performValidTransition(StateManager.CONFIGURE); + performValidTransition(StateManager.CONFIGURE_COMPLETE); + performValidTransition(StateManager.RECOVER); + performValidTransition(StateManager.ACTIVATE); + performValidTransition(StateManager.QUIESCE); + performValidTransition(StateManager.QUIESCE_COMPLETE); + performValidTransition(StateManager.RESTART); + performValidTransition(StateManager.ACTIVATE); + performValidTransition(StateManager.CLOSE_ACTIVE); + performValidTransition(StateManager.CLOSE_COMPLETE); + + _manager = new StateManager(this); + performValidTransition(StateManager.CONFIGURE); + performValidTransition(StateManager.CONFIGURE_COMPLETE); + performValidTransition(StateManager.RECOVER); + performValidTransition(StateManager.ACTIVATE); + performValidTransition(StateManager.QUIESCE); + performValidTransition(StateManager.QUIESCE_COMPLETE); + performValidTransition(StateManager.CLOSE_QUIESCED); + performValidTransition(StateManager.CLOSE_COMPLETE); + } + + private void performValidTransition(StateManager.Transition transition) + { + _manager.attainState(transition.getEndState()); + assertEquals("Unexpected end state", transition.getEndState(), _manager.getState()); + assertEquals("Unexpected event", transition.getEvent(), _event); + _event = null; + } + + public void testInvalidStateTransitions() + { + assertEquals(State.INITIAL, _manager.getState()); + + + performInvalidTransitions(StateManager.CONFIGURE, State.CONFIGURED); + performInvalidTransitions(StateManager.CONFIGURE_COMPLETE, State.RECOVERING); + performInvalidTransitions(StateManager.RECOVER, State.ACTIVE); + performInvalidTransitions(StateManager.ACTIVATE, State.QUIESCING, State.CLOSING); + performInvalidTransitions(StateManager.QUIESCE, State.QUIESCED); + performInvalidTransitions(StateManager.QUIESCE_COMPLETE, State.RECOVERING, State.CLOSING); + performInvalidTransitions(StateManager.CLOSE_QUIESCED, State.CLOSED); + performInvalidTransitions(StateManager.CLOSE_COMPLETE); + + + + + } + + private void performInvalidTransitions(StateManager.Transition preTransition, State... validTransitions) + { + if(preTransition != null) + { + performValidTransition(preTransition); + } + + EnumSet nextStates = EnumSet.allOf(State.class); + + if(validTransitions != null) + { + for(State state: validTransitions) + { + nextStates.remove(state); + } + } + + for(State nextState : nextStates) + { + performInvalidStateTransition(nextState); + } + + + } + + private void performInvalidStateTransition(State state) + { + try + { + _event = null; + State startState = _manager.getState(); + _manager.attainState(state); + fail("Invalid state transition performed: " + startState + " to " + state); + } + catch(IllegalStateException e) + { + // pass + } + assertNull("No event should have be fired", _event); + } + + public void event(Event event) + { + _event = event; + } +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/TestMemoryMessageStore.java b/java/broker/src/test/java/org/apache/qpid/server/store/TestMemoryMessageStore.java deleted file mode 100644 index 8a261b3b86..0000000000 --- a/java/broker/src/test/java/org/apache/qpid/server/store/TestMemoryMessageStore.java +++ /dev/null @@ -1,98 +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.store; - -import java.nio.ByteBuffer; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Adds some extra methods to the memory message store for testing purposes. - */ -public class TestMemoryMessageStore extends MemoryMessageStore -{ - private AtomicInteger _messageCount = new AtomicInteger(0); - - - public TestMemoryMessageStore() - { - } - - @Override - public StoredMessage addMessage(StorableMessageMetaData metaData) - { - return new TestableStoredMessage(super.addMessage(metaData)); - } - - public int getMessageCount() - { - return _messageCount.get(); - } - - private class TestableStoredMessage implements StoredMessage - { - private final StoredMessage _storedMessage; - - public TestableStoredMessage(StoredMessage storedMessage) - { - _messageCount.incrementAndGet(); - _storedMessage = storedMessage; - } - - public StorableMessageMetaData getMetaData() - { - return _storedMessage.getMetaData(); - } - - public long getMessageNumber() - { - return _storedMessage.getMessageNumber(); - } - - public void addContent(int offsetInMessage, ByteBuffer src) - { - _storedMessage.addContent(offsetInMessage, src); - } - - public int getContent(int offsetInMessage, ByteBuffer dst) - { - return _storedMessage.getContent(offsetInMessage, dst); - } - - - public ByteBuffer getContent(int offsetInMessage, int size) - { - return _storedMessage.getContent(offsetInMessage, size); - } - - public StoreFuture flushToStore() - { - return _storedMessage.flushToStore(); - } - - public void remove() - { - _storedMessage.remove(); - _messageCount.decrementAndGet(); - } - - } - -} diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java b/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java index e409734a17..210408f490 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java +++ b/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStore.java @@ -26,6 +26,7 @@ import org.apache.qpid.server.queue.AMQQueue; import java.nio.ByteBuffer; import java.util.HashMap; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; /** @@ -33,26 +34,8 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class TestableMemoryMessageStore extends MemoryMessageStore { - - private MemoryMessageStore _mms = null; - private HashMap _messages = new HashMap(); - private AtomicInteger _messageCount = new AtomicInteger(0); - - public TestableMemoryMessageStore(MemoryMessageStore mms) - { - _mms = mms; - } - - public TestableMemoryMessageStore() - { - - } - - @Override - public void close() throws Exception - { - // Not required to do anything - } + private final Map _messages = new HashMap(); + private final AtomicInteger _messageCount = new AtomicInteger(0); @Override public StoredMessage addMessage(StorableMessageMetaData metaData) @@ -65,41 +48,47 @@ public class TestableMemoryMessageStore extends MemoryMessageStore return _messageCount.get(); } + public Map getMessages() + { + return _messages; + } + private class TestableTransaction implements Transaction { + @Override public void enqueueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException { getMessages().put(message.getMessageNumber(), (AMQQueue)queue); } + @Override public void dequeueMessage(TransactionLogResource queue, EnqueableMessage message) throws AMQStoreException { getMessages().remove(message.getMessageNumber()); } + @Override public void commitTran() throws AMQStoreException { } + @Override public StoreFuture commitTranAsync() throws AMQStoreException { - return new StoreFuture() - { - public boolean isComplete() - { - return true; - } - - public void waitForCompletion() - { - - } - }; + return StoreFuture.IMMEDIATE_FUTURE; } public void abortTran() throws AMQStoreException { } + + public void removeXid(long format, byte[] globalId, byte[] branchId) + { + } + + public void recordXid(long format, byte[] globalId, byte[] branchId, Record[] enqueues, Record[] dequeues) + { + } } @@ -109,10 +98,6 @@ public class TestableMemoryMessageStore extends MemoryMessageStore return new TestableTransaction(); } - public HashMap getMessages() - { - return _messages; - } private class TestableStoredMessage implements StoredMessage { diff --git a/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStoreFactory.java b/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStoreFactory.java new file mode 100644 index 0000000000..44070f22ad --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/store/TestableMemoryMessageStoreFactory.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.store; + +public class TestableMemoryMessageStoreFactory implements MessageStoreFactory +{ + + @Override + public MessageStore createMessageStore() + { + return new TestableMemoryMessageStore(); + } + + @Override + public String getStoreClassName() + { + return TestableMemoryMessageStore.class.getSimpleName(); + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java b/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java index 1d6ccfbbc2..5ba9c0c015 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java +++ b/java/broker/src/test/java/org/apache/qpid/server/subscription/MockSubscription.java @@ -277,6 +277,10 @@ public class MockSubscription implements Subscription return false; } + public void queueEmpty() throws AMQException + { + } + public void setActive(final boolean isActive) { _isActive = isActive; diff --git a/java/broker/src/test/java/org/apache/qpid/server/transport/ServerSessionTest.java b/java/broker/src/test/java/org/apache/qpid/server/transport/ServerSessionTest.java new file mode 100644 index 0000000000..d775b0f2f8 --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/transport/ServerSessionTest.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.transport; + +import java.util.UUID; + +import org.apache.qpid.server.configuration.MockConnectionConfig; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.util.InternalBrokerBaseCase; +import org.apache.qpid.server.virtualhost.VirtualHost; +import org.apache.qpid.transport.Binary; + +public class ServerSessionTest extends InternalBrokerBaseCase +{ + + private VirtualHost _virtualHost; + + @Override + public void setUp() throws Exception + { + super.setUp(); + _virtualHost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHosts().iterator().next(); + } + + public void testCompareTo() throws Exception + { + ServerConnection connection = new ServerConnection(1); + connection.setConnectionConfig(createConnectionConfig()); + ServerSession session1 = new ServerSession(connection, new ServerSessionDelegate(), + new Binary(getName().getBytes()), 0 , connection.getConfig()); + + // create a session with the same name but on a different connection + ServerConnection connection2 = new ServerConnection(2); + connection2.setConnectionConfig(createConnectionConfig()); + ServerSession session2 = new ServerSession(connection2, new ServerSessionDelegate(), + new Binary(getName().getBytes()), 0 , connection2.getConfig()); + + assertFalse("Unexpected compare result", session1.compareTo(session2) == 0); + assertEquals("Unexpected compare result", 0, session1.compareTo(session1)); + } + + private MockConnectionConfig createConnectionConfig() + { + return new MockConnectionConfig(UUID.randomUUID(), null, null, + false, 1, _virtualHost, "address", Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, + "authid", "remoteProcessName", new Integer(1967), new Integer(1970), _virtualHost.getConfigStore(), Boolean.FALSE); + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java b/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java index fa0bb5be8b..af49238998 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java +++ b/java/broker/src/test/java/org/apache/qpid/server/txn/MockStoreTransaction.java @@ -20,19 +20,13 @@ */ package org.apache.qpid.server.txn; -import org.apache.commons.configuration.Configuration; import org.apache.commons.lang.NotImplementedException; - import org.apache.qpid.AMQStoreException; -import org.apache.qpid.server.logging.LogSubject; import org.apache.qpid.server.message.EnqueableMessage; import org.apache.qpid.server.store.MessageStore; -import org.apache.qpid.server.store.MessageStore.StoreFuture; -import org.apache.qpid.server.store.MessageStore.Transaction; -import org.apache.qpid.server.store.MessageStoreRecoveryHandler; -import org.apache.qpid.server.store.StorableMessageMetaData; -import org.apache.qpid.server.store.StoredMessage; -import org.apache.qpid.server.store.TransactionLogRecoveryHandler; +import org.apache.qpid.server.store.NullMessageStore; +import org.apache.qpid.server.store.StoreFuture; +import org.apache.qpid.server.store.Transaction; import org.apache.qpid.server.store.TransactionLogResource; /** @@ -112,44 +106,24 @@ class MockStoreTransaction implements Transaction _state = TransactionState.ABORTED; } - public static MessageStore createTestTransactionLog(final MockStoreTransaction storeTransaction) + public void removeXid(long format, byte[] globalId, byte[] branchId) { - return new MessageStore() - { - public void configureMessageStore(final String name, - final MessageStoreRecoveryHandler recoveryHandler, - final Configuration config, - final LogSubject logSubject) throws Exception - { - //TODO. - } - - public void close() throws Exception - { - //TODO. - } - - public StoredMessage addMessage(final T metaData) - { - return null; //TODO. - } - - public boolean isPersistent() - { - return false; //TODO. - } + } - public void configureTransactionLog(String name, TransactionLogRecoveryHandler recoveryHandler, - Configuration storeConfiguration, LogSubject logSubject) throws Exception - { - } + public void recordXid(long format, byte[] globalId, byte[] branchId, Record[] enqueues, Record[] dequeues) + { + } + public static MessageStore createTestTransactionLog(final MockStoreTransaction storeTransaction) + { + return new NullMessageStore() + { + @Override public Transaction newTransaction() { storeTransaction.setState(TransactionState.STARTED); return storeTransaction; } - - }; + }; } } \ No newline at end of file diff --git a/java/broker/src/test/java/org/apache/qpid/server/util/InternalBrokerBaseCase.java b/java/broker/src/test/java/org/apache/qpid/server/util/InternalBrokerBaseCase.java index 9df0aec545..8a34e92985 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/util/InternalBrokerBaseCase.java +++ b/java/broker/src/test/java/org/apache/qpid/server/util/InternalBrokerBaseCase.java @@ -20,6 +20,8 @@ */ package org.apache.qpid.server.util; +import java.util.UUID; + import org.apache.commons.configuration.XMLConfiguration; import org.apache.qpid.AMQException; @@ -43,6 +45,7 @@ import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.store.MessageStore; import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.qpid.server.store.TestableMemoryMessageStoreFactory; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.test.utils.QpidTestCase; @@ -65,10 +68,10 @@ public class InternalBrokerBaseCase extends QpidTestCase super.setUp(); _configXml.addProperty("virtualhosts.virtualhost.name", "test"); - _configXml.addProperty("virtualhosts.virtualhost.test.store.class", TestableMemoryMessageStore.class.getName()); + _configXml.addProperty("virtualhosts.virtualhost.test.store.factoryclass", TestableMemoryMessageStoreFactory.class.getName()); _configXml.addProperty("virtualhosts.virtualhost(-1).name", getName()); - _configXml.addProperty("virtualhosts.virtualhost(-1)."+getName()+".store.class", TestableMemoryMessageStore.class.getName()); + _configXml.addProperty("virtualhosts.virtualhost(-1)."+getName()+".store.factoryclass", TestableMemoryMessageStoreFactory.class.getName()); createBroker(); } diff --git a/java/broker/src/test/java/org/apache/qpid/server/util/MapJsonSerializerTest.java b/java/broker/src/test/java/org/apache/qpid/server/util/MapJsonSerializerTest.java new file mode 100644 index 0000000000..56567523df --- /dev/null +++ b/java/broker/src/test/java/org/apache/qpid/server/util/MapJsonSerializerTest.java @@ -0,0 +1,53 @@ +/* + * + * 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; + +import java.util.HashMap; +import java.util.Map; + +import junit.framework.TestCase; + +public class MapJsonSerializerTest extends TestCase +{ + private MapJsonSerializer _serializer; + + protected void setUp() throws Exception + { + super.setUp(); + _serializer = new MapJsonSerializer(); + + } + + public void testSerializeDeserialize() + { + Map testMap = new HashMap(); + testMap.put("string", "Test String"); + testMap.put("integer", new Integer(10)); + testMap.put("long", new Long(Long.MAX_VALUE)); + testMap.put("boolean", Boolean.TRUE); + + String jsonString = _serializer.serialize(testMap); + Map deserializedMap = _serializer.deserialize(jsonString); + + assertEquals(deserializedMap, testMap); + } + +} diff --git a/java/broker/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java b/java/broker/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java index 5fe664acdd..91174c5d10 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java +++ b/java/broker/src/test/java/org/apache/qpid/server/virtualhost/MockVirtualHost.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.virtualhost; +import java.util.concurrent.ScheduledFuture; import org.apache.qpid.server.binding.BindingFactory; import org.apache.qpid.server.configuration.BrokerConfig; import org.apache.qpid.server.configuration.ConfigStore; @@ -32,13 +33,14 @@ import org.apache.qpid.server.exchange.ExchangeFactory; import org.apache.qpid.server.exchange.ExchangeRegistry; import org.apache.qpid.server.federation.BrokerLink; import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.protocol.v1_0.LinkRegistry; import org.apache.qpid.server.queue.QueueRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; import org.apache.qpid.server.stats.StatisticsCounter; -import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.txn.DtxRegistry; import java.util.Map; import java.util.UUID; @@ -94,17 +96,17 @@ public class MockVirtualHost implements VirtualHost return null; } - public VirtualHostConfiguration getConfiguration() + public DtxRegistry getDtxRegistry() { return null; } - public IConnectionRegistry getConnectionRegistry() + public VirtualHostConfiguration getConfiguration() { return null; } - public DurableConfigurationStore getDurableConfigurationStore() + public IConnectionRegistry getConnectionRegistry() { return null; } @@ -170,6 +172,16 @@ public class MockVirtualHost implements VirtualHost } + public LinkRegistry getLinkRegistry(String remoteContainerId) + { + return null; + } + + public ScheduledFuture scheduleTask(long delay, Runnable timeoutTask) + { + return null; + } + public void scheduleHouseKeepingTask(long period, HouseKeepingTask task) { @@ -269,4 +281,10 @@ public class MockVirtualHost implements VirtualHost { } + + @Override + public State getState() + { + return State.ACTIVE; + } } \ No newline at end of file diff --git a/java/broker/src/test/java/org/apache/qpid/server/virtualhost/VirtualHostImplTest.java b/java/broker/src/test/java/org/apache/qpid/server/virtualhost/VirtualHostImplTest.java index df7b4da426..87eb0f9d16 100644 --- a/java/broker/src/test/java/org/apache/qpid/server/virtualhost/VirtualHostImplTest.java +++ b/java/broker/src/test/java/org/apache/qpid/server/virtualhost/VirtualHostImplTest.java @@ -27,7 +27,7 @@ import org.apache.qpid.server.configuration.ServerConfiguration; import org.apache.qpid.server.exchange.Exchange; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.registry.ApplicationRegistry; -import org.apache.qpid.server.store.TestableMemoryMessageStore; +import org.apache.qpid.server.store.MemoryMessageStoreFactory; import org.apache.qpid.server.util.TestApplicationRegistry; import org.apache.qpid.test.utils.QpidTestCase; @@ -68,31 +68,6 @@ public class VirtualHostImplTest extends QpidTestCase customBindingTestImpl(new String[0]); } - private void customBindingTestImpl(final String[] routingKeys) throws Exception - { - String exchangeName = getName() +".direct"; - String vhostName = getName(); - String queueName = getName(); - - File config = writeConfigFile(vhostName, queueName, exchangeName, false, routingKeys); - VirtualHost vhost = createVirtualHost(vhostName, config); - assertNotNull("virtualhost should exist", vhost); - - AMQQueue queue = vhost.getQueueRegistry().getQueue(queueName); - assertNotNull("queue should exist", queue); - - Exchange defaultExch = vhost.getExchangeRegistry().getDefaultExchange(); - assertTrue("queue should have been bound to default exchange with its name", defaultExch.isBound(queueName, queue)); - - Exchange exch = vhost.getExchangeRegistry().getExchange(exchangeName); - assertTrue("queue should have been bound to " + exchangeName + " with its name", exch.isBound(queueName, queue)); - - for(String key: routingKeys) - { - assertTrue("queue should have been bound to " + exchangeName + " with key " + key, exch.isBound(key, queue)); - } - } - /** * Tests that specifying custom routing keys for a queue in the configuration file results in failure * to create the vhost (since this is illegal, only queue names are used with the default exchange) @@ -106,12 +81,32 @@ public class VirtualHostImplTest extends QpidTestCase createVirtualHost(getName(), config); fail("virtualhost creation should have failed due to illegal configuration"); } - catch (ConfigurationException e) + catch (RuntimeException e) { + assertEquals(ConfigurationException.class, e.getCause().getClass()); //expected } } + public void testVirtualHostBecomesActive() throws Exception + { + File config = writeConfigFile(getName(), getName(), getName() +".direct", false, new String[0]); + VirtualHost vhost = createVirtualHost(getName(), config); + assertNotNull(vhost); + assertEquals(State.ACTIVE, vhost.getState()); + } + + public void testVirtualHostBecomesStoppedOnClose() throws Exception + { + File config = writeConfigFile(getName(), getName(), getName() +".direct", false, new String[0]); + VirtualHost vhost = createVirtualHost(getName(), config); + assertNotNull(vhost); + assertEquals(State.ACTIVE, vhost.getState()); + vhost.close(); + assertEquals(State.STOPPED, vhost.getState()); + assertEquals(0, vhost.getHouseKeepingActiveCount()); + } + /** * Tests that specifying an unknown exchange to bind the queue to results in failure to create the vhost */ @@ -124,12 +119,39 @@ public class VirtualHostImplTest extends QpidTestCase createVirtualHost(getName(), config); fail("virtualhost creation should have failed due to illegal configuration"); } - catch (ConfigurationException e) + catch (RuntimeException e) { + assertEquals(ConfigurationException.class, e.getCause().getClass()); //expected } } + private void customBindingTestImpl(final String[] routingKeys) throws Exception + { + String exchangeName = getName() +".direct"; + String vhostName = getName(); + String queueName = getName(); + + File config = writeConfigFile(vhostName, queueName, exchangeName, false, routingKeys); + VirtualHost vhost = createVirtualHost(vhostName, config); + assertNotNull("virtualhost should exist", vhost); + + AMQQueue queue = vhost.getQueueRegistry().getQueue(queueName); + assertNotNull("queue should exist", queue); + + Exchange defaultExch = vhost.getExchangeRegistry().getDefaultExchange(); + assertTrue("queue should have been bound to default exchange with its name", defaultExch.isBound(queueName, queue)); + + Exchange exch = vhost.getExchangeRegistry().getExchange(exchangeName); + assertTrue("queue should have been bound to " + exchangeName + " with its name", exch.isBound(queueName, queue)); + + for(String key: routingKeys) + { + assertTrue("queue should have been bound to " + exchangeName + " with key " + key, exch.isBound(key, queue)); + } + } + + private VirtualHost createVirtualHost(String vhostName, File config) throws Exception { _configuration = new ServerConfiguration(new XMLConfiguration(config)); @@ -167,11 +189,11 @@ public class VirtualHostImplTest extends QpidTestCase writer.write(""); writer.write(" " + vhostName + ""); writer.write(" "); - writer.write(" "); - writer.write(" " + TestableMemoryMessageStore.class.getName() + ""); - writer.write(" "); writer.write(" " + vhostName + ""); writer.write(" <" + vhostName + ">"); + writer.write(" "); + writer.write(" " + MemoryMessageStoreFactory.class.getName() + ""); + writer.write(" "); if(exchangeName != null && !dontDeclare) { writer.write(" "); -- cgit v1.2.1