summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Gemmell <robbie@apache.org>2012-06-28 09:14:52 +0000
committerRobert Gemmell <robbie@apache.org>2012-06-28 09:14:52 +0000
commit01b8ef15ad7826f42790868d94402332596a4403 (patch)
tree65ea28a9eb5fcb556249a5ae6702f6c469727947
parent15874225f863757fde6c078da7079592c534b60b (diff)
parent83c220a465faba7fa61c44dd69e7aef497083553 (diff)
downloadqpid-python-01b8ef15ad7826f42790868d94402332596a4403.tar.gz
merge non-java changes from trunk up to r1353860
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-config-and-management@1354874 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--.gitignore7
-rwxr-xr-xqpid/bin/install-cpp-python76
-rw-r--r--qpid/cpp/bindings/qmf/ruby/qmf.rb26
-rw-r--r--qpid/cpp/bindings/qpid/python/python.i166
-rw-r--r--qpid/cpp/bindings/qpid/ruby/LICENSE29
-rw-r--r--qpid/cpp/bindings/qpid/ruby/README.rdoc2
-rw-r--r--qpid/cpp/bindings/qpid/ruby/Rakefile4
-rw-r--r--qpid/cpp/bindings/qpid/ruby/features/creating_a_receiver.feature4
-rw-r--r--qpid/cpp/bindings/qpid/ruby/features/creating_a_sender.feature6
-rw-r--r--qpid/cpp/bindings/qpid/ruby/features/receiving_a_message.feature10
-rw-r--r--qpid/cpp/bindings/qpid/ruby/features/sending_a_message.feature6
-rw-r--r--qpid/cpp/bindings/qpid/ruby/features/step_definitions/address_steps.rb26
-rw-r--r--qpid/cpp/bindings/qpid/ruby/features/step_definitions/connection_steps.rb19
-rw-r--r--qpid/cpp/bindings/qpid/ruby/features/step_definitions/receiver_steps.rb27
-rw-r--r--qpid/cpp/bindings/qpid/ruby/features/step_definitions/sender_steps.rb19
-rw-r--r--qpid/cpp/bindings/qpid/ruby/features/step_definitions/session_steps.rb19
-rw-r--r--qpid/cpp/bindings/qpid/ruby/lib/qpid/encoding.rb6
-rw-r--r--qpid/cpp/bindings/qpid/ruby/lib/qpid/version.rb2
-rw-r--r--qpid/cpp/bindings/swig_python_typemaps.i6
-rw-r--r--qpid/cpp/configure.ac44
-rw-r--r--qpid/cpp/etc/Makefile.am25
-rw-r--r--qpid/cpp/etc/cluster.conf-example.xml.in (renamed from qpid/cpp/etc/cluster.conf-example.xml)57
-rwxr-xr-xqpid/cpp/etc/qpidd-primary.in (renamed from qpid/cpp/etc/qpidd-primary)8
-rwxr-xr-xqpid/cpp/etc/qpidd.in (renamed from qpid/cpp/etc/qpidd)17
-rw-r--r--qpid/cpp/include/qpid/Options.h13
-rw-r--r--qpid/cpp/include/qpid/RangeSet.h10
-rw-r--r--qpid/cpp/include/qpid/Url.h2
-rw-r--r--qpid/cpp/include/qpid/client/SessionBase_0_10.h13
-rw-r--r--qpid/cpp/include/qpid/client/SubscriptionSettings.h14
-rw-r--r--qpid/cpp/include/qpid/framing/Array.h22
-rw-r--r--qpid/cpp/include/qpid/framing/Buffer.h50
-rw-r--r--qpid/cpp/include/qpid/framing/FieldTable.h20
-rw-r--r--qpid/cpp/include/qpid/framing/ProtocolVersion.h2
-rw-r--r--qpid/cpp/include/qpid/framing/SequenceNumber.h12
-rw-r--r--qpid/cpp/include/qpid/framing/StructHelper.h2
-rw-r--r--qpid/cpp/include/qpid/framing/Uuid.h3
-rw-r--r--qpid/cpp/include/qpid/framing/amqp_types.h2
-rw-r--r--qpid/cpp/include/qpid/log/Logger.h2
-rw-r--r--qpid/cpp/include/qpid/log/Options.h2
-rw-r--r--qpid/cpp/include/qpid/log/Selector.h27
-rw-r--r--qpid/cpp/include/qpid/log/Statement.h169
-rw-r--r--qpid/cpp/include/qpid/management/Buffer.h3
-rw-r--r--qpid/cpp/include/qpid/sys/SystemInfo.h88
-rw-r--r--qpid/cpp/managementgen/qmfgen/management-types.xml2
-rwxr-xr-xqpid/cpp/managementgen/qmfgen/schema.py12
-rw-r--r--qpid/cpp/managementgen/qmfgen/templates/Class.cpp1
-rwxr-xr-xqpid/cpp/rubygen/framing.0-10/structs.rb26
-rw-r--r--qpid/cpp/src/CMakeLists.txt83
-rw-r--r--qpid/cpp/src/Makefile.am12
-rw-r--r--qpid/cpp/src/acl.mk2
-rw-r--r--qpid/cpp/src/ha.mk31
-rw-r--r--qpid/cpp/src/posix/QpiddBroker.cpp27
-rw-r--r--qpid/cpp/src/qmf/AgentSession.cpp25
-rw-r--r--qpid/cpp/src/qmf/AgentSessionImpl.h73
-rw-r--r--qpid/cpp/src/qmf/ConsoleSessionImpl.h2
-rw-r--r--qpid/cpp/src/qmf/EventNotifierImpl.cpp4
-rw-r--r--qpid/cpp/src/qmf/PrivateImplRef.h8
-rw-r--r--qpid/cpp/src/qpid/Options.cpp7
-rw-r--r--qpid/cpp/src/qpid/RefCounted.h12
-rw-r--r--qpid/cpp/src/qpid/UrlArray.cpp2
-rw-r--r--qpid/cpp/src/qpid/UrlArray.h1
-rw-r--r--qpid/cpp/src/qpid/acl/Acl.cpp116
-rw-r--r--qpid/cpp/src/qpid/acl/Acl.h99
-rw-r--r--qpid/cpp/src/qpid/acl/AclConnectionCounter.cpp324
-rw-r--r--qpid/cpp/src/qpid/acl/AclConnectionCounter.h101
-rw-r--r--qpid/cpp/src/qpid/acl/AclData.cpp14
-rw-r--r--qpid/cpp/src/qpid/acl/AclPlugin.cpp8
-rw-r--r--qpid/cpp/src/qpid/acl/AclReader.cpp26
-rw-r--r--qpid/cpp/src/qpid/acl/management-schema.xml49
-rw-r--r--qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp11
-rw-r--r--qpid/cpp/src/qpid/broker/AclModule.h25
-rw-r--r--qpid/cpp/src/qpid/broker/Bridge.cpp160
-rw-r--r--qpid/cpp/src/qpid/broker/Bridge.h63
-rw-r--r--qpid/cpp/src/qpid/broker/Broker.cpp237
-rw-r--r--qpid/cpp/src/qpid/broker/Broker.h46
-rw-r--r--qpid/cpp/src/qpid/broker/ConfigurationObserver.h61
-rw-r--r--qpid/cpp/src/qpid/broker/ConfigurationObservers.h72
-rw-r--r--qpid/cpp/src/qpid/broker/Connection.cpp62
-rw-r--r--qpid/cpp/src/qpid/broker/Connection.h19
-rw-r--r--qpid/cpp/src/qpid/broker/ConnectionFactory.cpp11
-rw-r--r--qpid/cpp/src/qpid/broker/ConnectionHandler.cpp24
-rw-r--r--qpid/cpp/src/qpid/broker/ConnectionHandler.h4
-rw-r--r--qpid/cpp/src/qpid/broker/ConnectionObservers.h28
-rw-r--r--qpid/cpp/src/qpid/broker/Daemon.h1
-rw-r--r--qpid/cpp/src/qpid/broker/DirectExchange.cpp3
-rw-r--r--qpid/cpp/src/qpid/broker/DtxManager.cpp29
-rw-r--r--qpid/cpp/src/qpid/broker/DtxManager.h3
-rw-r--r--qpid/cpp/src/qpid/broker/DtxWorkRecord.cpp11
-rw-r--r--qpid/cpp/src/qpid/broker/Exchange.cpp11
-rw-r--r--qpid/cpp/src/qpid/broker/ExchangeRegistry.cpp77
-rw-r--r--qpid/cpp/src/qpid/broker/Fairshare.cpp1
-rw-r--r--qpid/cpp/src/qpid/broker/FanOutExchange.cpp3
-rw-r--r--qpid/cpp/src/qpid/broker/HeadersExchange.cpp3
-rw-r--r--qpid/cpp/src/qpid/broker/LegacyLVQ.cpp29
-rw-r--r--qpid/cpp/src/qpid/broker/LegacyLVQ.h1
-rw-r--r--qpid/cpp/src/qpid/broker/Link.cpp388
-rw-r--r--qpid/cpp/src/qpid/broker/Link.h83
-rw-r--r--qpid/cpp/src/qpid/broker/LinkRegistry.cpp273
-rw-r--r--qpid/cpp/src/qpid/broker/LinkRegistry.h62
-rw-r--r--qpid/cpp/src/qpid/broker/Message.cpp27
-rw-r--r--qpid/cpp/src/qpid/broker/Message.h1
-rw-r--r--qpid/cpp/src/qpid/broker/MessageDeque.cpp61
-rw-r--r--qpid/cpp/src/qpid/broker/MessageDeque.h9
-rw-r--r--qpid/cpp/src/qpid/broker/MessageGroupManager.cpp92
-rw-r--r--qpid/cpp/src/qpid/broker/MessageGroupManager.h19
-rw-r--r--qpid/cpp/src/qpid/broker/MessageMap.cpp85
-rw-r--r--qpid/cpp/src/qpid/broker/MessageMap.h5
-rw-r--r--qpid/cpp/src/qpid/broker/Messages.h9
-rw-r--r--qpid/cpp/src/qpid/broker/NameGenerator.h1
-rw-r--r--qpid/cpp/src/qpid/broker/Observers.h69
-rw-r--r--qpid/cpp/src/qpid/broker/PriorityQueue.cpp122
-rw-r--r--qpid/cpp/src/qpid/broker/PriorityQueue.h28
-rw-r--r--qpid/cpp/src/qpid/broker/Queue.cpp637
-rw-r--r--qpid/cpp/src/qpid/broker/Queue.h53
-rw-r--r--qpid/cpp/src/qpid/broker/QueueFlowLimit.cpp4
-rw-r--r--qpid/cpp/src/qpid/broker/QueueListeners.cpp4
-rw-r--r--qpid/cpp/src/qpid/broker/QueueListeners.h7
-rw-r--r--qpid/cpp/src/qpid/broker/QueuePolicy.cpp4
-rw-r--r--qpid/cpp/src/qpid/broker/QueueRegistry.cpp57
-rw-r--r--qpid/cpp/src/qpid/broker/QueuedMessage.cpp (renamed from qpid/cpp/src/qpid/ha/ConnectionExcluder.cpp)22
-rw-r--r--qpid/cpp/src/qpid/broker/QueuedMessage.h3
-rw-r--r--qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp8
-rw-r--r--qpid/cpp/src/qpid/broker/SaslAuthenticator.cpp79
-rw-r--r--qpid/cpp/src/qpid/broker/SaslAuthenticator.h2
-rw-r--r--qpid/cpp/src/qpid/broker/SecureConnectionFactory.cpp11
-rw-r--r--qpid/cpp/src/qpid/broker/SemanticState.cpp12
-rw-r--r--qpid/cpp/src/qpid/broker/SemanticState.h4
-rw-r--r--qpid/cpp/src/qpid/broker/SessionAdapter.cpp60
-rw-r--r--qpid/cpp/src/qpid/broker/SessionAdapter.h7
-rw-r--r--qpid/cpp/src/qpid/broker/SessionContext.h1
-rw-r--r--qpid/cpp/src/qpid/broker/SessionHandler.cpp31
-rw-r--r--qpid/cpp/src/qpid/broker/SessionHandler.h25
-rw-r--r--qpid/cpp/src/qpid/broker/SessionState.cpp2
-rw-r--r--qpid/cpp/src/qpid/broker/SessionState.h5
-rw-r--r--qpid/cpp/src/qpid/broker/System.cpp10
-rw-r--r--qpid/cpp/src/qpid/broker/System.h17
-rw-r--r--qpid/cpp/src/qpid/broker/TopicExchange.cpp369
-rw-r--r--qpid/cpp/src/qpid/broker/TopicExchange.h126
-rw-r--r--qpid/cpp/src/qpid/broker/TopicKeyNode.h371
-rw-r--r--qpid/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp3
-rw-r--r--qpid/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp53
-rw-r--r--qpid/cpp/src/qpid/client/ConnectionHandler.cpp16
-rw-r--r--qpid/cpp/src/qpid/client/ConnectionImpl.cpp4
-rw-r--r--qpid/cpp/src/qpid/client/LoadPlugins.cpp6
-rw-r--r--qpid/cpp/src/qpid/client/PrivateImplRef.h8
-rw-r--r--qpid/cpp/src/qpid/client/SessionImpl.cpp2
-rw-r--r--qpid/cpp/src/qpid/client/SslConnector.cpp7
-rw-r--r--qpid/cpp/src/qpid/client/TCPConnector.cpp1
-rw-r--r--qpid/cpp/src/qpid/client/TCPConnector.h2
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp1
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp2
-rw-r--r--qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h1
-rw-r--r--qpid/cpp/src/qpid/cluster/Cluster.cpp28
-rw-r--r--qpid/cpp/src/qpid/cluster/ClusterMap.cpp4
-rw-r--r--qpid/cpp/src/qpid/cluster/ClusterTimer.cpp2
-rw-r--r--qpid/cpp/src/qpid/cluster/Connection.cpp76
-rw-r--r--qpid/cpp/src/qpid/cluster/Connection.h12
-rw-r--r--qpid/cpp/src/qpid/cluster/ConnectionCodec.cpp14
-rw-r--r--qpid/cpp/src/qpid/cluster/Cpg.cpp54
-rw-r--r--qpid/cpp/src/qpid/cluster/Cpg.h10
-rw-r--r--qpid/cpp/src/qpid/cluster/InitialStatusMap.cpp2
-rw-r--r--qpid/cpp/src/qpid/cluster/UpdateClient.cpp16
-rw-r--r--qpid/cpp/src/qpid/cluster/types.h23
-rw-r--r--qpid/cpp/src/qpid/console/ClassKey.cpp1
-rw-r--r--qpid/cpp/src/qpid/framing/AMQCommandControlBody.h70
-rw-r--r--qpid/cpp/src/qpid/framing/AMQContentBody.cpp2
-rw-r--r--qpid/cpp/src/qpid/framing/AMQContentBody.h8
-rw-r--r--qpid/cpp/src/qpid/framing/AMQFrame.cpp4
-rw-r--r--qpid/cpp/src/qpid/framing/BodyHandler.cpp56
-rw-r--r--qpid/cpp/src/qpid/framing/Buffer.cpp16
-rw-r--r--qpid/cpp/src/qpid/framing/FieldTable.cpp82
-rw-r--r--qpid/cpp/src/qpid/framing/FrameSet.cpp5
-rw-r--r--qpid/cpp/src/qpid/framing/FrameSet.h1
-rw-r--r--qpid/cpp/src/qpid/framing/ProtocolInitiation.cpp2
-rw-r--r--qpid/cpp/src/qpid/framing/Uuid.cpp7
-rw-r--r--qpid/cpp/src/qpid/framing/amqp_framing.h1
-rw-r--r--qpid/cpp/src/qpid/ha/Backup.cpp79
-rw-r--r--qpid/cpp/src/qpid/ha/Backup.h10
-rw-r--r--qpid/cpp/src/qpid/ha/BackupConnectionExcluder.h (renamed from qpid/cpp/src/qpid/framing/BodyHandler.h)45
-rw-r--r--qpid/cpp/src/qpid/ha/BrokerInfo.cpp118
-rw-r--r--qpid/cpp/src/qpid/ha/BrokerInfo.h84
-rw-r--r--qpid/cpp/src/qpid/ha/BrokerReplicator.cpp359
-rw-r--r--qpid/cpp/src/qpid/ha/BrokerReplicator.h28
-rw-r--r--qpid/cpp/src/qpid/ha/ConnectionObserver.cpp83
-rw-r--r--qpid/cpp/src/qpid/ha/ConnectionObserver.h74
-rw-r--r--qpid/cpp/src/qpid/ha/Counter.h (renamed from qpid/cpp/src/qpid/ha/ConnectionExcluder.h)37
-rw-r--r--qpid/cpp/src/qpid/ha/HaBroker.cpp290
-rw-r--r--qpid/cpp/src/qpid/ha/HaBroker.h73
-rw-r--r--qpid/cpp/src/qpid/ha/HaPlugin.cpp26
-rw-r--r--qpid/cpp/src/qpid/ha/Membership.cpp81
-rw-r--r--qpid/cpp/src/qpid/ha/Membership.h65
-rw-r--r--qpid/cpp/src/qpid/ha/Primary.cpp189
-rw-r--r--qpid/cpp/src/qpid/ha/Primary.h106
-rw-r--r--qpid/cpp/src/qpid/ha/QueueGuard.cpp140
-rw-r--r--qpid/cpp/src/qpid/ha/QueueGuard.h109
-rw-r--r--qpid/cpp/src/qpid/ha/QueueRange.h71
-rw-r--r--qpid/cpp/src/qpid/ha/QueueReplicator.cpp142
-rw-r--r--qpid/cpp/src/qpid/ha/QueueReplicator.h18
-rw-r--r--qpid/cpp/src/qpid/ha/RemoteBackup.cpp106
-rw-r--r--qpid/cpp/src/qpid/ha/RemoteBackup.h92
-rw-r--r--qpid/cpp/src/qpid/ha/ReplicatingSubscription.cpp373
-rw-r--r--qpid/cpp/src/qpid/ha/ReplicatingSubscription.h104
-rw-r--r--qpid/cpp/src/qpid/ha/ReplicationTest.cpp75
-rw-r--r--qpid/cpp/src/qpid/ha/ReplicationTest.h67
-rw-r--r--qpid/cpp/src/qpid/ha/Settings.h7
-rw-r--r--qpid/cpp/src/qpid/ha/management-schema.xml32
-rw-r--r--qpid/cpp/src/qpid/ha/types.cpp80
-rw-r--r--qpid/cpp/src/qpid/ha/types.h109
-rw-r--r--qpid/cpp/src/qpid/log/Logger.cpp7
-rw-r--r--qpid/cpp/src/qpid/log/Options.cpp18
-rw-r--r--qpid/cpp/src/qpid/log/Selector.cpp31
-rw-r--r--qpid/cpp/src/qpid/log/Statement.cpp61
-rw-r--r--qpid/cpp/src/qpid/log/posix/SinkOptions.cpp8
-rw-r--r--qpid/cpp/src/qpid/management/Buffer.cpp3
-rw-r--r--qpid/cpp/src/qpid/management/ManagementAgent.cpp27
-rw-r--r--qpid/cpp/src/qpid/management/ManagementAgent.h1
-rw-r--r--qpid/cpp/src/qpid/management/ManagementDirectExchange.cpp4
-rw-r--r--qpid/cpp/src/qpid/management/ManagementTopicExchange.cpp6
-rw-r--r--qpid/cpp/src/qpid/messaging/PrivateImplRef.h8
-rw-r--r--qpid/cpp/src/qpid/replication/ReplicatingEventListener.cpp2
-rw-r--r--qpid/cpp/src/qpid/replication/ReplicationExchange.cpp10
-rw-r--r--qpid/cpp/src/qpid/store/MessageStorePlugin.cpp6
-rw-r--r--qpid/cpp/src/qpid/store/ms-clfs/MessageLog.cpp8
-rw-r--r--qpid/cpp/src/qpid/store/ms-clfs/Transaction.h1
-rw-r--r--qpid/cpp/src/qpid/store/ms-clfs/TransactionLog.cpp8
-rw-r--r--qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp36
-rw-r--r--qpid/cpp/src/qpid/sys/AsynchIOHandler.h9
-rw-r--r--qpid/cpp/src/qpid/sys/MemStat.cpp (renamed from qpid/cpp/src/qpid/sys/windows/MemStat.cpp)4
-rw-r--r--qpid/cpp/src/qpid/sys/SslPlugin.cpp28
-rw-r--r--qpid/cpp/src/qpid/sys/TCPIOPlugin.cpp21
-rw-r--r--qpid/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp1
-rw-r--r--qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp8
-rwxr-xr-xqpid/cpp/src/qpid/sys/posix/LockFile.cpp2
-rw-r--r--qpid/cpp/src/qpid/sys/posix/MemStat.cpp2
-rw-r--r--qpid/cpp/src/qpid/sys/posix/PollableCondition.cpp5
-rw-r--r--qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp6
-rwxr-xr-xqpid/cpp/src/qpid/sys/posix/SystemInfo.cpp104
-rw-r--r--qpid/cpp/src/qpid/sys/ssl/SslHandler.cpp41
-rw-r--r--qpid/cpp/src/qpid/sys/ssl/SslHandler.h9
-rw-r--r--qpid/cpp/src/qpid/sys/ssl/SslIo.cpp19
-rw-r--r--qpid/cpp/src/qpid/sys/ssl/SslIo.h3
-rw-r--r--qpid/cpp/src/qpid/sys/unordered_map.h2
-rw-r--r--qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp100
-rwxr-xr-xqpid/cpp/src/qpid/sys/windows/IocpPoller.cpp5
-rw-r--r--qpid/cpp/src/qpid/sys/windows/PollableCondition.cpp5
-rw-r--r--qpid/cpp/src/qpid/sys/windows/Socket.cpp27
-rw-r--r--qpid/cpp/src/qpid/sys/windows/SslAsynchIO.cpp8
-rwxr-xr-xqpid/cpp/src/qpid/sys/windows/SystemInfo.cpp10
-rw-r--r--qpid/cpp/src/qpid/xml/XmlExchange.cpp34
-rw-r--r--qpid/cpp/src/qpid/xml/XmlExchange.h4
-rw-r--r--qpid/cpp/src/qpidd.cpp4
-rw-r--r--qpid/cpp/src/qpidd.h4
-rw-r--r--qpid/cpp/src/tests/Array.cpp4
-rw-r--r--qpid/cpp/src/tests/CMakeLists.txt16
-rw-r--r--qpid/cpp/src/tests/ExchangeTest.cpp2
-rw-r--r--qpid/cpp/src/tests/FieldTable.cpp13
-rw-r--r--qpid/cpp/src/tests/Frame.cpp1
-rw-r--r--qpid/cpp/src/tests/FramingTest.cpp1
-rw-r--r--qpid/cpp/src/tests/Makefile.am32
-rw-r--r--qpid/cpp/src/tests/MessageBuilderTest.cpp6
-rw-r--r--qpid/cpp/src/tests/MessageTest.cpp9
-rw-r--r--qpid/cpp/src/tests/MessageUtils.h4
-rw-r--r--qpid/cpp/src/tests/MessagingSessionTests.cpp7
-rw-r--r--qpid/cpp/src/tests/QueueFlowLimitTest.cpp1
-rw-r--r--qpid/cpp/src/tests/QueueTest.cpp244
-rw-r--r--qpid/cpp/src/tests/RangeSet.cpp134
-rw-r--r--qpid/cpp/src/tests/RefCounted.cpp6
-rw-r--r--qpid/cpp/src/tests/ReplicationTest.cpp4
-rw-r--r--qpid/cpp/src/tests/SessionState.cpp1
-rw-r--r--qpid/cpp/src/tests/StringUtils.cpp6
-rw-r--r--qpid/cpp/src/tests/SystemInfo.cpp52
-rw-r--r--qpid/cpp/src/tests/TestMessageStore.h2
-rw-r--r--qpid/cpp/src/tests/TimerTest.cpp2
-rw-r--r--qpid/cpp/src/tests/TopicExchangeTest.cpp14
-rw-r--r--qpid/cpp/src/tests/TxPublishTest.cpp4
-rw-r--r--qpid/cpp/src/tests/Uuid.cpp14
-rwxr-xr-xqpid/cpp/src/tests/acl.py327
-rw-r--r--qpid/cpp/src/tests/amqp_0_10/Map.cpp98
-rw-r--r--qpid/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp49
-rw-r--r--qpid/cpp/src/tests/amqp_0_10/apply.cpp99
-rw-r--r--qpid/cpp/src/tests/amqp_0_10/handlers.cpp125
-rw-r--r--qpid/cpp/src/tests/amqp_0_10/serialize.cpp429
-rw-r--r--qpid/cpp/src/tests/brokertest.py93
-rwxr-xr-xqpid/cpp/src/tests/cli_tests.py3
-rw-r--r--qpid/cpp/src/tests/cluster.cmake2
-rw-r--r--qpid/cpp/src/tests/cluster.mk11
-rwxr-xr-xqpid/cpp/src/tests/cluster_failover9
-rwxr-xr-xqpid/cpp/src/tests/cluster_python_tests3
-rwxr-xr-xqpid/cpp/src/tests/cluster_read_credit4
-rwxr-xr-xqpid/cpp/src/tests/cluster_test_logs.py6
-rwxr-xr-xqpid/cpp/src/tests/cluster_tests.py229
-rwxr-xr-xqpid/cpp/src/tests/clustered_replication_test17
-rwxr-xr-xqpid/cpp/src/tests/cpg_check.sh.in (renamed from qpid/cpp/src/tests/ais_check)18
-rwxr-xr-xqpid/cpp/src/tests/federated_cluster_test23
-rwxr-xr-xqpid/cpp/src/tests/federation.py467
-rwxr-xr-xqpid/cpp/src/tests/ha_tests.py660
-rw-r--r--qpid/cpp/src/tests/install_env.sh.in2
-rwxr-xr-xqpid/cpp/src/tests/ipv6_test3
-rw-r--r--qpid/cpp/src/tests/logging.cpp12
-rwxr-xr-xqpid/cpp/src/tests/qpid-cluster-benchmark29
-rwxr-xr-xqpid/cpp/src/tests/qpid-cpp-benchmark69
-rw-r--r--qpid/cpp/src/tests/qpid-latency-test.cpp18
-rw-r--r--qpid/cpp/src/tests/qpid-ping.cpp27
-rw-r--r--qpid/cpp/src/tests/qpid-receive.cpp5
-rw-r--r--qpid/cpp/src/tests/qpid-send.cpp67
-rw-r--r--qpid/cpp/src/tests/qpid-txtest.cpp5
-rw-r--r--qpid/cpp/src/tests/qpidd-empty.conf3
-rwxr-xr-xqpid/cpp/src/tests/run_acl_tests22
-rwxr-xr-xqpid/cpp/src/tests/run_cluster_authentication_soak3
-rwxr-xr-xqpid/cpp/src/tests/run_cluster_authentication_test3
-rwxr-xr-xqpid/cpp/src/tests/run_cluster_test3
-rwxr-xr-xqpid/cpp/src/tests/run_cluster_tests4
-rwxr-xr-xqpid/cpp/src/tests/run_failover_soak3
-rwxr-xr-xqpid/cpp/src/tests/run_federation_sys_tests8
-rwxr-xr-xqpid/cpp/src/tests/run_federation_tests15
-rwxr-xr-xqpid/cpp/src/tests/run_ha_tests (renamed from qpid/doc/book/build.sh)25
-rw-r--r--qpid/cpp/src/tests/sasl.mk21
-rwxr-xr-xqpid/cpp/src/tests/sasl_fed_ex_dynamic_cluster4
-rwxr-xr-xqpid/cpp/src/tests/sasl_fed_ex_link_cluster3
-rwxr-xr-xqpid/cpp/src/tests/sasl_fed_ex_queue_cluster3
-rwxr-xr-xqpid/cpp/src/tests/sasl_fed_ex_route_cluster3
-rwxr-xr-xqpid/cpp/src/tests/sasl_test_setup.sh2
-rwxr-xr-xqpid/cpp/src/tests/ssl_test7
-rwxr-xr-xqpid/cpp/src/tests/start_cluster3
-rw-r--r--qpid/cpp/src/tests/test_env.sh.in1
-rw-r--r--qpid/cpp/src/tests/testagent.cpp11
-rw-r--r--qpid/cpp/src/tests/txjob.cpp6
-rw-r--r--qpid/cpp/src/tests/txshift.cpp2
-rw-r--r--qpid/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp6
-rw-r--r--qpid/cpp/src/windows/QpiddBroker.cpp12
-rw-r--r--qpid/cpp/src/windows/SCM.cpp664
-rw-r--r--qpid/cpp/xml/cluster.xml7
-rw-r--r--qpid/doc/book/Makefile37
-rw-r--r--qpid/doc/book/README.txt118
-rwxr-xr-xqpid/doc/book/build-book.sh63
-rw-r--r--qpid/doc/book/build.xml193
-rw-r--r--qpid/doc/book/src/AMQP-Messaging-Broker-Java.xml72
-rw-r--r--qpid/doc/book/src/Active-Passive-Cluster.xml361
-rw-r--r--qpid/doc/book/src/Makefile.inc63
-rw-r--r--qpid/doc/book/src/amqp-advanced-message-queueing-protocol.html237
-rw-r--r--qpid/doc/book/src/common/css/style.css279
-rw-r--r--qpid/doc/book/src/cpp-broker/AMQP-Compatibility.xml (renamed from qpid/doc/book/src/AMQP-Compatibility.xml)0
-rw-r--r--qpid/doc/book/src/cpp-broker/AMQP-Messaging-Broker-CPP-Book.xml (renamed from qpid/doc/book/src/AMQP-Messaging-Broker-CPP-Book.xml)0
-rw-r--r--qpid/doc/book/src/cpp-broker/Active-Active-Cluster.xml (renamed from qpid/doc/book/src/Active-Active-Cluster.xml)0
-rw-r--r--qpid/doc/book/src/cpp-broker/Active-Passive-Cluster.xml672
-rw-r--r--qpid/doc/book/src/cpp-broker/Cheat-Sheet-for-configuring-Exchange-Options.xml (renamed from qpid/doc/book/src/Cheat-Sheet-for-configuring-Exchange-Options.xml)0
-rw-r--r--qpid/doc/book/src/cpp-broker/Cheat-Sheet-for-configuring-Queue-Options.xml (renamed from qpid/doc/book/src/Cheat-Sheet-for-configuring-Queue-Options.xml)0
-rw-r--r--qpid/doc/book/src/cpp-broker/HA-Queue-Replication.xml (renamed from qpid/doc/book/src/HA-Queue-Replication.xml)0
-rw-r--r--qpid/doc/book/src/cpp-broker/LVQ.xml (renamed from qpid/doc/book/src/LVQ.xml)0
-rw-r--r--[-rwxr-xr-x]qpid/doc/book/src/cpp-broker/Makefile (renamed from qpid/doc/book/build-chapter.sh)26
-rw-r--r--qpid/doc/book/src/cpp-broker/Managing-CPP-Broker.xml (renamed from qpid/doc/book/src/Managing-CPP-Broker.xml)0
-rw-r--r--qpid/doc/book/src/cpp-broker/QMF-Python-Console-Tutorial.xml (renamed from qpid/doc/book/src/QMF-Python-Console-Tutorial.xml)0
-rw-r--r--qpid/doc/book/src/cpp-broker/Qpid-Interoperability-Documentation.xml (renamed from qpid/doc/book/src/Qpid-Interoperability-Documentation.xml)0
-rw-r--r--qpid/doc/book/src/cpp-broker/Qpid-Management-Framework.xml (renamed from qpid/doc/book/src/Qpid-Management-Framework.xml)0
-rw-r--r--qpid/doc/book/src/cpp-broker/Running-CPP-Broker.xml (renamed from qpid/doc/book/src/Running-CPP-Broker.xml)0
-rw-r--r--qpid/doc/book/src/cpp-broker/Security.xml (renamed from qpid/doc/book/src/Security.xml)431
-rw-r--r--qpid/doc/book/src/cpp-broker/Using-Broker-Federation.xml (renamed from qpid/doc/book/src/Using-Broker-Federation.xml)0
-rw-r--r--qpid/doc/book/src/cpp-broker/Using-message-groups.xml (renamed from qpid/doc/book/src/Using-message-groups.xml)0
-rw-r--r--qpid/doc/book/src/cpp-broker/producer-flow-control.xml (renamed from qpid/doc/book/src/producer-flow-control.xml)0
-rw-r--r--qpid/doc/book/src/cpp-broker/queue-state-replication.xml (renamed from qpid/doc/book/src/queue-state-replication.xml)0
-rw-r--r--qpid/doc/book/src/css/style.css129
-rw-r--r--qpid/doc/book/src/java-broker/AMQP-Messaging-Broker-Java-Book.xml (renamed from qpid/doc/book/src/AMQP-Messaging-Broker-Java-Book.xml)4
-rw-r--r--qpid/doc/book/src/java-broker/Add-New-Users.xml (renamed from qpid/doc/book/src/Add-New-Users.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/Broker-Configuration-Guide.xml (renamed from qpid/doc/book/src/Broker-Configuration-Guide.xml)4
-rw-r--r--qpid/doc/book/src/java-broker/Configure-ACLs.xml (renamed from qpid/doc/book/src/Configure-ACLs.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/Configure-Java-Qpid-to-use-a-SSL-connection.xml (renamed from qpid/doc/book/src/Configure-Java-Qpid-to-use-a-SSL-connection.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/Configure-Log4j-CompositeRolling-Appender.xml (renamed from qpid/doc/book/src/Configure-Log4j-CompositeRolling-Appender.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/Configure-the-Broker-via-config.xml.xml (renamed from qpid/doc/book/src/Configure-the-Broker-via-config.xml.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/Configure-the-Virtual-Hosts-via-virtualhosts.xml.xml (renamed from qpid/doc/book/src/Configure-the-Virtual-Hosts-via-virtualhosts.xml.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/Configuring-Management-Users.xml (renamed from qpid/doc/book/src/Configuring-Management-Users.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/Configuring-Qpid-JMX-Management-Console.xml (renamed from qpid/doc/book/src/Configuring-Qpid-JMX-Management-Console.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/Debug-using-log4j.xml (renamed from qpid/doc/book/src/Debug-using-log4j.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/HA-Guide.xml990
-rw-r--r--qpid/doc/book/src/java-broker/How-to-Tune-M3-Java-Broker-Performance.xml (renamed from qpid/doc/book/src/How-to-Tune-M3-Java-Broker-Performance.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/How-to-Use-SlowConsumerDisconnect.xml (renamed from qpid/doc/book/src/How-to-Use-SlowConsumerDisconnect.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/Java-Broker-Feature-Guide.xml (renamed from qpid/doc/book/src/Java-Broker-Feature-Guide.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/Java-Environment-Variables.xml (renamed from qpid/doc/book/src/Java-Environment-Variables.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/Makefile20
-rw-r--r--qpid/doc/book/src/java-broker/Management-Console-Security.xml (renamed from qpid/doc/book/src/Management-Console-Security.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/MessageStore-Tool.xml (renamed from qpid/doc/book/src/MessageStore-Tool.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/Producer-Flow-Control.xml217
-rw-r--r--qpid/doc/book/src/java-broker/Qpid-JMX-Management-Console-FAQ.xml (renamed from qpid/doc/book/src/Qpid-JMX-Management-Console-FAQ.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/Qpid-JMX-Management-Console-User-Guide.xml (renamed from qpid/doc/book/src/Qpid-JMX-Management-Console-User-Guide.xml)44
-rw-r--r--qpid/doc/book/src/java-broker/Qpid-JMX-Management-Console.xml (renamed from qpid/doc/book/src/Qpid-JMX-Management-Console.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/Qpid-Java-Broker-Management-CLI.xml (renamed from qpid/doc/book/src/Qpid-Java-Broker-Management-CLI.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/Qpid-Java-Build-How-To.xml (renamed from qpid/doc/book/src/Qpid-Java-Build-How-To.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/Qpid-Java-FAQ.xml (renamed from qpid/doc/book/src/Qpid-Java-FAQ.xml)23
-rw-r--r--qpid/doc/book/src/java-broker/Qpid-Management-Features.xml (renamed from qpid/doc/book/src/Qpid-Management-Features.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/Qpid-Troubleshooting-Guide.xml (renamed from qpid/doc/book/src/Qpid-Troubleshooting-Guide.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/Topic-Configuration.xml (renamed from qpid/doc/book/src/java/broker/configuration/Topic-Configuration.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/Use-Priority-Queues.xml (renamed from qpid/doc/book/src/Use-Priority-Queues.xml)0
-rw-r--r--qpid/doc/book/src/java-broker/images/3113098.png (renamed from qpid/doc/book/src/images/jmx_console/3113098.png)bin9805 -> 9805 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113099.png (renamed from qpid/doc/book/src/images/jmx_console/3113099.png)bin12882 -> 12882 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113100.png (renamed from qpid/doc/book/src/images/jmx_console/3113100.png)bin38529 -> 38529 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113101.png (renamed from qpid/doc/book/src/images/jmx_console/3113101.png)bin45933 -> 45933 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113102.png (renamed from qpid/doc/book/src/images/jmx_console/3113102.png)bin7126 -> 7126 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113103.png (renamed from qpid/doc/book/src/images/jmx_console/3113103.png)bin34693 -> 34693 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113104.png (renamed from qpid/doc/book/src/images/jmx_console/3113104.png)bin61810 -> 61810 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113105.png (renamed from qpid/doc/book/src/images/jmx_console/3113105.png)bin26365 -> 26365 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113106.png (renamed from qpid/doc/book/src/images/jmx_console/3113106.png)bin45911 -> 45911 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113107.png (renamed from qpid/doc/book/src/images/jmx_console/3113107.png)bin31789 -> 31789 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113108.png (renamed from qpid/doc/book/src/images/jmx_console/3113108.png)bin39198 -> 39198 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113109.png (renamed from qpid/doc/book/src/images/jmx_console/3113109.png)bin13295 -> 13295 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113110.png (renamed from qpid/doc/book/src/images/jmx_console/3113110.png)bin38715 -> 38715 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113111.png (renamed from qpid/doc/book/src/images/jmx_console/3113111.png)bin52694 -> 52694 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113112.png (renamed from qpid/doc/book/src/images/jmx_console/3113112.png)bin39276 -> 39276 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113113.png (renamed from qpid/doc/book/src/images/jmx_console/3113113.png)bin46459 -> 46459 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113114.png (renamed from qpid/doc/book/src/images/jmx_console/3113114.png)bin64661 -> 64661 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113115.png (renamed from qpid/doc/book/src/images/jmx_console/3113115.png)bin38902 -> 38902 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113116.png (renamed from qpid/doc/book/src/images/jmx_console/3113116.png)bin9252 -> 9252 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113117.png (renamed from qpid/doc/book/src/images/jmx_console/3113117.png)bin40855 -> 40855 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113118.png (renamed from qpid/doc/book/src/images/jmx_console/3113118.png)bin13796 -> 13796 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/3113119.png (renamed from qpid/doc/book/src/images/jmx_console/3113119.png)bin39115 -> 39115 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/HA-2N-Key.pngbin0 -> 25912 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/HA-2N-Key.svg3
-rw-r--r--qpid/doc/book/src/java-broker/images/HA-2N-MasterFail.pngbin0 -> 200842 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/HA-2N-MasterFail.svg3
-rw-r--r--qpid/doc/book/src/java-broker/images/HA-2N-NetworkPartition.pngbin0 -> 159519 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/HA-2N-NetworkPartition.svg3
-rw-r--r--qpid/doc/book/src/java-broker/images/HA-2N-Normal.pngbin0 -> 42088 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/HA-2N-Normal.svg3
-rw-r--r--qpid/doc/book/src/java-broker/images/HA-2N-ReplicaFail.pngbin0 -> 162077 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/HA-2N-ReplicaFail.svg3
-rw-r--r--qpid/doc/book/src/java-broker/images/HA-2N-SplitBrain.pngbin0 -> 130546 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/HA-2N-SplitBrain.svg3
-rw-r--r--qpid/doc/book/src/java-broker/images/HA-BDBHAMessageStore-MBean-jconsole.pngbin0 -> 52500 bytes
-rw-r--r--qpid/doc/book/src/java-broker/images/HA-perftests-results.pngbin0 -> 29998 bytes
-rw-r--r--qpid/doc/book/src/old/ACL.xml (renamed from qpid/doc/book/src/ACL.xml)0
-rw-r--r--qpid/doc/book/src/old/AMQP-.NET-Messaging-Client.xml (renamed from qpid/doc/book/src/AMQP-.NET-Messaging-Client.xml)0
-rw-r--r--qpid/doc/book/src/old/AMQP-C++-Messaging-Client.xml (renamed from qpid/doc/book/src/AMQP-C++-Messaging-Client.xml)0
-rw-r--r--qpid/doc/book/src/old/AMQP-Java-JMS-Messaging-Client.xml (renamed from qpid/doc/book/src/AMQP-Java-JMS-Messaging-Client.xml)0
-rw-r--r--qpid/doc/book/src/old/AMQP-Messaging-Broker-CPP.xml (renamed from qpid/doc/book/src/AMQP-Messaging-Broker-CPP.xml)0
-rw-r--r--qpid/doc/book/src/old/AMQP-Python-Messaging-Client.xml (renamed from qpid/doc/book/src/AMQP-Python-Messaging-Client.xml)0
-rw-r--r--qpid/doc/book/src/old/AMQP-Ruby-Messaging-Client.xml (renamed from qpid/doc/book/src/AMQP-Ruby-Messaging-Client.xml)0
-rw-r--r--qpid/doc/book/src/old/AMQP.xml (renamed from qpid/doc/book/src/AMQP.xml)0
-rw-r--r--qpid/doc/book/src/old/Binding-URL-Format.xml (renamed from qpid/doc/book/src/Binding-URL-Format.xml)0
-rw-r--r--qpid/doc/book/src/old/Book-Info.xml (renamed from qpid/doc/book/src/Book-Info.xml)0
-rw-r--r--qpid/doc/book/src/old/Book.xml (renamed from qpid/doc/book/src/Book.xml)0
-rw-r--r--qpid/doc/book/src/old/Broker-CPP.xml (renamed from qpid/doc/book/src/Broker-CPP.xml)0
-rw-r--r--qpid/doc/book/src/old/Broker-Java.xml (renamed from qpid/doc/book/src/Broker-Java.xml)0
-rw-r--r--qpid/doc/book/src/old/Clients.xml (renamed from qpid/doc/book/src/Clients.xml)0
-rw-r--r--qpid/doc/book/src/old/Connection-URL-Format.xml (renamed from qpid/doc/book/src/Connection-URL-Format.xml)0
-rw-r--r--qpid/doc/book/src/old/Download.xml (renamed from qpid/doc/book/src/Download.xml)0
-rw-r--r--qpid/doc/book/src/old/Excel-AddIn.xml (renamed from qpid/doc/book/src/Excel-AddIn.xml)0
-rw-r--r--qpid/doc/book/src/old/FAQ.xml (renamed from qpid/doc/book/src/FAQ.xml)0
-rw-r--r--qpid/doc/book/src/old/Getting-Started.xml (renamed from qpid/doc/book/src/Getting-Started.xml)0
-rw-r--r--qpid/doc/book/src/old/How-to-Use-JNDI.xml (renamed from qpid/doc/book/src/How-to-Use-JNDI.xml)2
-rw-r--r--qpid/doc/book/src/old/InfoPlugin.xml (renamed from qpid/doc/book/src/InfoPlugin.xml)0
-rw-r--r--qpid/doc/book/src/old/Introduction.xml (renamed from qpid/doc/book/src/Introduction.xml)0
-rw-r--r--qpid/doc/book/src/old/Java-Broker-StatusLogMessages.xml (renamed from qpid/doc/book/src/Java-Broker-StatusLogMessages.xml)0
-rw-r--r--qpid/doc/book/src/old/Java-JMS-Selector-Syntax.xml (renamed from qpid/doc/book/src/Java-JMS-Selector-Syntax.xml)0
-rw-r--r--qpid/doc/book/src/old/Management-Design-notes.xml (renamed from qpid/doc/book/src/Management-Design-notes.xml)0
-rw-r--r--qpid/doc/book/src/old/NET-User-Guide.xml (renamed from qpid/doc/book/src/NET-User-Guide.xml)0
-rw-r--r--qpid/doc/book/src/old/PythonBrokerTest.xml (renamed from qpid/doc/book/src/PythonBrokerTest.xml)0
-rw-r--r--qpid/doc/book/src/old/QMan-Qpid-Management-bridge.xml (renamed from qpid/doc/book/src/QMan-Qpid-Management-bridge.xml)0
-rw-r--r--qpid/doc/book/src/old/Qpid-ACLs.xml (renamed from qpid/doc/book/src/Qpid-ACLs.xml)0
-rw-r--r--qpid/doc/book/src/old/Qpid-Book.xml (renamed from qpid/doc/book/src/Qpid-Book.xml)0
-rw-r--r--qpid/doc/book/src/old/Qpid-Compatibility-And-Interoperability-Book.xml (renamed from qpid/doc/book/src/Qpid-Compatibility-And-Interoperability-Book.xml)0
-rw-r--r--qpid/doc/book/src/old/SASL-Compatibility.xml (renamed from qpid/doc/book/src/SASL-Compatibility.xml)0
-rw-r--r--qpid/doc/book/src/old/SSL.xml (renamed from qpid/doc/book/src/SSL.xml)0
-rw-r--r--qpid/doc/book/src/old/Security-Plugins.xml (renamed from qpid/doc/book/src/Security-Plugins.xml)0
-rw-r--r--qpid/doc/book/src/old/System-Properties.xml (renamed from qpid/doc/book/src/System-Properties.xml)0
-rw-r--r--qpid/doc/book/src/old/Using-Qpid-with-other-JNDI-Providers.xml (renamed from qpid/doc/book/src/Using-Qpid-with-other-JNDI-Providers.xml)0
-rw-r--r--qpid/doc/book/src/old/WCF.xml (renamed from qpid/doc/book/src/WCF.xml)0
-rw-r--r--qpid/doc/book/src/old/schemas.xml (renamed from qpid/doc/book/src/schemas.xml)0
-rw-r--r--qpid/doc/book/src/programming/Makefile20
-rw-r--r--qpid/doc/book/src/programming/Message-Groups-Guide.xml (renamed from qpid/doc/book/src/Message-Groups-Guide.xml)0
-rw-r--r--qpid/doc/book/src/programming/Programming-In-Apache-Qpid-Book.xml (renamed from qpid/doc/book/src/Programming-In-Apache-Qpid.xml)10
-rw-r--r--qpid/doc/book/src/qmf/QmfBook.xml (renamed from qpid/doc/book/src/QmfBook.xml)0
-rw-r--r--qpid/doc/book/src/qmf/QmfIntroduction.xml (renamed from qpid/doc/book/src/QmfIntroduction.xml)0
-rw-r--r--qpid/doc/book/xsl/html-custom.xsl188
-rw-r--r--qpid/extras/qmf/src/py/qmf/console.py2
-rw-r--r--qpid/python/qpid/connection.py13
-rw-r--r--qpid/python/qpid/messaging/driver.py6
-rw-r--r--qpid/python/qpid/messaging/util.py9
-rw-r--r--qpid/specs/amqp.xsl534
-rw-r--r--qpid/specs/apache-filters.xml227
-rw-r--r--qpid/specs/management-schema.xml50
-rw-r--r--qpid/tests/src/py/qpid_tests/broker_0_10/__init__.py1
-rw-r--r--qpid/tests/src/py/qpid_tests/broker_0_10/management.py37
-rw-r--r--qpid/tests/src/py/qpid_tests/broker_0_10/msg_groups.py64
-rw-r--r--qpid/tests/src/py/qpid_tests/broker_0_10/new_api.py2
-rw-r--r--qpid/tests/src/py/qpid_tests/broker_0_10/qmf_events.py83
-rw-r--r--qpid/tools/src/py/.gitignore1
-rwxr-xr-xqpid/tools/src/py/qmf-tool2
-rwxr-xr-xqpid/tools/src/py/qpid-config2
-rwxr-xr-xqpid/tools/src/py/qpid-ha32
-rwxr-xr-xqpid/tools/src/py/qpid-printevents113
-rwxr-xr-xqpid/tools/src/py/qpid-stat27
-rwxr-xr-xqpid/tools/src/py/qpid-tool1
-rw-r--r--qpid/tools/src/py/qpidtoollibs/broker.py55
-rw-r--r--qpid/wcf/src/Apache/Qpid/DtcPlugin/DtcPlugin.cpp7
492 files changed, 14523 insertions, 6276 deletions
diff --git a/.gitignore b/.gitignore
index bf3a63166e..0b053fdcd7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,7 +32,7 @@ configure
.deps
.libs
autom4te.cache
-aclocal.m4
+*.m4
developer.doxygen
user.doxygen
qpid/cpp/libtool
@@ -53,10 +53,15 @@ release
.settings
generated/
target
+classes
+qpid/java/lib/*.jar
+qpid/java/lib/required
qpid/java/lib/bdbstore
qpid/java/lib/cobertura
qpid/java/lib/findbugs
qpid/java/lib/ivy
+qpid/java/lib/csvjdbc
+qpid/java/lib/jfree
qpid/java/build.overrides
qpid/java/felix-cache/*
qpid/java/eclipse-projects
diff --git a/qpid/bin/install-cpp-python b/qpid/bin/install-cpp-python
new file mode 100755
index 0000000000..d43e8e4899
--- /dev/null
+++ b/qpid/bin/install-cpp-python
@@ -0,0 +1,76 @@
+#!/bin/sh
+#
+# 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.
+#
+
+
+#
+# Install C++ build and python tools to the standard places in a Unix buld
+# WARNING: Will destroy any existing installation!
+#
+
+# NOTE: build must be configured like this:
+# ../qpid/cpp/configure --prefix=/usr --exec-prefix=/usr --sysconfdir=/etc --libdir=/usr/lib64
+# NOTE: Must run as root.
+
+usage() {
+ cat <<EOF
+Usage $0 -pc <cpp-build-directory>
+-p <prefix> : Prefix to install python
+-s : Skip C++ installation
+EOF
+
+ exit 1
+}
+
+fail() { echo $*; exit 1; }
+
+while getopts "ps" opt; do
+ case $opt in
+ p) PY_PREFIX="--prefix $OPTARG";;
+ s) SKIP_CPP=1;;
+ *) usage;;
+ esac
+done
+shift `expr $OPTIND - 1`
+BUILD=$1
+SRC=$(dirname $0)/..
+
+# Install python
+cd $SRC || fail "No such directory: $SRC"
+for d in python tools extras/qmf; do
+ (
+ cd $d || fail "No such directory: $(pwd)/$d"
+ ./setup.py install || fail Python install failed in $(pwd)
+ )
+done
+
+if test $SKIP_CPP; then exit; fi
+
+test -n "$BUILD" || { echo "No build directory."; usage; }
+test -d "$BUILD" || fail "No such directory: $BUILD"
+SRC=$(dirname $BUILD)
+
+ # Install C++
+cd $BUILD
+make -j1 install || fail "C++ install failed in $BUILD"
+
+# NOTE: setup.py does not uninstall, but you can get a list of files installed with:
+# setup.py install --record <output-file>
+
+
diff --git a/qpid/cpp/bindings/qmf/ruby/qmf.rb b/qpid/cpp/bindings/qmf/ruby/qmf.rb
index 34d3255d8d..9fbd50cbf6 100644
--- a/qpid/cpp/bindings/qmf/ruby/qmf.rb
+++ b/qpid/cpp/bindings/qmf/ruby/qmf.rb
@@ -26,18 +26,28 @@ module Qmf
# Pull all the TYPE_* constants into Qmf namespace. Maybe there's an easier way?
Qmfengine.constants.each do |c|
+ c = c.to_s
if c.index('TYPE_') == 0 or c.index('ACCESS_') == 0 or c.index('DIR_') == 0 or
c.index('CLASS_') == 0 or c.index('SEV_') == 0
const_set(c, Qmfengine.const_get(c))
end
end
+ module StringHelpers
+ def ensure_encoding(str)
+ enc = (Encoding.default_external.name || "UTF-8" rescue "UTF-8")
+ str.respond_to?(:force_encoding) ? str.force_encoding(enc) : str
+ end
+ end
+
class Util
+ include StringHelpers
+
def qmf_to_native(val)
case val.getType
when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.asUint
when TYPE_UINT64 then val.asUint64
- when TYPE_SSTR, TYPE_LSTR then val.asString
+ when TYPE_SSTR, TYPE_LSTR then ensure_encoding(val.asString)
when TYPE_ABSTIME then val.asInt64
when TYPE_DELTATIME then val.asUint64
when TYPE_REF then ObjectId.new(val.asObjectId)
@@ -161,6 +171,7 @@ module Qmf
##==============================================================================
class ConnectionSettings
+ include StringHelpers
attr_reader :impl
def initialize(url = nil)
@@ -192,7 +203,7 @@ module Qmf
def get_attr(key)
_v = @impl.getAttr(key)
if _v.isString()
- return _v.asString()
+ return ensure_encoding(_v.asString())
elsif _v.isUint()
return _v.asUint()
elsif _v.isBool()
@@ -348,7 +359,7 @@ module Qmf
@broker = kwargs[:broker] if kwargs.include?(:broker)
@allow_sets = :true
- if cls:
+ if cls
@event_class = cls
@impl = Qmfengine::Event.new(@event_class.impl)
elsif kwargs.include?(:impl)
@@ -434,7 +445,7 @@ module Qmf
@allow_sets = :false
@broker = kwargs[:broker] if kwargs.include?(:broker)
- if cls:
+ if cls
@object_class = cls
@impl = Qmfengine::Object.new(@object_class.impl)
elsif kwargs.include?(:impl)
@@ -707,6 +718,8 @@ module Qmf
end
class MethodResponse
+ include StringHelpers
+
def initialize(impl)
@impl = Qmfengine::MethodResponse.new(impl)
end
@@ -720,7 +733,7 @@ module Qmf
end
def text
- exception.asString
+ ensure_encoding(exception.asString)
end
def args
@@ -885,6 +898,7 @@ module Qmf
end
class SchemaClassKey
+ include StringHelpers
attr_reader :impl
def initialize(i)
@impl = Qmfengine::SchemaClassKey.new(i)
@@ -899,7 +913,7 @@ module Qmf
end
def to_s
- @impl.asString
+ ensure_encoding(@impl.asString)
end
end
diff --git a/qpid/cpp/bindings/qpid/python/python.i b/qpid/cpp/bindings/qpid/python/python.i
index 9d45bf54ee..4d8a64b376 100644
--- a/qpid/cpp/bindings/qpid/python/python.i
+++ b/qpid/cpp/bindings/qpid/python/python.i
@@ -33,41 +33,68 @@
* names as in the C++ library. They get renamed to their Python
* equivalents when brought into the Python wrapping
*/
+%define QPID_EXCEPTION(exception, parent)
%{
-static PyObject* pNoMessageAvailable;
-static PyObject* pTargetCapacityExceeded;
-static PyObject* pNotFound;
-static PyObject* pTransportFailure;
+static PyObject* exception;
%}
-
%init %{
- pNoMessageAvailable = PyErr_NewException(
- "_cqpid.NoMessageAvailable", NULL, NULL);
- Py_INCREF(pNoMessageAvailable);
- PyModule_AddObject(m, "NoMessageAvailable", pNoMessageAvailable);
-
- pTargetCapacityExceeded = PyErr_NewException(
- "_cqpid.TargetCapacityExceeded", NULL, NULL);
- Py_INCREF(pTargetCapacityExceeded);
- PyModule_AddObject(m, "TargetCapacityExceeded", pTargetCapacityExceeded);
-
- pNotFound = PyErr_NewException(
- "_cqpid.NotFound", NULL, NULL);
- Py_INCREF(pNotFound);
- PyModule_AddObject(m, "NotFound", pNotFound);
-
- pTransportFailure = PyErr_NewException(
- "_cqpid.TransportFailure", NULL, NULL);
- Py_INCREF(pTransportFailure);
- PyModule_AddObject(m, "TransportFailure", pTransportFailure);
+ exception = PyErr_NewException(
+ (char *) ("_cqpid." #exception), parent, NULL);
+ Py_INCREF(exception);
+ PyModule_AddObject(m, #exception, exception);
%}
-
%pythoncode %{
- Empty = _cqpid.NoMessageAvailable
- TargetCapacityExceeded = _cqpid.TargetCapacityExceeded
- NotFound = _cqpid.NotFound
- ConnectError = _cqpid.TransportFailure
+ exception = _cqpid. ## exception
%}
+%enddef
+
+ /* Python equivalents of C++ exceptions. */
+ /* */
+ /* Commented out lines are exceptions in the Python library, but not */
+ /* in the C++ library. */
+
+QPID_EXCEPTION(MessagingError, NULL)
+
+QPID_EXCEPTION(LinkError, MessagingError)
+QPID_EXCEPTION(AddressError, LinkError)
+QPID_EXCEPTION(ResolutionError, AddressError)
+QPID_EXCEPTION(AssertionFailed, ResolutionError)
+QPID_EXCEPTION(NotFound, ResolutionError)
+QPID_EXCEPTION(InvalidOption, LinkError)
+QPID_EXCEPTION(MalformedAddress, LinkError)
+QPID_EXCEPTION(ReceiverError, LinkError)
+QPID_EXCEPTION(FetchError, ReceiverError)
+QPID_EXCEPTION(Empty, FetchError)
+/* QPID_EXCEPTION(InsufficientCapacity, LinkError) */
+/* QPID_EXCEPTION(LinkClosed, LinkError) */
+QPID_EXCEPTION(SenderError, LinkError)
+QPID_EXCEPTION(SendError, SenderError)
+QPID_EXCEPTION(TargetCapacityExceeded, SendError)
+
+QPID_EXCEPTION(ConnectionError, MessagingError)
+QPID_EXCEPTION(ConnectError, ConnectionError)
+/* QPID_EXCEPTION(AuthenticationFailure, ConnectError) */
+/* QPID_EXCEPTION(VersionError, ConnectError) */
+/* QPID_EXCEPTION(ConnectionClosed, ConnectionError) */
+/* QPID_EXCEPTION(HeartbeartTimeout, ConnectionError) */
+
+QPID_EXCEPTION(SessionError, MessagingError)
+/* QPID_EXCEPTION(Detached, SessionError) */
+/* QPID_EXCEPTION(NontransactionalSession, SessionError) */
+/* QPID_EXCEPTION(ServerError, SessionError) */
+/* QPID_EXCEPTION(SessionClosed, SessionError) */
+QPID_EXCEPTION(TransactionError, SessionError)
+QPID_EXCEPTION(TransactionAborted, TransactionError)
+QPID_EXCEPTION(UnauthorizedAccess, SessionError)
+
+/* QPID_EXCEPTION(InternalError, MessagingError) */
+
+%define TRANSLATE_EXCEPTION(cpp_exception, py_exception)
+ catch ( cpp_exception & ex) {
+ pExceptionType = py_exception;
+ error = ex.what();
+ }
+%enddef
/* Define the general-purpose exception handling */
%exception {
@@ -76,22 +103,31 @@ static PyObject* pTransportFailure;
Py_BEGIN_ALLOW_THREADS;
try {
$action
- } catch (qpid::messaging::NoMessageAvailable & ex) {
- pExceptionType = pNoMessageAvailable;
- error = ex.what();
- } catch (qpid::messaging::TargetCapacityExceeded & ex) {
- pExceptionType = pTargetCapacityExceeded;
- error = ex.what();
- } catch (qpid::messaging::NotFound & ex) {
- pExceptionType = pNotFound;
- error = ex.what();
- } catch (qpid::messaging::TransportFailure & ex) {
- pExceptionType = pTransportFailure;
- error = ex.what();
- } catch (qpid::types::Exception& ex) {
- pExceptionType = PyExc_RuntimeError;
- error = ex.what();
}
+ /* Catch and translate exceptions. */
+ TRANSLATE_EXCEPTION(qpid::messaging::NoMessageAvailable, Empty)
+ TRANSLATE_EXCEPTION(qpid::messaging::NotFound, NotFound)
+ TRANSLATE_EXCEPTION(qpid::messaging::AssertionFailed, AssertionFailed)
+ TRANSLATE_EXCEPTION(qpid::messaging::ResolutionError, ResolutionError)
+ TRANSLATE_EXCEPTION(qpid::messaging::TargetCapacityExceeded,
+ TargetCapacityExceeded)
+ TRANSLATE_EXCEPTION(qpid::messaging::TransportFailure, ConnectError)
+ TRANSLATE_EXCEPTION(qpid::messaging::MalformedAddress, MalformedAddress)
+ TRANSLATE_EXCEPTION(qpid::messaging::AddressError, AddressError)
+ TRANSLATE_EXCEPTION(qpid::messaging::FetchError, FetchError)
+ TRANSLATE_EXCEPTION(qpid::messaging::ReceiverError, ReceiverError)
+ TRANSLATE_EXCEPTION(qpid::messaging::SendError, SendError)
+ TRANSLATE_EXCEPTION(qpid::messaging::SenderError, SenderError)
+ TRANSLATE_EXCEPTION(qpid::messaging::InvalidOptionString, InvalidOption)
+ TRANSLATE_EXCEPTION(qpid::messaging::LinkError, LinkError)
+ TRANSLATE_EXCEPTION(qpid::messaging::TransactionAborted, TransactionAborted)
+ TRANSLATE_EXCEPTION(qpid::messaging::TransactionError, TransactionError)
+ TRANSLATE_EXCEPTION(qpid::messaging::UnauthorizedAccess, UnauthorizedAccess)
+ TRANSLATE_EXCEPTION(qpid::messaging::SessionError, SessionError)
+ TRANSLATE_EXCEPTION(qpid::messaging::ConnectionError, ConnectionError)
+ TRANSLATE_EXCEPTION(qpid::messaging::KeyError, PyExc_KeyError)
+ TRANSLATE_EXCEPTION(qpid::messaging::MessagingException, MessagingError)
+ TRANSLATE_EXCEPTION(qpid::types::Exception, PyExc_RuntimeError)
Py_END_ALLOW_THREADS;
if (!error.empty()) {
PyErr_SetString(pExceptionType, error.c_str());
@@ -132,7 +168,10 @@ static PyObject* pTransportFailure;
# equivalent in C++, so we will translate them to sasl_mechanism
# when possible.
def __init__(self, url=None, **options):
- args = [url] if url else []
+ if url:
+ args = [url]
+ else:
+ args = []
if options :
if "sasl_mechanisms" in options :
if ' ' in options.get("sasl_mechanisms",'') :
@@ -196,7 +235,7 @@ static PyObject* pTransportFailure;
self._acknowledge_all(sync)
__swig_getmethods__["connection"] = getConnection
- if _newclass: connection = _swig_property(getConnection)
+ if _newclass: connection = property(getConnection)
%}
}
@@ -205,10 +244,10 @@ static PyObject* pTransportFailure;
%pythoncode %{
__swig_getmethods__["capacity"] = getCapacity
__swig_setmethods__["capacity"] = setCapacity
- if _newclass: capacity = _swig_property(getCapacity, setCapacity)
+ if _newclass: capacity = property(getCapacity, setCapacity)
__swig_getmethods__["session"] = getSession
- if _newclass: session = _swig_property(getSession)
+ if _newclass: session = property(getSession)
%}
%pythoncode %{
@@ -233,10 +272,10 @@ static PyObject* pTransportFailure;
__swig_getmethods__["capacity"] = getCapacity
__swig_setmethods__["capacity"] = setCapacity
- if _newclass: capacity = _swig_property(getCapacity, setCapacity)
+ if _newclass: capacity = property(getCapacity, setCapacity)
__swig_getmethods__["session"] = getSession
- if _newclass: session = _swig_property(getSession)
+ if _newclass: session = property(getSession)
%}
}
@@ -298,24 +337,23 @@ static PyObject* pTransportFailure;
self.setContent(content)
__swig_getmethods__["content"] = _get_content
__swig_setmethods__["content"] = _set_content
- if _newclass: content = _swig_property(_get_content, _set_content)
+ if _newclass: content = property(_get_content, _set_content)
__swig_getmethods__["content_type"] = getContentType
__swig_setmethods__["content_type"] = setContentType
- if _newclass: content_type = _swig_property(getContentType,
- setContentType)
+ if _newclass: content_type = property(getContentType, setContentType)
__swig_getmethods__["id"] = getMessageId
__swig_setmethods__["id"] = setMessageId
- if _newclass: id = _swig_property(getMessageId, setMessageId)
+ if _newclass: id = property(getMessageId, setMessageId)
__swig_getmethods__["subject"] = getSubject
__swig_setmethods__["subject"] = setSubject
- if _newclass: subject = _swig_property(getSubject, setSubject)
+ if _newclass: subject = property(getSubject, setSubject)
__swig_getmethods__["priority"] = getPriority
__swig_setmethods__["priority"] = setPriority
- if _newclass: priority = _swig_property(getPriority, setPriority)
+ if _newclass: priority = property(getPriority, setPriority)
def getTtl(self) :
return self._getTtl().getMilliseconds()/1000.0
@@ -323,28 +361,26 @@ static PyObject* pTransportFailure;
self._setTtl(Duration(int(1000*duration)))
__swig_getmethods__["ttl"] = getTtl
__swig_setmethods__["ttl"] = setTtl
- if _newclass: ttl = _swig_property(getTtl, setTtl)
+ if _newclass: ttl = property(getTtl, setTtl)
__swig_getmethods__["user_id"] = getUserId
__swig_setmethods__["user_id"] = setUserId
- if _newclass: user_id = _swig_property(getUserId, setUserId)
+ if _newclass: user_id = property(getUserId, setUserId)
__swig_getmethods__["correlation_id"] = getCorrelationId
__swig_setmethods__["correlation_id"] = setCorrelationId
- if _newclass: correlation_id = _swig_property(getCorrelationId,
- setCorrelationId)
+ if _newclass: correlation_id = property(getCorrelationId, setCorrelationId)
__swig_getmethods__["redelivered"] = getRedelivered
__swig_setmethods__["redelivered"] = setRedelivered
- if _newclass: redelivered = _swig_property(getRedelivered,
- setRedelivered)
+ if _newclass: redelivered = property(getRedelivered, setRedelivered)
__swig_getmethods__["durable"] = getDurable
__swig_setmethods__["durable"] = setDurable
- if _newclass: durable = _swig_property(getDurable, setDurable)
+ if _newclass: durable = property(getDurable, setDurable)
__swig_getmethods__["properties"] = getProperties
- if _newclass: properties = _swig_property(getProperties)
+ if _newclass: properties = property(getProperties)
def getReplyTo(self) :
return self._getReplyTo().str()
@@ -352,7 +388,7 @@ static PyObject* pTransportFailure;
self._setReplyTo(Address(address_str))
__swig_getmethods__["reply_to"] = getReplyTo
__swig_setmethods__["reply_to"] = setReplyTo
- if _newclass: reply_to = _swig_property(getReplyTo, setReplyTo)
+ if _newclass: reply_to = property(getReplyTo, setReplyTo)
def __repr__(self):
args = []
diff --git a/qpid/cpp/bindings/qpid/ruby/LICENSE b/qpid/cpp/bindings/qpid/ruby/LICENSE
index cff2a5e25d..232fd660d6 100644
--- a/qpid/cpp/bindings/qpid/ruby/LICENSE
+++ b/qpid/cpp/bindings/qpid/ruby/LICENSE
@@ -203,32 +203,3 @@
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.
-
-=========================================================================
-== Boost License ==
-=========================================================================
-
-Boost Software License - Version 1.0 - August 17th, 2003
-
-Permission is hereby granted, free of charge, to any person or organization
-obtaining a copy of the software and accompanying documentation covered by
-this license (the "Software") to use, reproduce, display, distribute,
-execute, and transmit the Software, and to prepare derivative works of the
-Software, and to permit third-parties to whom the Software is furnished to
-do so, all subject to the following:
-
-The copyright notices in the Software and this entire statement, including
-the above license grant, this restriction and the following disclaimer,
-must be included in all copies of the Software, in whole or in part, and
-all derivative works of the Software, unless such copies or derivative
-works are solely in the form of machine-executable object code generated by
-a source language processor.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
-SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
-FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
-
diff --git a/qpid/cpp/bindings/qpid/ruby/README.rdoc b/qpid/cpp/bindings/qpid/ruby/README.rdoc
index 0ae7e5cbed..478fc939d9 100644
--- a/qpid/cpp/bindings/qpid/ruby/README.rdoc
+++ b/qpid/cpp/bindings/qpid/ruby/README.rdoc
@@ -2,7 +2,7 @@
Qpid is an cross-platform enterprise messaging system.
-Version :: 0.10.0.alpha.0
+Version :: 0.17.0
= Links
diff --git a/qpid/cpp/bindings/qpid/ruby/Rakefile b/qpid/cpp/bindings/qpid/ruby/Rakefile
index df0b3970b6..99c3e13c83 100644
--- a/qpid/cpp/bindings/qpid/ruby/Rakefile
+++ b/qpid/cpp/bindings/qpid/ruby/Rakefile
@@ -120,7 +120,9 @@ spec = Gem::Specification.new do |s|
"lib/**/*.rb",
"test/**/*.rb",
"examples/**/*.rb",
- "ext/**/*"]
+ "ext/**/*",
+ "features/**/*",
+ "spec/**/*"]
end
Gem::PackageTask.new(spec) do |pkg|
diff --git a/qpid/cpp/bindings/qpid/ruby/features/creating_a_receiver.feature b/qpid/cpp/bindings/qpid/ruby/features/creating_a_receiver.feature
index f509f49115..1f758153af 100644
--- a/qpid/cpp/bindings/qpid/ruby/features/creating_a_receiver.feature
+++ b/qpid/cpp/bindings/qpid/ruby/features/creating_a_receiver.feature
@@ -21,9 +21,9 @@ Feature: Creating a receiver
Scenario: The address string is fine
Given an open session
- Then creating a receiver with "my-queue;{create:always}" succeeds
+ Then creating a receiver with "my-queue;{create:always,delete:always}" succeeds
Scenario: Using an Address object
Given an open session
- And an Address with the name "create-receiver-test" and subject "foo" and option "create" set to "always"
+ And an Address with the name "create-receiver-test" and subject "foo" and option "create" set to "always" and "delete" set to "always"
Then creating a receiver with an Address succeeds
diff --git a/qpid/cpp/bindings/qpid/ruby/features/creating_a_sender.feature b/qpid/cpp/bindings/qpid/ruby/features/creating_a_sender.feature
index ac75543c2d..1c09ff837d 100644
--- a/qpid/cpp/bindings/qpid/ruby/features/creating_a_sender.feature
+++ b/qpid/cpp/bindings/qpid/ruby/features/creating_a_sender.feature
@@ -5,11 +5,11 @@ Feature: Creating a sender
Scenario: The session is closed
Given a closed session
- Then creating a sender with "my-queue;{create:always}" raises an exception
+ Then creating a sender with "my-queue;{create:always,delete:always}" raises an exception
Scenario: The connection is closed
Given an open session with a closed connection
- Then creating a sender with "my-queue;{create:always}" raises an exception
+ Then creating a sender with "my-queue;{create:always,delete:always}" raises an exception
Scenario: The address is malformed
Given an open session
@@ -17,7 +17,7 @@ Feature: Creating a sender
Scenario: The address string is valid
Given an open session
- Then creating a sender with "my-queue;{create:always}" succeeds
+ Then creating a sender with "my-queue;{create:always,delete:always}" succeeds
Scenario: Using an Address object
Given an open session
diff --git a/qpid/cpp/bindings/qpid/ruby/features/receiving_a_message.feature b/qpid/cpp/bindings/qpid/ruby/features/receiving_a_message.feature
index b68a78c337..7b6db4a5ac 100644
--- a/qpid/cpp/bindings/qpid/ruby/features/receiving_a_message.feature
+++ b/qpid/cpp/bindings/qpid/ruby/features/receiving_a_message.feature
@@ -4,24 +4,26 @@ Feature: Receving a message
I need to be able to receive messages
Scenario: Receiving after the session is closed
- Given a sender and receiver for "my-queue;{create:always}"
+ Given a sender and receiver for "my-queue;{create:always,delete:always}"
And the message "this is a test" is sent
And the session is closed
Then getting the next message raises an error
Scenario: Receiving after the connection is closed
- Given a sender and receiver for "my-queue;{create:always}"
+ Given a sender and receiver for "my-queue;{create:always,delete:always}"
And the message "this is a test" is sent
And the connection is closed
Then getting the next message raises an error
Scenario: No message is received on an empty queue
- Given an existing receiver for "my-queue;{create:always}"
+ Given an existing receiver for "my-queue;{create:always,delete:always}"
And the receiver has no pending messages
Then getting the next message raises an error
Scenario: A message is pending
- Given a sender and receiver for "my-queue;{create:always}"
+ Given an open session
+ And given a sender for "my-queue;{create:always}"
+ And given a receiver for "my-queue;{create:always,delete:always}"
And the receiver has a capacity of 1
And the message "this is a test" is sent
Then the receiver should have 1 message available
diff --git a/qpid/cpp/bindings/qpid/ruby/features/sending_a_message.feature b/qpid/cpp/bindings/qpid/ruby/features/sending_a_message.feature
index b1127d3664..45cbd42f06 100644
--- a/qpid/cpp/bindings/qpid/ruby/features/sending_a_message.feature
+++ b/qpid/cpp/bindings/qpid/ruby/features/sending_a_message.feature
@@ -5,17 +5,17 @@ Feature: Sending a message
Scenario: The session is closed
Given an open session
- And creating a sender with "my-queue;{create:always}" succeeds
+ And creating a sender with "my-queue;{create:always,delete:always}" succeeds
And the session is closed
Then sending the message "This is a test" should raise an error
Scenario: The connection is closed
Given an open session
- And creating a sender with "my-queue;{create:always}" succeeds
+ And creating a sender with "my-queue;{create:always,delete:always}" succeeds
And the connection is closed
Then sending the message "This is a test" should raise an error
Scenario: The message sends successfully
Given an open session
- And creating a sender with "my-queue;{create:always}" succeeds
+ And creating a sender with "my-queue;{create:always,delete:always}" succeeds
Then sending the message "This is a test" succeeds \ No newline at end of file
diff --git a/qpid/cpp/bindings/qpid/ruby/features/step_definitions/address_steps.rb b/qpid/cpp/bindings/qpid/ruby/features/step_definitions/address_steps.rb
index e5071ca4e6..0531e5ee69 100644
--- a/qpid/cpp/bindings/qpid/ruby/features/step_definitions/address_steps.rb
+++ b/qpid/cpp/bindings/qpid/ruby/features/step_definitions/address_steps.rb
@@ -1,5 +1,31 @@
+#
+# 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.
+#
+
Given /^an Address with the name "([^"]*)" and subject "([^"]*)" and option "([^"]*)" set to "([^"]*)"$/ do |name, subject, key, value|
options = Hash.new
options["#{key}"] = "#{value}"
@address = Qpid::Messaging::Address.new "#{name}", "#{subject}", options
end
+
+Given /^an Address with the name "([^"]*)" and subject "([^"]*)" and option "([^"]*)" set to "([^"]*)" and "([^"]*)" set to "([^"]*)"$/ do |name, subject, key1, value1, key2, value2|
+ options = Hash.new
+ options["#{key1}"] = "#{value1}"
+ options["#{key2}"] = "#{value2}"
+ @address = Qpid::Messaging::Address.new "#{name}", "#{subject}", options
+end
diff --git a/qpid/cpp/bindings/qpid/ruby/features/step_definitions/connection_steps.rb b/qpid/cpp/bindings/qpid/ruby/features/step_definitions/connection_steps.rb
index b4146ac1fb..3fe3e6941f 100644
--- a/qpid/cpp/bindings/qpid/ruby/features/step_definitions/connection_steps.rb
+++ b/qpid/cpp/bindings/qpid/ruby/features/step_definitions/connection_steps.rb
@@ -1,3 +1,22 @@
+#
+# 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.
+#
+
# close all connections
After do
@connection.close if @connection
diff --git a/qpid/cpp/bindings/qpid/ruby/features/step_definitions/receiver_steps.rb b/qpid/cpp/bindings/qpid/ruby/features/step_definitions/receiver_steps.rb
index 8157fb7735..e454dac345 100644
--- a/qpid/cpp/bindings/qpid/ruby/features/step_definitions/receiver_steps.rb
+++ b/qpid/cpp/bindings/qpid/ruby/features/step_definitions/receiver_steps.rb
@@ -1,3 +1,22 @@
+#
+# 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.
+#
+
Given /^an existing receiver for "([^"]*)"$/ do |address|
steps %Q{
Given an open session
@@ -40,3 +59,11 @@ Then /^the receiver should have (\d+) message available$/ do |available|
sleep 1
@receiver.available.should == available.to_i
end
+
+Given /^given a sender for "([^"]*)"$/ do |address|
+ @sender = @session.create_sender "#{address}"
+end
+
+Given /^given a receiver for "([^"]*)"$/ do |address|
+ @receiver = @session.create_receiver "#{address}"
+end
diff --git a/qpid/cpp/bindings/qpid/ruby/features/step_definitions/sender_steps.rb b/qpid/cpp/bindings/qpid/ruby/features/step_definitions/sender_steps.rb
index 3ff081c7d2..93dbd2d5c0 100644
--- a/qpid/cpp/bindings/qpid/ruby/features/step_definitions/sender_steps.rb
+++ b/qpid/cpp/bindings/qpid/ruby/features/step_definitions/sender_steps.rb
@@ -1,3 +1,22 @@
+#
+# 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.
+#
+
Given /^the message "([^"]*)" is sent$/ do |content|
@sender.send Qpid::Messaging::Message.new :content => "#{content}"
end
diff --git a/qpid/cpp/bindings/qpid/ruby/features/step_definitions/session_steps.rb b/qpid/cpp/bindings/qpid/ruby/features/step_definitions/session_steps.rb
index f97e423ee9..cf775d917d 100644
--- a/qpid/cpp/bindings/qpid/ruby/features/step_definitions/session_steps.rb
+++ b/qpid/cpp/bindings/qpid/ruby/features/step_definitions/session_steps.rb
@@ -1,3 +1,22 @@
+#
+# 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.
+#
+
Given /^a closed session/ do
steps %Q{
Given an open connection
diff --git a/qpid/cpp/bindings/qpid/ruby/lib/qpid/encoding.rb b/qpid/cpp/bindings/qpid/ruby/lib/qpid/encoding.rb
index 7b9130156d..2f20fab18e 100644
--- a/qpid/cpp/bindings/qpid/ruby/lib/qpid/encoding.rb
+++ b/qpid/cpp/bindings/qpid/ruby/lib/qpid/encoding.rb
@@ -45,8 +45,10 @@ module Qpid
content_type = message.content_type unless content_type
case content_type
- when "amqp/map": Cqpid.decodeMap message.message_impl
- when "amqp/list": Cqpid.decodeList message.message_impl
+ when "amqp/map"
+ Cqpid.decodeMap message.message_impl
+ when "amqp/list"
+ Cqpid.decodeList message.message_impl
end
message.content
diff --git a/qpid/cpp/bindings/qpid/ruby/lib/qpid/version.rb b/qpid/cpp/bindings/qpid/ruby/lib/qpid/version.rb
index f387ba98dc..39524e428f 100644
--- a/qpid/cpp/bindings/qpid/ruby/lib/qpid/version.rb
+++ b/qpid/cpp/bindings/qpid/ruby/lib/qpid/version.rb
@@ -22,7 +22,7 @@ module Qpid
module Version
NUMBERS = [MAJOR = 0,
- MINOR = 13,
+ MINOR = 17,
BUILD = 0]
end
diff --git a/qpid/cpp/bindings/swig_python_typemaps.i b/qpid/cpp/bindings/swig_python_typemaps.i
index e99ce65254..25a4e46b18 100644
--- a/qpid/cpp/bindings/swig_python_typemaps.i
+++ b/qpid/cpp/bindings/swig_python_typemaps.i
@@ -25,7 +25,11 @@ static PyObject* pUuidModule;
%}
%init %{
- pUuidModule = PyImport_ImportModule("uuid");
+ /* Instead of directly referencing the uuid module (which is not available
+ * on older versions of Python), reference the wrapper defined in
+ * qpid.datatypes.
+ */
+ pUuidModule = PyImport_ImportModule("qpid.datatypes");
/* Although it is not required, we'll publish the uuid module in our
* module, as if this module was a python module and we called
diff --git a/qpid/cpp/configure.ac b/qpid/cpp/configure.ac
index 6ba5f62f0e..c68fed932e 100644
--- a/qpid/cpp/configure.ac
+++ b/qpid/cpp/configure.ac
@@ -27,7 +27,7 @@ AC_PROG_CC_STDC
AM_PROG_CC_C_O
AC_PROG_CXX
AC_USE_SYSTEM_EXTENSIONS
-AC_LANG([C++])
+AC_LANG([C++])
# Check for optional use of help2man
AC_CHECK_PROG([HELP2MAN], [help2man], [help2man])
@@ -83,14 +83,14 @@ if test x$GXX = xyes; then
gl_COMPILER_FLAGS(-Wvolatile-register-var)
gl_COMPILER_FLAGS(-Winvalid-pch)
gl_COMPILER_FLAGS(-Wno-system-headers)
- gl_COMPILER_FLAGS(-Woverloaded-virtual)
+ gl_COMPILER_FLAGS(-Woverloaded-virtual)
AC_SUBST([WARNING_CFLAGS], [$COMPILER_FLAGS])
AC_DEFINE([lint], 1, [Define to 1 if the compiler is checking for lint.])
COMPILER_FLAGS=
fi
else
AC_CHECK_DECL([__SUNPRO_CC], [SUNCC=yes], [SUNCC=no])
-
+
# Set up for sun CC compiler
if test x$SUNCC = xyes; then
if test "${enableval}" = yes; then
@@ -118,7 +118,7 @@ LIBS=$gl_saved_libs
gl_CLOCK_TIME
-# Enable Valgrind
+# Enable Valgrind
AC_ARG_ENABLE([valgrind],
[AS_HELP_STRING([--enable-valgrind],
[run valgrind memory checker on tests, if available (default yes)])],
@@ -203,13 +203,26 @@ AM_CONDITIONAL([HAVE_RUBY_DEVEL], [test -f $RUBY_INC/ruby.h && test -n "$SWIG"])
# Python bindings: To build python wrappers, the python-devel files must be present.
AM_PATH_PYTHON()
AS_IF([test -n "$PYTHON"], [
- PKG_CHECK_MODULES([PYTHON], [python], [have_python_dev=yes],[
+ PKG_CHECK_MODULES([PYTHON], [python-$PYTHON_VERSION], [have_python_dev=yes],[
+ # We didn't find pkg-config information for python-2.7 this
+ # may mean we have an earlier python version:
+ # Try to find the include and library files directly in the default
+ # location
+ AC_MSG_WARN([Didn't find Python 2.7 developer libs - looking for older version])
+ PYTHON_INC=$($PYTHON -c 'import distutils.sysconfig; print distutils.sysconfig.get_python_inc()')
+ AC_CHECK_LIB([python$PYTHON_VERSION],[Py_Initialize])
+ AC_CHECK_FILE(["$PYTHON_INC/Python.h"],[
+ PYTHON_CFLAGS="-I$PYTHON_INC"
+ PYTHON_LIBS="-lpython$PYTHON_VERSION"
+ have_python_dev=yes
+ ],[
if test yes = "$with_python" ; then
AC_MSG_ERROR([Couldn't find Python developer libs - you probably need to install a python-dev or python-devel package])
else
AC_MSG_WARN([Couldn't find Python developer libs - you probably don't have a python-dev or python-devel package installed])
fi
])
+ ])
AC_SUBST(PYTHON_CFLAGS)
AC_SUBST(PYTHON_LIBS)
])
@@ -249,7 +262,7 @@ tmp_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS -L/usr/lib/openais -L/usr/lib64/openais -L/usr/lib/corosync -L/usr/lib64/corosync"
AC_CHECK_LIB([cpg],[cpg_local_get],[have_libcpg=yes],)
AC_CHECK_HEADERS([openais/cpg.h corosync/cpg.h],[have_cpg_h=yes],)
-AC_ARG_WITH([cpg],
+AC_ARG_WITH([cpg],
[AS_HELP_STRING([--with-cpg], [Build with CPG support for clustering.])],
[case "${withval}" in
yes) # yes - require dependencies
@@ -257,7 +270,7 @@ AC_ARG_WITH([cpg],
test x$have_cpg_h = xyes || AC_MSG_ERROR([cpg.h not found, install openais-devel or corosync-devel])
with_cpg=yes
;;
- no) with_cpg=no ;;
+ no) with_cpg=no ;;
*) AC_MSG_ERROR([Bad value ${withval} for --with-cpg option]) ;;
esac],
[ # not specified - use if present
@@ -265,12 +278,14 @@ AC_ARG_WITH([cpg],
]
)
AM_CONDITIONAL([HAVE_LIBCPG], [test x$with_cpg = xyes])
+AC_SUBST(USE_CPG, [$with_cpg])
+
# Clean up unnceccassary flags if we don't use clustering
AS_IF([test ! x$with_cpg = xyes], [LDFLAGS=$tmp_LDFLAGS])
AC_CHECK_LIB([cman],[cman_is_quorate],have_libcman=yes,)
AC_CHECK_HEADERS([libcman.h],have_libcman_h=yes,)
-AC_ARG_WITH([libcman],
+AC_ARG_WITH([libcman],
[AS_HELP_STRING([--with-libcman], [Integration with libcman quorum service.])],
[case "${withval}" in
yes) # yes - require dependencies
@@ -278,7 +293,7 @@ AC_ARG_WITH([libcman],
test x$have_libcman_h = xyes || AC_MSG_ERROR([libcman.h not found, install cman-devel or cmanlib-devel])
with_libcman=yes
;;
- no) with_libcman=no ;;
+ no) with_libcman=no ;;
*) AC_MSG_ERROR([Bad value ${withval} for --with-libcman option]) ;;
esac],
[ # not specified - use if present and we're using with_cpg
@@ -430,9 +445,9 @@ AC_ARG_WITH([ssl],
[
with_SSL=yes
AC_PATH_PROG([NSPR_CONFIG], [nspr-config])
- AS_IF([test x$NSPR_CONFIG = x], [with_SSL=no],
+ AS_IF([test x$NSPR_CONFIG = x], [with_SSL=no],
[AC_PATH_PROG([NSS_CONFIG], [nss-config])
- AS_IF([test x$NSS_CONFIG = x], [with_SSL=no],
+ AS_IF([test x$NSS_CONFIG = x], [with_SSL=no],
[SSL_CFLAGS="`$NSPR_CONFIG --cflags` `$NSS_CONFIG --cflags`"
SSL_LDFLAGS="`$NSPR_CONFIG --libs` `$NSS_CONFIG --libs`"])])
]
@@ -502,11 +517,11 @@ AC_ARG_WITH([probes],
)
# Check for some syslog capabilities not present in all systems
-AC_TRY_COMPILE([#include <sys/syslog.h>],
+AC_TRY_COMPILE([#include <sys/syslog.h>],
[int v = LOG_AUTHPRIV;],
[AC_DEFINE([HAVE_LOG_AUTHPRIV], [1], [Set to 1 whether LOG_AUTHPRIV is supported.])],)
-AC_TRY_COMPILE([#include <sys/syslog.h>],
+AC_TRY_COMPILE([#include <sys/syslog.h>],
[int v = LOG_FTP;],
[AC_DEFINE([HAVE_LOG_FTP], [1], [Set to 1 whether LOG_FTP is supported.])],)
@@ -524,8 +539,6 @@ AC_CHECK_LIB([nsl],[getipnodebyname],[NSL_LIB="-lnsl"],[NSL_LIB=""],[])
SOCKLIBS="$SOCKET_LIB $NSL_LIB"
AC_SUBST([SOCKLIBS])
-AM_PATH_PYTHON()
-
# Used by env scripts to find libraries in cmake or autoconf builds.
builddir_lib_suffix="/.libs"
AC_SUBST([builddir_lib_suffix])
@@ -560,6 +573,7 @@ AC_CONFIG_FILES([
managementgen/Makefile
etc/Makefile
src/Makefile
+ src/tests/cpg_check.sh
src/tests/Makefile
src/tests/test_env.sh
src/tests/install_env.sh
diff --git a/qpid/cpp/etc/Makefile.am b/qpid/cpp/etc/Makefile.am
index ab7c62d95d..aa41c65b37 100644
--- a/qpid/cpp/etc/Makefile.am
+++ b/qpid/cpp/etc/Makefile.am
@@ -20,9 +20,10 @@ SASL_CONF = sasl2/qpidd.conf
EXTRA_DIST = \
$(SASL_CONF) \
- qpidd qpidd-primary qpidd.conf qpidc.conf CMakeLists.txt
+ qpidd.in qpidd-primary.in qpidd.conf qpidc.conf CMakeLists.txt \
+ cluster.conf-example.xml.in
-confdir=$(sysconfdir)/qpid
+confdir = $(sysconfdir)/qpid
nobase_conf_DATA=\
qpidc.conf
@@ -32,5 +33,23 @@ nobase_sysconf_DATA = \
if HAVE_SASL
nobase_sysconf_DATA += \
$(SASL_CONF)
-
endif
+
+# Substitute values for directories in init scripts.
+#
+# We can't use autoconf substitution directly because it leaves
+# ${prefix} and ${exec_prefix} unexpanded. Substitute with sed
+# scripts.
+SUBST="s|!!sysconfdir!!|${sysconfdir}|;s|!!sbindir!!|${sbindir}|;s|!!bindir!!|${bindir}|"
+qpidd: qpidd.in
+ sed $(SUBST) $< > $@
+qpidd-primary: qpidd-primary.in
+ sed $(SUBST) $< > $@
+cluster.conf-example.xml: cluster.conf-example.xml.in
+ sed $(SUBST) $< > $@
+
+CLEANFILES = qpidd qpidd-primary cluster.conf-example.xml
+
+initddir = $(sysconfdir)/init.d
+nobase_initd_SCRIPTS = qpidd qpidd-primary
+
diff --git a/qpid/cpp/etc/cluster.conf-example.xml b/qpid/cpp/etc/cluster.conf-example.xml.in
index 555843fd8e..14b961a363 100644
--- a/qpid/cpp/etc/cluster.conf-example.xml
+++ b/qpid/cpp/etc/cluster.conf-example.xml.in
@@ -1,49 +1,43 @@
<?xml version="1.0"?>
<!--
-This is an example of a cluster.conf file to run qpidd under rgmanager.
-This example assumes a 3 node cluster, with nodes named mrg32, mrg34 and mrg35.
-There is no fencing in this configuration.
+This is an example of a cluster.conf file to run qpidd HA under rgmanager.
+This example assumes a 3 node cluster, with nodes named node1, node2 and node3.
+
+NOTE: fencing is not shown, it should be configured in a real cluster configuration.
-->
-<cluster alias="qpid-hot-standby" config_version="4" name="qpid-hot-standby">
- <!-- The cluster has 3 nodes. Each has a unique nodid and one vote for quorum. -->
+<cluster name="qpid-test" config_version="18">
+ <!-- The cluster has 3 nodes. Each has a unique nodid and one vote
+ for quorum. -->
<clusternodes>
- <clusternode name="mrg32" nodeid="1">
- <fence/>
- </clusternode>
- <clusternode name="mrg34" nodeid="2">
- <fence/>
- </clusternode>
- <clusternode name="mrg35" nodeid="3">
- <fence/>
- </clusternode>
+ <clusternode name="node1.example.com" nodeid="1"/>
+ <clusternode name="node2.example.com" nodeid="2"/>
+ <clusternode name="node3.example.com" nodeid="3"/>
</clusternodes>
- <cman/>
- <!-- Optionally you can specify logging, this is the most verbose -->
- <rm log_level="7" log_facility="daemon">
-
+ <!-- Resouce Manager configuration. -->
+ <rm>
<!--
There is a failoverdomain for each node containing just that node.
- This lets us stipulate that the qpidd service should always run on all nodes.
+ This lets us stipulate that the qpidd service should always run on each node.
-->
<failoverdomains>
- <failoverdomain name="mrg32-domain" restricted="1">
- <failoverdomainnode name="mrg32"/>
+ <failoverdomain name="node1-domain" restricted="1">
+ <failoverdomainnode name="node1.example.com"/>
</failoverdomain>
- <failoverdomain name="mrg34-domain" restricted="1">
- <failoverdomainnode name="mrg34"/>
+ <failoverdomain name="node2-domain" restricted="1">
+ <failoverdomainnode name="node2.example.com"/>
</failoverdomain>
- <failoverdomain name="mrg35-domain" restricted="1">
- <failoverdomainnode name="mrg35"/>
+ <failoverdomain name="node3-domain" restricted="1">
+ <failoverdomainnode name="node3.example.com"/>
</failoverdomain>
</failoverdomains>
<resources>
<!-- This script starts a qpidd broker acting as a backup. -->
- <script file="/etc/init.d/qpidd" name="qpidd"/>
+ <script file="!!sysconfdir!!/init.d/qpidd" name="qpidd"/>
<!-- This script promotes the qpidd broker on this node to primary. -->
- <script file="/etc/init.d/qpidd-primary" name="qpidd-primary"/>
+ <script file="!!sysconfdir!!/init.d/qpidd-primary" name="qpidd-primary"/>
<!-- This is a virtual IP address for broker replication traffic. -->
<ip address="20.0.10.200" monitor_link="1"/>
@@ -53,20 +47,20 @@ There is no fencing in this configuration.
</resources>
<!-- There is a qpidd service on each node, it should be restarted if it fails. -->
- <service name="mrg32-qpidd-service" domain="mrg32-domain" recovery="restart">
+ <service name="node1-qpidd-service" domain="node1-domain" recovery="restart">
<script ref="qpidd"/>
</service>
- <service name="mrg34-qpidd-service" domain="mrg34-domain" recovery="restart">
+ <service name="node2-qpidd-service" domain="node2-domain" recovery="restart">
<script ref="qpidd"/>
</service>
- <service name="mrg35-qpidd-service" domain="mrg35-domain" recovery="restart">
+ <service name="node3-qpidd-service" domain="node3-domain" recovery="restart">
<script ref="qpidd"/>
</service>
<!-- There should always be a single qpidd-primary service, it can run on any node. -->
<service name="qpidd-primary-service" autostart="1" exclusive="0" recovery="relocate">
<script ref="qpidd-primary"/>
- <!-- The primary has the IP addresses for brokers and clients. -->
+ <!-- The primary has the IP addresses for brokers and clients to connect. -->
<ip ref="20.0.10.200"/>
<ip ref="20.0.20.200"/>
</service>
@@ -74,4 +68,3 @@ There is no fencing in this configuration.
<fencedevices/>
<fence_daemon clean_start="0" post_fail_delay="0" post_join_delay="3"/>
</cluster>
-
diff --git a/qpid/cpp/etc/qpidd-primary b/qpid/cpp/etc/qpidd-primary.in
index 2bbc22129d..39700bead3 100755
--- a/qpid/cpp/etc/qpidd-primary
+++ b/qpid/cpp/etc/qpidd-primary.in
@@ -40,13 +40,13 @@ prog=qpidd
# Source function library.
. /etc/rc.d/init.d/functions
-if [ -f /etc/sysconfig/$prog ] ; then
- . /etc/sysconfig/$prog
+if [ -f !!sysconfdir!!/sysconfig/$prog ] ; then
+ . !!sysconfdir!!/sysconfig/$prog
fi
-# The following variables can be overridden in /etc/sysconfig/$prog
+# The following variables can be overridden in !!sysconfdir!!/sysconfig/$prog
[[ $QPID_PORT ]] || QPID_PORT=5672
-[[ $QPID_HA ]] || QPID_HA=/usr/bin/qpid-ha
+[[ $QPID_HA ]] || QPID_HA=!!bindir!!/qpid-ha
export QPID_PORT
RETVAL=0
diff --git a/qpid/cpp/etc/qpidd b/qpid/cpp/etc/qpidd.in
index 07cbb825d5..2b43625b7d 100755
--- a/qpid/cpp/etc/qpidd
+++ b/qpid/cpp/etc/qpidd.in
@@ -39,18 +39,19 @@ prog=qpidd
lockfile=/var/lock/subsys/$prog
pidfile=/var/run/qpidd.pid
+# Source configuration
+if [ -f !!sysconfdir!!/sysconfig/$prog ] ; then
+ . !!sysconfdir!!/sysconfig/$prog
+fi
+
# Source function library.
. /etc/rc.d/init.d/functions
-if [ -f /etc/sysconfig/$prog ] ; then
- . /etc/sysconfig/$prog
-fi
-
RETVAL=0
#ensure binary is present and executable
-if [[ !(-x /usr/sbin/$prog) ]] ; then
- echo "/usr/sbin/$prog not found or not executable"
+if [[ !(-x !!sbindir!!/$prog) ]] ; then
+ echo "!!sbindir!!/$prog not found or not executable"
exit 5
fi
@@ -64,7 +65,7 @@ fi
start() {
[[ $QPID_DATA_DIR ]] || QPID_DATA_DIR=/var/lib/qpidd
echo -n $"Starting Qpid AMQP daemon: "
- daemon --pidfile $pidfile --check $prog --user qpidd /usr/sbin/$prog --data-dir $QPID_DATA_DIR --daemon $QPIDD_OPTIONS
+ daemon --pidfile $pidfile --check $prog --user qpidd !!sbindir!!/$prog --data-dir $QPID_DATA_DIR --daemon $QPIDD_OPTIONS
RETVAL=$?
echo
[ $RETVAL = 0 ] && touch $lockfile
@@ -72,7 +73,7 @@ start() {
touch $pidfile
chown qpidd.qpidd $pidfile
[ -x /sbin/restorecon ] && /sbin/restorecon $pidfile
- runuser - -s /bin/sh qpidd -c "/usr/sbin/$prog --check > $pidfile"
+ runuser - -s /bin/sh qpidd -c "!!sbindir!!/$prog --check > $pidfile"
fi
return $RETVAL
}
diff --git a/qpid/cpp/include/qpid/Options.h b/qpid/cpp/include/qpid/Options.h
index 63d91c2d72..0bbe7b704f 100644
--- a/qpid/cpp/include/qpid/Options.h
+++ b/qpid/cpp/include/qpid/Options.h
@@ -81,13 +81,12 @@ po::value_semantic* optValue(T& value, const char* name) {
*/
template <class T>
po::value_semantic* optValue(std::vector<T>& value, const char* name) {
- using namespace std;
- ostringstream os;
- copy(value.begin(), value.end(), ostream_iterator<T>(os, " "));
- string val=os.str();
+ std::ostringstream os;
+ std::copy(value.begin(), value.end(), std::ostream_iterator<T>(os, " "));
+ std::string val=os.str();
if (!val.empty())
val.erase(val.end()-1); // Remove trailing " "
- return (new OptionValue<vector<T> >(value, prettyArg(name, val)));
+ return (new OptionValue<std::vector<T> >(value, prettyArg(name, val)));
}
/** Create a boolean switch value. Presence of the option sets the value. */
@@ -163,10 +162,12 @@ struct Options : public po::options_description {
*/
struct CommonOptions : public Options {
QPID_COMMON_EXTERN CommonOptions(const std::string& name=std::string(),
- const std::string& configfile=std::string());
+ const std::string& configfile=std::string(),
+ const std::string& clientConfigFile=std::string());
bool help;
bool version;
std::string config;
+ std::string clientConfig;
};
diff --git a/qpid/cpp/include/qpid/RangeSet.h b/qpid/cpp/include/qpid/RangeSet.h
index 36991fd784..ef0ad032da 100644
--- a/qpid/cpp/include/qpid/RangeSet.h
+++ b/qpid/cpp/include/qpid/RangeSet.h
@@ -224,17 +224,15 @@ bool RangeSet<T>::contains(const Range<T>& r) const {
template <class T> void RangeSet<T>::addRange(const Range<T>& r) {
if (r.empty()) return;
- typename Ranges::iterator i =
- std::lower_bound(ranges.begin(), ranges.end(), r);
+ typename Ranges::iterator i = std::lower_bound(ranges.begin(), ranges.end(), r);
if (i == ranges.end() || !i->touching(r))
- ranges.insert(i, r);
+ ranges.insert(i, r); // No overlap
else {
i->merge(r);
typename Ranges::iterator j = i;
- if (++j != ranges.end() && i->touching(*j)) {
+ while (++j != ranges.end() && i->touching(*j))
i->merge(*j);
- ranges.erase(j);
- }
+ ranges.erase(i+1,j);
}
}
diff --git a/qpid/cpp/include/qpid/Url.h b/qpid/cpp/include/qpid/Url.h
index 6dc7c787ee..b3ff9576e2 100644
--- a/qpid/cpp/include/qpid/Url.h
+++ b/qpid/cpp/include/qpid/Url.h
@@ -39,7 +39,7 @@ struct Url : public std::vector<Address> {
* on a multi-homed host. */
QPID_COMMON_EXTERN static Url getIpAddressesUrl(uint16_t port);
- struct Invalid : public Exception { Invalid(const std::string& s); };
+ struct Invalid : public Exception { QPID_COMMON_EXTERN Invalid(const std::string& s); };
/** Convert to string form. */
QPID_COMMON_EXTERN std::string str() const;
diff --git a/qpid/cpp/include/qpid/client/SessionBase_0_10.h b/qpid/cpp/include/qpid/client/SessionBase_0_10.h
index ea50ab32f7..630987c11d 100644
--- a/qpid/cpp/include/qpid/client/SessionBase_0_10.h
+++ b/qpid/cpp/include/qpid/client/SessionBase_0_10.h
@@ -36,14 +36,13 @@ namespace client {
class Connection;
class SessionImpl;
-using std::string;
-using framing::Content;
-using framing::FieldTable;
-using framing::SequenceNumber;
-using framing::SequenceSet;
-using framing::SequenceNumberSet;
+using qpid::framing::Content;
+using qpid::framing::FieldTable;
+using qpid::framing::SequenceNumber;
+using qpid::framing::SequenceSet;
+using qpid::framing::SequenceNumberSet;
using qpid::SessionId;
-using framing::Xid;
+using qpid::framing::Xid;
/** Unit of message credit: messages or bytes */
enum CreditUnit { MESSAGE_CREDIT=0, BYTE_CREDIT=1, UNLIMITED_CREDIT=0xFFFFFFFF };
diff --git a/qpid/cpp/include/qpid/client/SubscriptionSettings.h b/qpid/cpp/include/qpid/client/SubscriptionSettings.h
index b4cb302b56..bee39f6816 100644
--- a/qpid/cpp/include/qpid/client/SubscriptionSettings.h
+++ b/qpid/cpp/include/qpid/client/SubscriptionSettings.h
@@ -28,7 +28,19 @@ namespace qpid {
namespace client {
/** Bring AMQP enum definitions for message class into this namespace. */
-using namespace qpid::framing::message;
+using qpid::framing::message::AcceptMode;
+using qpid::framing::message::AcquireMode;
+using qpid::framing::message::ACCEPT_MODE_EXPLICIT;
+using qpid::framing::message::ACCEPT_MODE_NONE;
+using qpid::framing::message::ACQUIRE_MODE_NOT_ACQUIRED;
+using qpid::framing::message::ACQUIRE_MODE_PRE_ACQUIRED;
+using qpid::framing::message::CREDIT_UNIT_BYTE;
+using qpid::framing::message::CREDIT_UNIT_MESSAGE;
+using qpid::framing::message::DELIVERY_MODE_NON_PERSISTENT;
+using qpid::framing::message::DELIVERY_MODE_PERSISTENT;
+using qpid::framing::message::FLOW_MODE_CREDIT;
+using qpid::framing::message::FLOW_MODE_WINDOW;
+
enum CompletionMode {
MANUAL_COMPLETION = 0,
diff --git a/qpid/cpp/include/qpid/framing/Array.h b/qpid/cpp/include/qpid/framing/Array.h
index 1e97be3bb4..6254f6271a 100644
--- a/qpid/cpp/include/qpid/framing/Array.h
+++ b/qpid/cpp/include/qpid/framing/Array.h
@@ -1,3 +1,6 @@
+#ifndef QPID_FRAMING_ARRAY_H
+#define QPID_FRAMING_ARRAY_H
+
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -18,21 +21,22 @@
* under the License.
*
*/
+
#include "qpid/framing/amqp_types.h"
-#include "qpid/framing/FieldValue.h"
#include "qpid/framing/TypeCode.h"
+
#include <boost/shared_ptr.hpp>
+
#include <iostream>
#include <vector>
-#include "qpid/CommonImportExport.h"
-#ifndef _Array_
-#define _Array_
+#include "qpid/CommonImportExport.h"
namespace qpid {
namespace framing {
class Buffer;
+class FieldValue;
class QPID_COMMON_CLASS_EXTERN Array
{
@@ -75,12 +79,10 @@ class QPID_COMMON_CLASS_EXTERN Array
// Non-std interface
QPID_COMMON_INLINE_EXTERN void add(ValuePtr value) { push_back(value); }
- template <class T>
- void collect(std::vector<T>& out) const
- {
- for (ValueVector::const_iterator i = values.begin(); i != values.end(); ++i) {
- out.push_back((*i)->get<T>());
- }
+ // For use in standard algorithms
+ template <typename R, typename V>
+ static R get(const V& v) {
+ return v->template get<R>();
}
private:
diff --git a/qpid/cpp/include/qpid/framing/Buffer.h b/qpid/cpp/include/qpid/framing/Buffer.h
index 8b08e60762..2ccad3bd57 100644
--- a/qpid/cpp/include/qpid/framing/Buffer.h
+++ b/qpid/cpp/include/qpid/framing/Buffer.h
@@ -1,3 +1,6 @@
+#ifndef QPID_FRAMING_BUFFER_H
+#define QPID_FRAMING_BUFFER_H
+
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -18,13 +21,12 @@
* under the License.
*
*/
-#include "qpid/framing/amqp_types.h"
+
#include "qpid/Exception.h"
#include "qpid/CommonImportExport.h"
-#include <boost/iterator/iterator_facade.hpp>
+#include "qpid/sys/IntegerTypes.h"
-#ifndef _Buffer_
-#define _Buffer_
+#include <string>
namespace qpid {
namespace framing {
@@ -41,42 +43,18 @@ class QPID_COMMON_CLASS_EXTERN Buffer
uint32_t size;
char* data;
uint32_t position;
- uint32_t r_position;
public:
void checkAvailable(uint32_t count) { if (position + count > size) throw OutOfBounds(); }
- /** Buffer input/output iterator.
- * Supports using an amqp_0_10::Codec with a framing::Buffer.
- */
- class Iterator : public boost::iterator_facade<
- Iterator, char, boost::random_access_traversal_tag>
- {
- public:
- Iterator(Buffer& b) : buffer(&b) {}
-
- private:
- friend class boost::iterator_core_access;
- char& dereference() const { return buffer->data[buffer->position]; }
- void increment() { ++buffer->position; }
- bool equal(const Iterator& x) const { return buffer == x.buffer; }
-
- Buffer* buffer;
- };
-
- friend class Iterator;
-
QPID_COMMON_EXTERN Buffer(char* data=0, uint32_t size=0);
- QPID_COMMON_EXTERN void record();
- QPID_COMMON_EXTERN void restore(bool reRecord = false);
QPID_COMMON_EXTERN void reset();
QPID_COMMON_INLINE_EXTERN uint32_t available() { return size - position; }
QPID_COMMON_INLINE_EXTERN uint32_t getSize() { return size; }
QPID_COMMON_INLINE_EXTERN uint32_t getPosition() { return position; }
QPID_COMMON_INLINE_EXTERN void setPosition(uint32_t p) { position = p; }
- QPID_COMMON_INLINE_EXTERN Iterator getIterator() { return Iterator(*this); }
QPID_COMMON_INLINE_EXTERN char* getPointer() { return data; }
QPID_COMMON_EXTERN void putOctet(uint8_t i);
@@ -108,16 +86,16 @@ class QPID_COMMON_CLASS_EXTERN Buffer
template <int n>
QPID_COMMON_EXTERN void putUInt(uint64_t);
- QPID_COMMON_EXTERN void putShortString(const string& s);
- QPID_COMMON_EXTERN void putMediumString(const string& s);
- QPID_COMMON_EXTERN void putLongString(const string& s);
- QPID_COMMON_EXTERN void getShortString(string& s);
- QPID_COMMON_EXTERN void getMediumString(string& s);
- QPID_COMMON_EXTERN void getLongString(string& s);
+ QPID_COMMON_EXTERN void putShortString(const std::string& s);
+ QPID_COMMON_EXTERN void putMediumString(const std::string& s);
+ QPID_COMMON_EXTERN void putLongString(const std::string& s);
+ QPID_COMMON_EXTERN void getShortString(std::string& s);
+ QPID_COMMON_EXTERN void getMediumString(std::string& s);
+ QPID_COMMON_EXTERN void getLongString(std::string& s);
QPID_COMMON_EXTERN void getBin128(uint8_t* b);
- QPID_COMMON_EXTERN void putRawData(const string& s);
- QPID_COMMON_EXTERN void getRawData(string& s, uint32_t size);
+ QPID_COMMON_EXTERN void putRawData(const std::string& s);
+ QPID_COMMON_EXTERN void getRawData(std::string& s, uint32_t size);
QPID_COMMON_EXTERN void putRawData(const uint8_t* data, size_t size);
QPID_COMMON_EXTERN void getRawData(uint8_t* data, size_t size);
diff --git a/qpid/cpp/include/qpid/framing/FieldTable.h b/qpid/cpp/include/qpid/framing/FieldTable.h
index 293fb2eed7..1986a72d10 100644
--- a/qpid/cpp/include/qpid/framing/FieldTable.h
+++ b/qpid/cpp/include/qpid/framing/FieldTable.h
@@ -1,3 +1,6 @@
+#ifndef _FieldTable_
+#define _FieldTable_
+
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -18,16 +21,17 @@
* under the License.
*
*/
-#include <iostream>
-#include <vector>
+
+#include "qpid/framing/amqp_types.h"
+#include "qpid/sys/Mutex.h"
+
#include <boost/shared_ptr.hpp>
#include <boost/shared_array.hpp>
+
+#include <iosfwd>
#include <map>
-#include "qpid/framing/amqp_types.h"
-#include "qpid/CommonImportExport.h"
-#ifndef _FieldTable_
-#define _FieldTable_
+#include "qpid/CommonImportExport.h"
namespace qpid {
/**
@@ -114,11 +118,13 @@ class FieldTable
private:
void realDecode() const;
- void flushRawCache() const;
+ void flushRawCache();
+ mutable qpid::sys::Mutex lock;
mutable ValueMap values;
mutable boost::shared_array<uint8_t> cachedBytes;
mutable uint32_t cachedSize; // if = 0 then non cached size as 0 is not a legal size
+ mutable bool newBytes;
QPID_COMMON_EXTERN friend std::ostream& operator<<(std::ostream& out, const FieldTable& body);
};
diff --git a/qpid/cpp/include/qpid/framing/ProtocolVersion.h b/qpid/cpp/include/qpid/framing/ProtocolVersion.h
index 30094c165d..26d628e41c 100644
--- a/qpid/cpp/include/qpid/framing/ProtocolVersion.h
+++ b/qpid/cpp/include/qpid/framing/ProtocolVersion.h
@@ -24,6 +24,8 @@
#include "qpid/framing/amqp_types.h"
#include "qpid/CommonImportExport.h"
+#include <string>
+
namespace qpid
{
namespace framing
diff --git a/qpid/cpp/include/qpid/framing/SequenceNumber.h b/qpid/cpp/include/qpid/framing/SequenceNumber.h
index dd85d97a52..00fa2469c8 100644
--- a/qpid/cpp/include/qpid/framing/SequenceNumber.h
+++ b/qpid/cpp/include/qpid/framing/SequenceNumber.h
@@ -57,12 +57,18 @@ boost::equality_comparable<
QPID_COMMON_EXTERN uint32_t encodedSize() const;
template <class S> void serialize(S& s) { s(value); }
-
- friend inline int32_t operator-(const SequenceNumber& a, const SequenceNumber& b);
};
inline int32_t operator-(const SequenceNumber& a, const SequenceNumber& b) {
- return int32_t(a.value - b.value);
+ return int32_t(a.getValue() - b.getValue());
+}
+
+inline SequenceNumber operator+(const SequenceNumber& a, int32_t n) {
+ return SequenceNumber(a.getValue() + n);
+}
+
+inline SequenceNumber operator-(const SequenceNumber& a, int32_t n) {
+ return SequenceNumber(a.getValue() - n);
}
struct Window
diff --git a/qpid/cpp/include/qpid/framing/StructHelper.h b/qpid/cpp/include/qpid/framing/StructHelper.h
index 21f9b91fa9..fe2fa64ce7 100644
--- a/qpid/cpp/include/qpid/framing/StructHelper.h
+++ b/qpid/cpp/include/qpid/framing/StructHelper.h
@@ -34,7 +34,7 @@ class QPID_COMMON_CLASS_EXTERN StructHelper
{
public:
- template <class T> void encode(const T t, std::string& data) {
+ template <class T> void encode(const T& t, std::string& data) {
uint32_t size = t.bodySize() + 2/*type*/;
data.resize(size);
Buffer wbuffer(const_cast<char*>(data.data()), size);
diff --git a/qpid/cpp/include/qpid/framing/Uuid.h b/qpid/cpp/include/qpid/framing/Uuid.h
index ccfd7e9534..e9e56ed7c9 100644
--- a/qpid/cpp/include/qpid/framing/Uuid.h
+++ b/qpid/cpp/include/qpid/framing/Uuid.h
@@ -48,6 +48,9 @@ struct Uuid : public boost::array<uint8_t, 16> {
/** Copy from 16 bytes of data. */
QPID_COMMON_EXTERN Uuid(const uint8_t* data);
+ /** Parse format 1b4e28ba-2fa1-11d2-883f-b9a761bde3fb. */
+ QPID_COMMON_EXTERN Uuid(const std::string&);
+
// Default op= and copy ctor are fine.
// boost::array gives us ==, < etc.
diff --git a/qpid/cpp/include/qpid/framing/amqp_types.h b/qpid/cpp/include/qpid/framing/amqp_types.h
index d9088b7a12..2072a83904 100644
--- a/qpid/cpp/include/qpid/framing/amqp_types.h
+++ b/qpid/cpp/include/qpid/framing/amqp_types.h
@@ -27,12 +27,10 @@
*/
#include "qpid/sys/IntegerTypes.h"
-#include <string>
namespace qpid {
namespace framing {
-using std::string;
typedef uint8_t FrameType;
typedef uint16_t ChannelId;
typedef uint32_t BatchOffset;
diff --git a/qpid/cpp/include/qpid/log/Logger.h b/qpid/cpp/include/qpid/log/Logger.h
index d255b7e150..9464fa52dd 100644
--- a/qpid/cpp/include/qpid/log/Logger.h
+++ b/qpid/cpp/include/qpid/log/Logger.h
@@ -36,7 +36,7 @@ namespace log {
class QPID_COMMON_CLASS_EXTERN Logger : private boost::noncopyable {
public:
/** Flags indicating what to include in the log output */
- enum FormatFlag { FILE=1, LINE=2, FUNCTION=4, LEVEL=8, TIME=16, THREAD=32, HIRES=64};
+ enum FormatFlag { FILE=1, LINE=2, FUNCTION=4, LEVEL=8, TIME=16, THREAD=32, HIRES=64, CATEGORY=128};
/**
* Logging output sink.
diff --git a/qpid/cpp/include/qpid/log/Options.h b/qpid/cpp/include/qpid/log/Options.h
index 17cbfde9bc..819f2c85f1 100644
--- a/qpid/cpp/include/qpid/log/Options.h
+++ b/qpid/cpp/include/qpid/log/Options.h
@@ -39,7 +39,7 @@ struct Options : public qpid::Options {
std::string argv0;
std::string name;
std::vector<std::string> selectors;
- bool time, level, thread, source, function, hiresTs;
+ bool time, level, thread, source, function, hiresTs, category;
bool trace;
std::string prefix;
std::auto_ptr<SinkOptions> sinkOptions;
diff --git a/qpid/cpp/include/qpid/log/Selector.h b/qpid/cpp/include/qpid/log/Selector.h
index 061152d7e2..498d4a7342 100644
--- a/qpid/cpp/include/qpid/log/Selector.h
+++ b/qpid/cpp/include/qpid/log/Selector.h
@@ -35,17 +35,24 @@ struct Options;
class Selector {
public:
/** Empty selector selects nothing */
- Selector() {}
+ Selector() {
+ reset();
+ }
/** Set selector from Options */
QPID_COMMON_EXTERN Selector(const Options&);
/** Equavlient to: Selector s; s.enable(l, s) */
Selector(Level l, const std::string& s=std::string()) {
+ reset();
enable(l,s);
}
- Selector(const std::string& enableStr) { enable(enableStr); }
+ Selector(const std::string& enableStr) {
+ reset();
+ enable(enableStr);
+ }
+
/**
* Enable messages with level in levels where the file
* name contains substring. Empty string matches all.
@@ -54,14 +61,30 @@ class Selector {
substrings[level].push_back(substring);
}
+ /**
+ * Enable messages at this level for this category
+ */
+ void enable(Level level, Category category) {
+ catFlags[level][category] = true;
+ }
+
/** Enable based on a 'level[+]:file' string */
QPID_COMMON_EXTERN void enable(const std::string& enableStr);
/** True if level is enabled for file. */
QPID_COMMON_EXTERN bool isEnabled(Level level, const char* function);
+ QPID_COMMON_EXTERN bool isEnabled(Level level, const char* function, Category category);
+
+ /** Reset the category enable flags */
+ QPID_COMMON_EXTERN void reset() {
+ for (int lt = 0; lt < LevelTraits::COUNT; ++lt)
+ for (int ct = 0; ct < CategoryTraits::COUNT; ++ct)
+ catFlags[lt][ct] = false;
+ }
private:
std::vector<std::string> substrings[LevelTraits::COUNT];
+ bool catFlags[LevelTraits::COUNT][CategoryTraits::COUNT];
};
diff --git a/qpid/cpp/include/qpid/log/Statement.h b/qpid/cpp/include/qpid/log/Statement.h
index 7b3ab60b81..f61596917a 100644
--- a/qpid/cpp/include/qpid/log/Statement.h
+++ b/qpid/cpp/include/qpid/log/Statement.h
@@ -22,6 +22,7 @@
#include "qpid/Msg.h"
#include "qpid/CommonImportExport.h"
#include <boost/current_function.hpp>
+#include <list>
namespace qpid {
namespace log {
@@ -55,15 +56,118 @@ struct LevelTraits {
static const char* name(Level);
};
-/** POD struct representing a logging statement in source code. */
+/** Formal message categories
+ * https://issues.apache.org/jira/browse/QPID-3902
+ *
+ * Category Source code directory
+ * -------- ---------------------
+ * Security acl ssl gssapi sasl cyrus
+ * Broker broker
+ * Management agent console qmf
+ * Protocol amqp_0_10 framing
+ * System log sys types xml thread mutex fork pipe time ...
+ * HA cluster ha replication
+ * Messaging messaging
+ * Client client
+ * Store store
+ * Network tcp rdma AsynchIO socket epoll
+ * Test
+ * Unspecified
+ */
+enum Category { security, broker, management, protocol, system, ha, messaging,
+ store, network, test, client, unspecified };
+struct CategoryTraits {
+ static const int COUNT=unspecified+1;
+
+ /** Test if given name is a Category name
+ */
+ static bool isCategory(const std::string& name);
+
+ /** Get category from string name
+ * @exception if name invalid.
+ */
+ static Category category(const char* name);
+
+ /** Get category from string name.
+ * @exception if name invalid.
+ */
+ static Category category(const std::string& name) {
+ return category(name.c_str());
+ }
+
+ /** String name of category */
+ static const char* name(Category);
+};
+
+
+class CategoryFileNameHints {
+public:
+ CategoryFileNameHints(){
+ hintList.push_back(std::make_pair("AsynchIo", network));
+ hintList.push_back(std::make_pair("TCP", network));
+ hintList.push_back(std::make_pair("epoll", network));
+ hintList.push_back(std::make_pair("Pollable", network));
+ hintList.push_back(std::make_pair("Socket", network));
+
+ hintList.push_back(std::make_pair("Sasl", security));
+ hintList.push_back(std::make_pair("Ssl", security));
+ hintList.push_back(std::make_pair("Acl", security));
+ hintList.push_back(std::make_pair("acl", security));
+ hintList.push_back(std::make_pair("cyrus", security));
+
+ hintList.push_back(std::make_pair("amqp_", protocol));
+ hintList.push_back(std::make_pair("framing", protocol));
+
+ hintList.push_back(std::make_pair("management", management));
+ hintList.push_back(std::make_pair("qmf", management));
+ hintList.push_back(std::make_pair("console", management));
+ hintList.push_back(std::make_pair("Management", management));
+
+ hintList.push_back(std::make_pair("cluster", ha));
+ hintList.push_back(std::make_pair("qpid/ha", ha));
+ hintList.push_back(std::make_pair("qpid\\ha", ha));
+ hintList.push_back(std::make_pair("replication", ha));
+ hintList.push_back(std::make_pair("ClusterSafe", ha));
+
+ hintList.push_back(std::make_pair("broker", broker));
+ hintList.push_back(std::make_pair("SessionState",broker));
+ hintList.push_back(std::make_pair("DataDir", broker));
+ hintList.push_back(std::make_pair("qpidd", broker));
+ hintList.push_back(std::make_pair("xml", broker));
+ hintList.push_back(std::make_pair("QpidBroker", broker));
+
+ hintList.push_back(std::make_pair("store", store));
+
+ hintList.push_back(std::make_pair("assert", system));
+ hintList.push_back(std::make_pair("Exception", system));
+ hintList.push_back(std::make_pair("sys", system));
+ hintList.push_back(std::make_pair("SCM", system));
+
+ hintList.push_back(std::make_pair("tests", test));
+
+ hintList.push_back(std::make_pair("messaging", messaging));
+ hintList.push_back(std::make_pair("types", messaging));
+
+ hintList.push_back(std::make_pair("client", client));
+ }
+
+ static Category categoryOf(const char*const fName);
+
+private:
+ std::list<std::pair<const char* const, Category> > hintList;
+};
+
+ /** POD struct representing a logging statement in source code. */
struct Statement {
bool enabled;
const char* file;
int line;
const char* function;
Level level;
+ Category category;
QPID_COMMON_EXTERN void log(const std::string& message);
+ QPID_COMMON_EXTERN static void categorize(Statement& s);
struct Initializer {
QPID_COMMON_EXTERN Initializer(Statement& s);
@@ -72,8 +176,14 @@ struct Statement {
};
///@internal static initializer for a Statement.
-#define QPID_LOG_STATEMENT_INIT(level) \
- { 0, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, (::qpid::log::level) }
+#define QPID_LOG_STATEMENT_INIT_CAT(LEVEL, CATEGORY) \
+{ 0, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, (::qpid::log::LEVEL), \
+(::qpid::log::CATEGORY) }
+
+
+///@internal static initializer for a Statement with unspecified category
+#define QPID_LOG_STATEMENT_INIT(LEVEL) \
+QPID_LOG_STATEMENT_INIT_CAT ( LEVEL , unspecified )
/**
* Like QPID_LOG but computes an additional boolean test expression
@@ -96,13 +206,26 @@ struct Statement {
} while(0)
/**
+ * Line QPID_LOG_IF but with the additional specification of a category.
+ * @param CATEGORY message category.
+ */
+#define QPID_LOG_IF_CAT(LEVEL, CATEGORY, TEST, MESSAGE) \
+ do { \
+ using ::qpid::log::Statement; \
+ static Statement stmt_= QPID_LOG_STATEMENT_INIT_CAT(LEVEL, CATEGORY); \
+ static Statement::Initializer init_(stmt_); \
+ if (stmt_.enabled && (TEST)) \
+ stmt_.log(::qpid::Msg() << MESSAGE); \
+ } while(0)
+
+/**
* FLAG must be a boolean variable. Assigns FLAG to true iff logging
* is enabled for LEVEL in the calling context. Use when extra
* support code is needed to generate log messages, to ensure that it
* is only run if the logging level is enabled.
* e.g.
* bool logWarning;
- * QPID_LOG_TEST(LEVEL, logWarning);
+ * QPID_LOG_TEST(warning, logWarning);
* if (logWarning) { do stuff needed for warning log messages }
*/
#define QPID_LOG_TEST(LEVEL, FLAG) \
@@ -113,12 +236,31 @@ struct Statement {
FLAG = stmt_.enabled; \
} while(0)
+ /**
+ * FLAG must be a boolean variable. Assigns FLAG to true iff logging
+ * is enabled for LEVEL in the calling context. Use when extra
+ * support code is needed to generate log messages, to ensure that it
+ * is only run if the logging level is enabled.
+ * e.g.
+ * bool logWarning;
+ * QPID_LOG_TEST_CAT(warning, System, logWarning);
+ * if (logWarning) { do stuff needed for warning log messages }
+ */
+ #define QPID_LOG_TEST_CAT(LEVEL, CATEGORY, FLAG) \
+ do { \
+ using ::qpid::log::Statement; \
+ static Statement stmt_= QPID_LOG_STATEMENT_INIT_CAT(LEVEL, CATEGORY); \
+ static Statement::Initializer init_(stmt_); \
+ FLAG = stmt_.enabled; \
+ } while(0)
+
/**
* Macro for log statements. Example of use:
* @code
* QPID_LOG(debug, "There are " << foocount << " foos in the bar.");
* QPID_LOG(error, boost::format("Dohickey %s exploded") % dohicky.name());
* @endcode
+ * Using QPID_LOG implies a category of Unspecified.
*
* You can subscribe to log messages by level, by component, by filename
* or a combination @see Configuration.
@@ -130,6 +272,25 @@ struct Statement {
*/
#define QPID_LOG(LEVEL, MESSAGE) QPID_LOG_IF(LEVEL, true, MESSAGE);
+/**
+ * Macro for log statements. Example of use:
+ * @code
+ * QPID_LOG_CAT(debug, System, "There are " << foocount << " foos in the bar.");
+ * QPID_LOG_CAT(error, System, boost::format("Dohickey %s exploded") % dohicky.name());
+ * @endcode
+ * Using QPID_LOG_CAT requires the specification of a category.
+ *
+ * You can subscribe to log messages by level, by component, by filename
+ * or a combination @see Configuration.
+ *
+ *@param LEVEL severity Level for message, should be one of:
+ * debug, info, notice, warning, error, critical. NB no qpid::log:: prefix.
+ *@param CATEGORY basic Category for the message.
+ *@param MESSAGE any object with an @eostream operator<<, or a sequence
+ * like of ostreamable objects separated by @e<<.
+ */
+#define QPID_LOG_CAT(LEVEL, CATEGORY, MESSAGE) QPID_LOG_IF_CAT(LEVEL, CATEGORY, true, MESSAGE);
+
}} // namespace qpid::log
diff --git a/qpid/cpp/include/qpid/management/Buffer.h b/qpid/cpp/include/qpid/management/Buffer.h
index c32494b8c0..1ac52bf276 100644
--- a/qpid/cpp/include/qpid/management/Buffer.h
+++ b/qpid/cpp/include/qpid/management/Buffer.h
@@ -46,13 +46,12 @@ public:
QPID_COMMON_EXTERN Buffer(char* data=0, uint32_t size=0);
QPID_COMMON_EXTERN ~Buffer();
- QPID_COMMON_EXTERN void record();
- QPID_COMMON_EXTERN void restore(bool reRecord = false);
QPID_COMMON_EXTERN void reset();
QPID_COMMON_EXTERN uint32_t available();
QPID_COMMON_EXTERN uint32_t getSize();
QPID_COMMON_EXTERN uint32_t getPosition();
+ QPID_COMMON_EXTERN void setPosition(uint32_t);
QPID_COMMON_EXTERN char* getPointer();
QPID_COMMON_EXTERN void putOctet(uint8_t i);
diff --git a/qpid/cpp/include/qpid/sys/SystemInfo.h b/qpid/cpp/include/qpid/sys/SystemInfo.h
index 23594cf650..24bc099d75 100644
--- a/qpid/cpp/include/qpid/sys/SystemInfo.h
+++ b/qpid/cpp/include/qpid/sys/SystemInfo.h
@@ -34,51 +34,61 @@ namespace sys {
* Results may be dependent on OS/hardware.
*/
namespace SystemInfo {
- /**
- * Estimate available concurrency, e.g. number of CPU cores.
- * -1 means estimate not available on this platform.
- */
- QPID_COMMON_EXTERN long concurrency();
+/**
+ * Estimate available concurrency, e.g. number of CPU cores.
+ * -1 means estimate not available on this platform.
+ */
+QPID_COMMON_EXTERN long concurrency();
- /**
- * Get the local host name and set it in the specified.
- * Returns false if it can't be obtained and sets errno to any error value.
- */
- QPID_COMMON_EXTERN bool getLocalHostname (Address &address);
+/**
+ * Get the local host name and set it in the specified.
+ * Returns false if it can't be obtained and sets errno to any error value.
+ */
+QPID_COMMON_EXTERN bool getLocalHostname (Address &address);
- QPID_COMMON_EXTERN void getLocalIpAddresses (uint16_t port, std::vector<Address> &addrList);
+/**
+ * Get the (possibly multiple) local IP addresses of this host
+ * using the specified port.
+ */
+QPID_COMMON_EXTERN void getLocalIpAddresses (uint16_t port, std::vector<Address> &addrList);
+
+/**
+ * Return true if host names an address of the local host.
+ *@param host host name or IP address.
+ */
+QPID_COMMON_EXTERN bool isLocalHost(const std::string& host);
- /**
- * Retrieve system identifiers and versions. This is information that can
- * generally be retrieved via POSIX uname().
- *
- * @param osName Receives the OS name; e.g., GNU/Linux or Windows
- * @param nodeName Receives the nodename. This may or may not match the
- * set hostname from getLocalHostname().
- * @param release Receives the OS release identifier.
- * @param version Receives the OS release version (kernel, build, sp, etc.)
- * @param machine Receives the hardware type.
- */
- QPID_COMMON_EXTERN void getSystemId (std::string &osName,
- std::string &nodeName,
- std::string &release,
- std::string &version,
- std::string &machine);
+/**
+ * Retrieve system identifiers and versions. This is information that can
+ * generally be retrieved via POSIX uname().
+ *
+ * @param osName Receives the OS name; e.g., GNU/Linux or Windows
+ * @param nodeName Receives the nodename. This may or may not match the
+ * set hostname from getLocalHostname().
+ * @param release Receives the OS release identifier.
+ * @param version Receives the OS release version (kernel, build, sp, etc.)
+ * @param machine Receives the hardware type.
+ */
+QPID_COMMON_EXTERN void getSystemId (std::string &osName,
+ std::string &nodeName,
+ std::string &release,
+ std::string &version,
+ std::string &machine);
- /**
- * Get the process ID of the current process.
- */
- QPID_COMMON_EXTERN uint32_t getProcessId();
+/**
+ * Get the process ID of the current process.
+ */
+QPID_COMMON_EXTERN uint32_t getProcessId();
- /**
- * Get the process ID of the parent of the current process.
- */
- QPID_COMMON_EXTERN uint32_t getParentProcessId();
+/**
+ * Get the process ID of the parent of the current process.
+ */
+QPID_COMMON_EXTERN uint32_t getParentProcessId();
- /**
- * Get the name of the current process (i.e. the name of the executable)
- */
- QPID_COMMON_EXTERN std::string getProcessName();
+/**
+ * Get the name of the current process (i.e. the name of the executable)
+ */
+QPID_COMMON_EXTERN std::string getProcessName();
}}} // namespace qpid::sys::SystemInfo
diff --git a/qpid/cpp/managementgen/qmfgen/management-types.xml b/qpid/cpp/managementgen/qmfgen/management-types.xml
index c88f0caeae..efa8322806 100644
--- a/qpid/cpp/managementgen/qmfgen/management-types.xml
+++ b/qpid/cpp/managementgen/qmfgen/management-types.xml
@@ -35,7 +35,7 @@
<type name="int16" base="S16" cpp="int16_t" encode="@.putInt16(#)" decode="# = @.getInt16()" stream="#" size="2" accessor="direct" init="0"/>
<type name="int32" base="S32" cpp="int32_t" encode="@.putInt32(#)" decode="# = @.getInt32()" stream="#" size="4" accessor="direct" init="0"/>
<type name="int64" base="S64" cpp="int64_t" encode="@.putInt64(#)" decode="# = @.getInt64()" stream="#" size="8" accessor="direct" init="0"/>
-<type name="bool" base="BOOL" cpp="bool" encode="@.putOctet(#?1:0)" decode="# = @.getOctet()==1" stream="#" size="1" accessor="direct" init="0"/>
+<type name="bool" base="BOOL" cpp="bool" encode="@.putOctet(#?1:0)" decode="# = @.getOctet()==1" stream="#" size="1" accessor="direct" init="false"/>
<type name="sstr" base="SSTR" cpp="std::string" encode="@.putShortString(#)" decode="@.getShortString(#)" stream="#" size="(1 + #.length())" accessor="direct" init='""' byRef="y" unmap="(#).getString()"/>
<type name="lstr" base="LSTR" cpp="std::string" encode="@.putMediumString(#)" decode="@.getMediumString(#)" stream="#" size="(2 + #.length())" accessor="direct" init='""' byRef="y" unmap="(#).getString()"/>
<type name="absTime" base="ABSTIME" cpp="int64_t" encode="@.putLongLong(#)" decode="# = @.getLongLong()" stream="#" size="8" accessor="direct" init="0"/>
diff --git a/qpid/cpp/managementgen/qmfgen/schema.py b/qpid/cpp/managementgen/qmfgen/schema.py
index c48ae572d2..dc8ffae446 100755
--- a/qpid/cpp/managementgen/qmfgen/schema.py
+++ b/qpid/cpp/managementgen/qmfgen/schema.py
@@ -225,8 +225,7 @@ class SchemaType:
def genRead (self, stream, varName, indent=" "):
stream.write(indent + self.decode.replace("@", "buf").replace("#", varName) + ";\n")
- def genUnmap (self, stream, varName, indent=" ", key=None, mapName="_map",
- _optional=False):
+ def genUnmap (self, stream, varName, indent=" ", key=None, mapName="_map", _optional=False, _default=None):
if key is None:
key = varName
stream.write(indent + "if ((_i = " + mapName + ".find(\"" + key + "\")) != " + mapName + ".end()) {\n")
@@ -234,6 +233,11 @@ class SchemaType:
self.unmap.replace("#", "_i->second") + ";\n")
if _optional:
stream.write(indent + " _found = true;\n")
+ stream.write(indent + "} else {\n")
+ default = _default
+ if not default:
+ default = self.init
+ stream.write(indent + " " + varName + " = " + default + ";\n")
stream.write(indent + "}\n")
def genWrite (self, stream, varName, indent=" "):
@@ -1405,7 +1409,9 @@ class SchemaClass:
"ioArgs." + arg.dir.lower () + "_" + arg.name,
" ",
arg.name,
- "inMap")
+ "inMap",
+ False,
+ arg.default)
stream.write (" bool allow = coreObject->AuthorizeMethod(METHOD_" +\
method.getName().upper() + ", ioArgs, userId);\n")
diff --git a/qpid/cpp/managementgen/qmfgen/templates/Class.cpp b/qpid/cpp/managementgen/qmfgen/templates/Class.cpp
index fc0b9c8177..396812f2e0 100644
--- a/qpid/cpp/managementgen/qmfgen/templates/Class.cpp
+++ b/qpid/cpp/managementgen/qmfgen/templates/Class.cpp
@@ -29,6 +29,7 @@
/*MGEN:Class.MethodArgIncludes*/
#include <iostream>
#include <sstream>
+#include <string.h>
using namespace qmf::/*MGEN:Class.Namespace*/;
using qpid::management::ManagementAgent;
diff --git a/qpid/cpp/rubygen/framing.0-10/structs.rb b/qpid/cpp/rubygen/framing.0-10/structs.rb
index 62b33ce773..f93742e57e 100755
--- a/qpid/cpp/rubygen/framing.0-10/structs.rb
+++ b/qpid/cpp/rubygen/framing.0-10/structs.rb
@@ -185,6 +185,13 @@ class StructGen < CppGen
end
end
+ def check_field(f)
+ if (size = StringSizeMap[f.cpptype.encoded] and size < 4)
+ limit = 2 ** (size * 8)
+ genl "if (#{f.cppname}.size() >= #{limit}) throw IllegalArgumentException(\"Value for #{f.cppname} is too large\");"
+ end
+ end
+
def process_packed_fields(s)
s.fields.each { |f| yield f, s.fields.index(f) }
end
@@ -260,11 +267,16 @@ EOS
indent {
process_packed_fields(s) { |f, i| genl "set#{f.name.caps}(_#{f.cppname});" if f.type_ == "bit"}
process_packed_fields(s) { |f, i| genl "flags |= #{flag_mask(s, i)};" unless f.type_ == "bit"}
+ s.fields.each { |f| check_field(f) }
}
genl "}"
else
indent { gen s.fields.collect { |f| " #{f.cppname}(_#{f.cppname})" }.join(",\n") }
- genl "{}"
+ genl "{"
+ indent {
+ s.fields.each { |f| check_field(f) }
+ }
+ genl "}"
end
end
#default constructors:
@@ -298,6 +310,7 @@ EOS
indent {
genl "#{f.cppname} = _#{f.cppname};"
genl "flags |= #{flag_mask(s, i)};"
+ check_field(f)
}
genl "}"
genl "#{f.cpptype.ret} #{s.cppname}::get#{f.name.caps}() const { return #{f.cppname}; }"
@@ -329,6 +342,7 @@ EOS
indent {
genl "#{f.cppname} = _#{f.cppname};"
genl "flags |= #{flag_mask(s, i)};"
+ check_field(f)
}
genl "}"
genl "#{f.cpptype.ret} #{s.body_name}::get#{f.name.caps}() const { return #{f.cppname}; }"
@@ -364,7 +378,12 @@ EOS
end
def define_accessors(f)
- genl "void set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) { #{f.cppname} = _#{f.cppname}; }"
+ genl "void set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {"
+ indent {
+ genl "#{f.cppname} = _#{f.cppname};"
+ check_field(f)
+ }
+ genl "}"
genl "#{f.cpptype.ret} get#{f.name.caps}() const { return #{f.cppname}; }"
if (f.cpptype.name == "FieldTable")
genl "#{f.cpptype.name}& get#{f.name.caps}() { return #{f.cppname}; }"
@@ -401,6 +420,7 @@ EOS
#include <ostream>
#include "qpid/framing/amqp_types_full.h"
+#include "qpid/framing/reply_exceptions.h"
#include "qpid/CommonImportExport.h"
namespace qpid {
@@ -465,7 +485,7 @@ EOS
end
gen <<EOS
#include "qpid/framing/#{classname}.h"
-#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/Buffer.h"
using namespace qpid::framing;
diff --git a/qpid/cpp/src/CMakeLists.txt b/qpid/cpp/src/CMakeLists.txt
index cfcdead883..a9b2c6bdb2 100644
--- a/qpid/cpp/src/CMakeLists.txt
+++ b/qpid/cpp/src/CMakeLists.txt
@@ -281,7 +281,7 @@ endif (CMAKE_COMPILER_IS_GNUCXX)
if (CMAKE_CXX_COMPILER_ID STREQUAL SunPro)
set (COMPILER_FLAGS "-library=stlport4 -mt")
- set (WARNING_FLAGS "+w2")
+ set (WARNING_FLAGS "+w")
endif (CMAKE_CXX_COMPILER_ID STREQUAL SunPro)
option(ENABLE_WARNINGS "Enable lots of compiler warnings (recommended)" ON)
@@ -354,6 +354,7 @@ endif (NOT Boost_REGEX_LIBRARY)
# Boost on Windows can use automatic linking to pick up the correct
# Boost libs based on compile-time touching of the headers. Since we don't
# really need to add them to the link lines, set the names to blanks.
+option(QPID_LINK_BOOST_DYNAMIC "Link with dynamic Boost libs (OFF to link static)" ON)
if (MSVC)
install (PROGRAMS
${Boost_DATE_TIME_LIBRARY_DEBUG} ${Boost_DATE_TIME_LIBRARY_RELEASE}
@@ -371,7 +372,6 @@ if (MSVC)
COMPONENT ${QPID_COMPONENT_COMMON})
endif (NOT Boost_VERSION LESS 103500)
- option(QPID_LINK_BOOST_DYNAMIC "Link with dynamic Boost libs (OFF to link static)" ON)
if (QPID_LINK_BOOST_DYNAMIC)
add_definitions( /D BOOST_ALL_DYN_LINK)
string (REPLACE .lib .dll
@@ -594,6 +594,8 @@ if (BUILD_ACL)
set (acl_SOURCES
qpid/acl/Acl.cpp
qpid/acl/Acl.h
+ qpid/acl/AclConnectionCounter.cpp
+ qpid/acl/AclConnectionCounter.h
qpid/acl/AclData.cpp
qpid/acl/AclData.h
qpid/acl/AclPlugin.cpp
@@ -624,25 +626,42 @@ set (ha_default ON)
option(BUILD_HA "Build Active-Passive HA plugin" ${ha_default})
if (BUILD_HA)
set (ha_SOURCES
+ qpid/ha/BackupConnectionExcluder.h
+ qpid/ha/BrokerInfo.cpp
+ qpid/ha/BrokerInfo.h
+ qpid/ha/QueueGuard.cpp
+ qpid/ha/QueueGuard.h
+ qpid/ha/ReplicationTest.cpp
+ qpid/ha/ReplicationTest.h
qpid/ha/Backup.cpp
qpid/ha/Backup.h
+ qpid/ha/BrokerReplicator.cpp
+ qpid/ha/BrokerReplicator.h
+ qpid/ha/ConnectionObserver.cpp
+ qpid/ha/ConnectionObserver.h
+ qpid/ha/Counter.h
qpid/ha/HaBroker.cpp
qpid/ha/HaBroker.h
qpid/ha/HaPlugin.cpp
- qpid/ha/Settings.h
- qpid/ha/QueueReplicator.h
+ qpid/ha/Membership.cpp
+ qpid/ha/Membership.h
+ qpid/ha/Primary.cpp
+ qpid/ha/Primary.h
+ qpid/ha/QueueRange.h
qpid/ha/QueueReplicator.cpp
- qpid/ha/ReplicatingSubscription.h
+ qpid/ha/QueueReplicator.h
qpid/ha/ReplicatingSubscription.cpp
- qpid/ha/BrokerReplicator.cpp
- qpid/ha/BrokerReplicator.h
- qpid/ha/ConnectionExcluder.cpp
- qpid/ha/ConnectionExcluder.h
+ qpid/ha/ReplicatingSubscription.h
+ qpid/ha/Settings.h
+ qpid/ha/types.cpp
+ qpid/ha/types.h
+ qpid/ha/RemoteBackup.cpp
+ qpid/ha/RemoteBackup.h
)
add_library (ha MODULE ${ha_SOURCES})
set_target_properties (ha PROPERTIES PREFIX "")
- target_link_libraries (ha qpidbroker ${Boost_PROGRAM_OPTIONS_LIBRARY})
+ target_link_libraries (ha qpidtypes qpidcommon qpidbroker)
if (CMAKE_COMPILER_IS_GNUCXX)
set_target_properties (ha PROPERTIES
PREFIX ""
@@ -665,6 +684,11 @@ include (ssl.cmake)
# Check for syslog capabilities not present on all systems
check_symbol_exists (LOG_AUTHPRIV "sys/syslog.h" HAVE_LOG_AUTHPRIV)
check_symbol_exists (LOG_FTP "sys/syslog.h" HAVE_LOG_FTP)
+
+# Set default Memory Status module (Null implementation)
+set (qpid_memstat_module
+ qpid/sys/MemStat.cpp
+)
# Allow MSVC user to select 'WinXP-SP3/Windows Server 2003' as build target version
set (win32_winnt_default OFF)
@@ -727,7 +751,6 @@ if (CMAKE_SYSTEM_NAME STREQUAL Windows)
qpid/sys/windows/SystemInfo.cpp
qpid/sys/windows/Thread.cpp
qpid/sys/windows/Time.cpp
- qpid/sys/windows/MemStat.cpp
qpid/client/windows/SaslFactory.cpp
${sslcommon_windows_SOURCES}
)
@@ -773,41 +796,54 @@ else (CMAKE_SYSTEM_NAME STREQUAL Windows)
)
endif (POLLER STREQUAL poll)
+ # Set default System Info module
+ set (qpid_system_module
+ qpid/sys/posix/SystemInfo.cpp
+ )
+
if (CMAKE_SYSTEM_NAME STREQUAL Linux)
- set (qpid_system_module
- qpid/sys/posix/SystemInfo.cpp
- )
add_definitions(-pthread)
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread")
if (CMAKE_COMPILER_IS_GNUCXX)
set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${GCC_CATCH_UNDEFINED} -pthread")
+ set (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -pthread")
endif (CMAKE_COMPILER_IS_GNUCXX)
+ # On Linux override memory status module
+ set (qpid_memstat_module
+ qpid/sys/posix/MemStat.cpp
+ )
endif (CMAKE_SYSTEM_NAME STREQUAL Linux)
- set (qpidtypes_platform_SOURCES)
- set (qpidtypes_platform_LIBS
- uuid
- ${Boost_SYSTEM_LIBRARY}
- )
-
if (CMAKE_SYSTEM_NAME STREQUAL SunOS)
+ # On Solaris override the system info module
set (qpid_system_module
qpid/sys/solaris/SystemInfo.cpp
)
-# On Sun we want -lpthread -lthread as the 2nd last and last libs passed to linker
+ # On Sun we want -lpthread -lthread as the 2nd last and last libs passed to linker
set (qpidtypes_platform_LIBS ${qpidtypes_platform_LIBS}
pthread
thread
)
endif (CMAKE_SYSTEM_NAME STREQUAL SunOS)
+ if (CMAKE_CXX_COMPILER_ID STREQUAL SunPro)
+ # -lmalloc needed for mallinfo.
+ set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lmalloc")
+ set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lmalloc")
+ endif (CMAKE_CXX_COMPILER_ID STREQUAL SunPro)
+
+ set (qpidtypes_platform_SOURCES)
+ set (qpidtypes_platform_LIBS
+ uuid
+ ${Boost_SYSTEM_LIBRARY}
+ )
+
set (qpidcommon_platform_SOURCES
qpid/sys/posix/AsynchIO.cpp
qpid/sys/posix/Fork.cpp
qpid/sys/posix/FileSysDir.cpp
qpid/sys/posix/IOHandle.cpp
qpid/sys/posix/LockFile.cpp
- qpid/sys/posix/MemStat.cpp
qpid/sys/posix/Mutex.cpp
qpid/sys/posix/PipeHandle.cpp
qpid/sys/posix/PollableCondition.cpp
@@ -874,7 +910,6 @@ set (qpidcommon_SOURCES
qpid/framing/AMQHeaderBody.cpp
qpid/framing/AMQHeartbeatBody.cpp
qpid/framing/Array.cpp
- qpid/framing/BodyHandler.cpp
qpid/framing/Buffer.cpp
qpid/framing/Endian.cpp
qpid/framing/FieldTable.cpp
@@ -911,6 +946,7 @@ set (qpidcommon_SOURCES
qpid/sys/Timer.cpp
qpid/sys/TimerWarnings.cpp
qpid/amqp_0_10/Codecs.cpp
+ ${qpid_memstat_module}
)
add_msvc_version (qpidcommon library dll)
@@ -1112,6 +1148,7 @@ set (qpidbroker_SOURCES
qpid/broker/NameGenerator.cpp
qpid/broker/NullMessageStore.cpp
qpid/broker/QueueBindings.cpp
+ qpid/broker/QueuedMessage.cpp
qpid/broker/QueueEvents.cpp
qpid/broker/QueuePolicy.cpp
qpid/broker/QueueRegistry.cpp
diff --git a/qpid/cpp/src/Makefile.am b/qpid/cpp/src/Makefile.am
index e03c88ec8b..24ec765351 100644
--- a/qpid/cpp/src/Makefile.am
+++ b/qpid/cpp/src/Makefile.am
@@ -28,6 +28,7 @@ windows_dist = \
qpid/log/windows/SinkOptions.cpp \
qpid/log/windows/SinkOptions.h \
../include/qpid/sys/windows/check.h \
+ qpid/sys/MemState.cpp \
qpid/sys/windows/AsynchIO.cpp \
qpid/sys/windows/AsynchIoResult.h \
../include/qpid/sys/windows/Condition.h \
@@ -53,7 +54,6 @@ windows_dist = \
../include/qpid/sys/windows/Time.h \
qpid/sys/windows/uuid.cpp \
qpid/sys/windows/uuid.h \
- qpid/sys/windows/MemStat.cpp \
windows/QpiddBroker.cpp \
windows/SCM.h \
windows/SCM.cpp \
@@ -140,7 +140,7 @@ tmoduleexec_LTLIBRARIES=
AM_CXXFLAGS += -DBOOST_FILESYSTEM_VERSION=2
## Automake macros to build libraries and executables.
-qpidd_CXXFLAGS = $(AM_CXXFLAGS) -DQPIDD_MODULE_DIR=\"$(dmoduleexecdir)\" -DQPIDD_CONF_FILE=\"$(sysconfdir)/qpidd.conf\"
+qpidd_CXXFLAGS = $(AM_CXXFLAGS) -DQPIDD_MODULE_DIR=\"$(dmoduleexecdir)\" -DQPIDD_CONF_FILE=\"$(sysconfdir)/qpidd.conf\" -DQPIDC_CONF_FILE=\"$(confdir)/qpidc.conf\"
libqpidclient_la_CXXFLAGS = $(AM_CXXFLAGS) -DQPIDC_MODULE_DIR=\"$(cmoduleexecdir)\" -DQPIDC_CONF_FILE=\"$(confdir)/qpidc.conf\"
qpidd_LDADD = \
@@ -380,7 +380,6 @@ libqpidcommon_la_SOURCES += \
qpid/assert.h \
qpid/framing/AMQBody.cpp \
qpid/framing/AMQBody.h \
- qpid/framing/AMQCommandControlBody.h \
qpid/framing/AMQContentBody.cpp \
qpid/framing/AMQContentBody.h \
qpid/framing/AMQDataBlock.h \
@@ -398,8 +397,6 @@ libqpidcommon_la_SOURCES += \
qpid/framing/AccumulatedAck.h \
qpid/framing/Array.cpp \
qpid/framing/BodyFactory.h \
- qpid/framing/BodyHandler.cpp \
- qpid/framing/BodyHandler.h \
qpid/framing/Buffer.cpp \
qpid/framing/ResizableBuffer.h \
qpid/framing/ChannelHandler.h \
@@ -552,6 +549,8 @@ libqpidbroker_la_SOURCES = \
qpid/broker/ConsumerFactory.h \
qpid/broker/ConnectionObserver.h \
qpid/broker/ConnectionObservers.h \
+ qpid/broker/ConfigurationObserver.h \
+ qpid/broker/ConfigurationObservers.h \
qpid/broker/Daemon.cpp \
qpid/broker/Daemon.h \
qpid/broker/Deliverable.h \
@@ -614,6 +613,7 @@ libqpidbroker_la_SOURCES = \
qpid/broker/NameGenerator.h \
qpid/broker/NullMessageStore.cpp \
qpid/broker/NullMessageStore.h \
+ qpid/broker/Observers.h \
qpid/broker/OwnershipToken.h \
qpid/broker/Persistable.h \
qpid/broker/PersistableConfig.h \
@@ -636,6 +636,7 @@ libqpidbroker_la_SOURCES = \
qpid/broker/QueuePolicy.h \
qpid/broker/QueueRegistry.cpp \
qpid/broker/QueueRegistry.h \
+ qpid/broker/QueuedMessage.cpp \
qpid/broker/QueuedMessage.h \
qpid/broker/QueueFlowLimit.h \
qpid/broker/QueueFlowLimit.cpp \
@@ -682,6 +683,7 @@ libqpidbroker_la_SOURCES = \
qpid/broker/ThresholdAlerts.h \
qpid/broker/TopicExchange.cpp \
qpid/broker/TopicExchange.h \
+ qpid/broker/TopicKeyNode.h \
qpid/broker/TransactionalStore.h \
qpid/broker/TxAccept.cpp \
qpid/broker/TxAccept.h \
diff --git a/qpid/cpp/src/acl.mk b/qpid/cpp/src/acl.mk
index b8e2ff0e13..0301f8c754 100644
--- a/qpid/cpp/src/acl.mk
+++ b/qpid/cpp/src/acl.mk
@@ -24,6 +24,8 @@ dmoduleexec_LTLIBRARIES += acl.la
acl_la_SOURCES = \
qpid/acl/Acl.cpp \
qpid/acl/Acl.h \
+ qpid/acl/AclConnectionCounter.cpp \
+ qpid/acl/AclConnectionCounter.h \
qpid/acl/AclData.cpp \
qpid/acl/AclData.h \
qpid/acl/AclPlugin.cpp \
diff --git a/qpid/cpp/src/ha.mk b/qpid/cpp/src/ha.mk
index 8a2cee30c7..ad41db9b0e 100644
--- a/qpid/cpp/src/ha.mk
+++ b/qpid/cpp/src/ha.mk
@@ -25,18 +25,35 @@ dmoduleexec_LTLIBRARIES += ha.la
ha_la_SOURCES = \
qpid/ha/Backup.cpp \
qpid/ha/Backup.h \
+ qpid/ha/BackupConnectionExcluder.h \
+ qpid/ha/BrokerInfo.cpp \
+ qpid/ha/BrokerInfo.h \
+ qpid/ha/BrokerReplicator.cpp \
+ qpid/ha/BrokerReplicator.h \
+ qpid/ha/ConnectionObserver.cpp \
+ qpid/ha/ConnectionObserver.h \
+ qpid/ha/Counter.h \
qpid/ha/HaBroker.cpp \
qpid/ha/HaBroker.h \
qpid/ha/HaPlugin.cpp \
- qpid/ha/Settings.h \
- qpid/ha/QueueReplicator.h \
+ qpid/ha/Membership.cpp \
+ qpid/ha/Membership.h \
+ qpid/ha/Primary.cpp \
+ qpid/ha/Primary.h \
+ qpid/ha/QueueGuard.cpp \
+ qpid/ha/QueueGuard.h \
+ qpid/ha/QueueRange.h \
qpid/ha/QueueReplicator.cpp \
- qpid/ha/ReplicatingSubscription.h \
+ qpid/ha/QueueReplicator.h \
qpid/ha/ReplicatingSubscription.cpp \
- qpid/ha/BrokerReplicator.cpp \
- qpid/ha/BrokerReplicator.h \
- qpid/ha/ConnectionExcluder.cpp \
- qpid/ha/ConnectionExcluder.h
+ qpid/ha/ReplicatingSubscription.h \
+ qpid/ha/ReplicationTest.cpp \
+ qpid/ha/ReplicationTest.h \
+ qpid/ha/Settings.h \
+ qpid/ha/RemoteBackup.cpp \
+ qpid/ha/RemoteBackup.h \
+ qpid/ha/types.cpp \
+ qpid/ha/types.h
ha_la_LIBADD = libqpidbroker.la
ha_la_LDFLAGS = $(PLUGINLDFLAGS)
diff --git a/qpid/cpp/src/posix/QpiddBroker.cpp b/qpid/cpp/src/posix/QpiddBroker.cpp
index 1cebcfc3ac..76e3bb6674 100644
--- a/qpid/cpp/src/posix/QpiddBroker.cpp
+++ b/qpid/cpp/src/posix/QpiddBroker.cpp
@@ -31,14 +31,15 @@
#include <unistd.h>
#include <sys/utsname.h>
-using namespace std;
-using namespace qpid;
-using qpid::broker::Broker;
-using qpid::broker::Daemon;
+using std::cout;
+using std::endl;
+
+namespace qpid {
+namespace broker {
BootstrapOptions::BootstrapOptions(const char* argv0)
: qpid::Options("Options"),
- common("", QPIDD_CONF_FILE),
+ common("", QPIDD_CONF_FILE, QPIDC_CONF_FILE),
module(QPIDD_MODULE_DIR),
log(argv0)
{
@@ -90,7 +91,7 @@ struct QpiddPosixOptions : public QpiddOptionsPrivate {
QpiddOptions::QpiddOptions(const char* argv0)
: qpid::Options("Options"),
- common("", QPIDD_CONF_FILE),
+ common("", QPIDD_CONF_FILE, QPIDC_CONF_FILE),
module(QPIDD_MODULE_DIR),
log(argv0)
{
@@ -153,8 +154,14 @@ int QpiddBroker::execute (QpiddOptions *options) {
throw Exception("Internal error obtaining platform options");
if (myOptions->daemon.check || myOptions->daemon.quit) {
- pid_t pid = Daemon::getPid(myOptions->daemon.piddir,
- options->broker.port);
+ pid_t pid;
+ try {
+ pid = Daemon::getPid(myOptions->daemon.piddir, options->broker.port);
+ } catch (const ErrnoException& e) {
+ // This is not a critical error, usually means broker is not running
+ QPID_LOG(notice, "Cannot stop broker: " << e.what());
+ return 1;
+ }
if (pid < 0)
return 1;
if (myOptions->daemon.check)
@@ -197,7 +204,9 @@ int QpiddBroker::execute (QpiddOptions *options) {
return 0;
}
+}} // namespace qpid::Broker
+
int main(int argc, char* argv[])
{
- return run_broker(argc, argv);
+ return qpid::broker::run_broker(argc, argv);
}
diff --git a/qpid/cpp/src/qmf/AgentSession.cpp b/qpid/cpp/src/qmf/AgentSession.cpp
index 3b5806aea4..4d7be33188 100644
--- a/qpid/cpp/src/qmf/AgentSession.cpp
+++ b/qpid/cpp/src/qmf/AgentSession.cpp
@@ -21,6 +21,22 @@
#include "qmf/AgentSessionImpl.h"
+#include <iostream>
+#include <memory>
+
+namespace qmf {
+
+using std::string;
+using std::map;
+
+using qpid::messaging::Address;
+using qpid::messaging::Connection;
+using qpid::messaging::Duration;
+using qpid::messaging::Message;
+using qpid::messaging::Receiver;
+using qpid::messaging::Sender;
+using qpid::types::Variant;
+
AgentSession::AgentSession(AgentSessionImpl* impl) { PI::ctor(*this, impl); }
AgentSession::AgentSession(const AgentSession& s) : qmf::Handle<AgentSessionImpl>() { PI::copy(*this, s); }
AgentSession::~AgentSession() { PI::dtor(*this); }
@@ -332,7 +348,7 @@ void AgentSessionImpl::delData(const DataAddr& addr)
void AgentSessionImpl::authAccept(AgentEvent& authEvent)
{
- auto_ptr<AgentEventImpl> eventImpl(new AgentEventImpl(AGENT_QUERY));
+ std::auto_ptr<AgentEventImpl> eventImpl(new AgentEventImpl(AGENT_QUERY));
eventImpl->setQuery(authEvent.getQuery());
eventImpl->setUserId(authEvent.getUserId());
eventImpl->setReplyTo(AgentEventImplAccess::get(authEvent).getReplyTo());
@@ -593,7 +609,7 @@ void AgentSessionImpl::handleMethodRequest(const Variant::Map& content, const Me
//
// Construct an AgentEvent to be sent to the application.
//
- auto_ptr<AgentEventImpl> eventImpl(new AgentEventImpl(AGENT_METHOD));
+ std::auto_ptr<AgentEventImpl> eventImpl(new AgentEventImpl(AGENT_METHOD));
eventImpl->setUserId(msg.getUserId());
eventImpl->setReplyTo(msg.getReplyTo());
eventImpl->setCorrelationId(msg.getCorrelationId());
@@ -655,8 +671,8 @@ void AgentSessionImpl::handleQueryRequest(const Variant::Map& content, const Mes
//
// Construct an AgentEvent to be sent to the application or directly handled by the agent.
//
- auto_ptr<QueryImpl> queryImpl(new QueryImpl(content));
- auto_ptr<AgentEventImpl> eventImpl(new AgentEventImpl(AGENT_AUTH_QUERY));
+ std::auto_ptr<QueryImpl> queryImpl(new QueryImpl(content));
+ std::auto_ptr<AgentEventImpl> eventImpl(new AgentEventImpl(AGENT_AUTH_QUERY));
eventImpl->setUserId(msg.getUserId());
eventImpl->setReplyTo(msg.getReplyTo());
eventImpl->setCorrelationId(msg.getCorrelationId());
@@ -1012,3 +1028,4 @@ const AgentSessionImpl& AgentSessionImplAccess::get(const AgentSession& session)
return *session.impl;
}
+}
diff --git a/qpid/cpp/src/qmf/AgentSessionImpl.h b/qpid/cpp/src/qmf/AgentSessionImpl.h
index ae512a4054..64a39ab2e8 100644
--- a/qpid/cpp/src/qmf/AgentSessionImpl.h
+++ b/qpid/cpp/src/qmf/AgentSessionImpl.h
@@ -57,17 +57,10 @@
#include <queue>
#include <map>
-#include <iostream>
-#include <memory>
-
-using namespace std;
-using namespace qpid::messaging;
-using namespace qmf;
-using qpid::types::Variant;
-
-typedef qmf::PrivateImplRef<AgentSession> PI;
namespace qmf {
+ typedef qmf::PrivateImplRef<AgentSession> PI;
+
class AgentSessionImpl : public virtual qpid::RefCounted, public qpid::sys::Runnable {
public:
~AgentSessionImpl();
@@ -75,29 +68,29 @@ namespace qmf {
//
// Methods from API handle
//
- AgentSessionImpl(Connection& c, const string& o);
- void setDomain(const string& d) { checkOpen(); domain = d; }
- void setVendor(const string& v) { checkOpen(); attributes["_vendor"] = v; }
- void setProduct(const string& p) { checkOpen(); attributes["_product"] = p; }
- void setInstance(const string& i) { checkOpen(); attributes["_instance"] = i; }
- void setAttribute(const string& k, const qpid::types::Variant& v) { checkOpen(); attributes[k] = v; }
- const string& getName() const { return agentName; }
+ AgentSessionImpl(qpid::messaging::Connection& c, const std::string& o);
+ void setDomain(const std::string& d) { checkOpen(); domain = d; }
+ void setVendor(const std::string& v) { checkOpen(); attributes["_vendor"] = v; }
+ void setProduct(const std::string& p) { checkOpen(); attributes["_product"] = p; }
+ void setInstance(const std::string& i) { checkOpen(); attributes["_instance"] = i; }
+ void setAttribute(const std::string& k, const qpid::types::Variant& v) { checkOpen(); attributes[k] = v; }
+ const std::string& getName() const { return agentName; }
void open();
void closeAsync();
void close();
- bool nextEvent(AgentEvent& e, Duration t);
+ bool nextEvent(AgentEvent& e, qpid::messaging::Duration t);
int pendingEvents() const;
void setEventNotifier(EventNotifierImpl* eventNotifier);
EventNotifierImpl* getEventNotifier() const;
void registerSchema(Schema& s);
- DataAddr addData(Data& d, const string& n, bool persist);
+ DataAddr addData(Data& d, const std::string& n, bool persist);
void delData(const DataAddr&);
void authAccept(AgentEvent& e);
- void authReject(AgentEvent& e, const string& m);
- void raiseException(AgentEvent& e, const string& s);
+ void authReject(AgentEvent& e, const std::string& m);
+ void raiseException(AgentEvent& e, const std::string& s);
void raiseException(AgentEvent& e, const Data& d);
void response(AgentEvent& e, const Data& d);
void complete(AgentEvent& e);
@@ -106,21 +99,21 @@ namespace qmf {
void raiseEvent(const Data& d, int s);
private:
- typedef map<DataAddr, Data, DataAddrCompare> DataIndex;
- typedef map<SchemaId, Schema, SchemaIdCompare> SchemaMap;
+ typedef std::map<DataAddr, Data, DataAddrCompare> DataIndex;
+ typedef std::map<SchemaId, Schema, SchemaIdCompare> SchemaMap;
mutable qpid::sys::Mutex lock;
qpid::sys::Condition cond;
- Connection connection;
- Session session;
- Sender directSender;
- Sender topicSender;
- string domain;
- Variant::Map attributes;
- Variant::Map options;
- string agentName;
+ qpid::messaging::Connection connection;
+ qpid::messaging::Session session;
+ qpid::messaging::Sender directSender;
+ qpid::messaging::Sender topicSender;
+ std::string domain;
+ qpid::types::Variant::Map attributes;
+ qpid::types::Variant::Map options;
+ std::string agentName;
bool opened;
- queue<AgentEvent> eventQueue;
+ std::queue<AgentEvent> eventQueue;
EventNotifierImpl* eventNotifier;
qpid::sys::Thread* thread;
bool threadCanceled;
@@ -140,25 +133,25 @@ namespace qmf {
bool strictSecurity;
uint32_t maxThreadWaitTime;
uint64_t schemaUpdateTime;
- string directBase;
- string topicBase;
+ std::string directBase;
+ std::string topicBase;
SchemaMap schemata;
DataIndex globalIndex;
- map<SchemaId, DataIndex, SchemaIdCompareNoHash> schemaIndex;
+ std::map<SchemaId, DataIndex, SchemaIdCompareNoHash> schemaIndex;
void checkOpen();
void setAgentName();
void enqueueEvent(const AgentEvent&);
void alertEventNotifierLH(bool readable);
- void handleLocateRequest(const Variant::List& content, const Message& msg);
- void handleMethodRequest(const Variant::Map& content, const Message& msg);
- void handleQueryRequest(const Variant::Map& content, const Message& msg);
+ void handleLocateRequest(const qpid::types::Variant::List& content, const qpid::messaging::Message& msg);
+ void handleMethodRequest(const qpid::types::Variant::Map& content, const qpid::messaging::Message& msg);
+ void handleQueryRequest(const qpid::types::Variant::Map& content, const qpid::messaging::Message& msg);
void handleSchemaRequest(AgentEvent&);
- void handleV1SchemaRequest(qpid::management::Buffer&, uint32_t, const Message&);
- void dispatch(Message);
+ void handleV1SchemaRequest(qpid::management::Buffer&, uint32_t, const qpid::messaging::Message&);
+ void dispatch(qpid::messaging::Message);
void sendHeartbeat();
- void send(Message, const Address&);
+ void send(qpid::messaging::Message, const qpid::messaging::Address&);
void flushResponses(AgentEvent&, bool);
void periodicProcessing(uint64_t);
void run();
diff --git a/qpid/cpp/src/qmf/ConsoleSessionImpl.h b/qpid/cpp/src/qmf/ConsoleSessionImpl.h
index e2b30602fa..2c06df030c 100644
--- a/qpid/cpp/src/qmf/ConsoleSessionImpl.h
+++ b/qpid/cpp/src/qmf/ConsoleSessionImpl.h
@@ -47,8 +47,6 @@
#include <map>
#include <queue>
-using namespace std;
-
namespace qmf {
class ConsoleSessionImpl : public virtual qpid::RefCounted, public qpid::sys::Runnable {
public:
diff --git a/qpid/cpp/src/qmf/EventNotifierImpl.cpp b/qpid/cpp/src/qmf/EventNotifierImpl.cpp
index 20114aaa5e..81b6d637a3 100644
--- a/qpid/cpp/src/qmf/EventNotifierImpl.cpp
+++ b/qpid/cpp/src/qmf/EventNotifierImpl.cpp
@@ -21,6 +21,8 @@
#include "qmf/AgentSessionImpl.h"
#include "qmf/ConsoleSessionImpl.h"
+namespace qmf {
+
EventNotifierImpl::EventNotifierImpl(AgentSession& agentSession)
: readable(false), agent(agentSession)
{
@@ -54,3 +56,5 @@ bool EventNotifierImpl::isReadable() const
{
return this->readable;
}
+
+}
diff --git a/qpid/cpp/src/qmf/PrivateImplRef.h b/qpid/cpp/src/qmf/PrivateImplRef.h
index 960cbb2e09..c0c07d7e1b 100644
--- a/qpid/cpp/src/qmf/PrivateImplRef.h
+++ b/qpid/cpp/src/qmf/PrivateImplRef.h
@@ -76,15 +76,15 @@ template <class T> class PrivateImplRef {
/** Set the implementation pointer in a handle */
static void set(T& t, const intrusive_ptr& p) {
if (t.impl == p) return;
- if (t.impl) boost::intrusive_ptr_release(t.impl);
+ if (t.impl) intrusive_ptr_release(t.impl);
t.impl = p.get();
- if (t.impl) boost::intrusive_ptr_add_ref(t.impl);
+ if (t.impl) intrusive_ptr_add_ref(t.impl);
}
// Helper functions to implement the ctor, dtor, copy, assign
- static void ctor(T& t, Impl* p) { t.impl = p; if (p) boost::intrusive_ptr_add_ref(p); }
+ static void ctor(T& t, Impl* p) { t.impl = p; if (p) intrusive_ptr_add_ref(p); }
static void copy(T& t, const T& x) { if (&t == &x) return; t.impl = 0; assign(t, x); }
- static void dtor(T& t) { if(t.impl) boost::intrusive_ptr_release(t.impl); }
+ static void dtor(T& t) { if(t.impl) intrusive_ptr_release(t.impl); }
static T& assign(T& t, const T& x) { set(t, get(x)); return t;}
};
diff --git a/qpid/cpp/src/qpid/Options.cpp b/qpid/cpp/src/qpid/Options.cpp
index 4b13e349f5..35787aa8f3 100644
--- a/qpid/cpp/src/qpid/Options.cpp
+++ b/qpid/cpp/src/qpid/Options.cpp
@@ -186,13 +186,14 @@ void Options::parse(int argc, char const* const* argv, const std::string& config
}
}
-CommonOptions::CommonOptions(const string& name, const string& configfile)
- : Options(name), config(configfile)
+CommonOptions::CommonOptions(const string& name, const string& configfile, const string& clientfile)
+ : Options(name), config(configfile), clientConfig(clientfile)
{
addOptions()
("help,h", optValue(help), "Displays the help message")
("version,v", optValue(version), "Displays version information")
- ("config", optValue(config, "FILE"), "Reads configuration from FILE");
+ ("config", optValue(config, "FILE"), "Reads configuration from FILE")
+ ("client-config", optValue(clientConfig, "FILE"), "Reads client configuration from FILE (for cluster interconnect)");
}
diff --git a/qpid/cpp/src/qpid/RefCounted.h b/qpid/cpp/src/qpid/RefCounted.h
index f9e0107103..26e3e2c4ba 100644
--- a/qpid/cpp/src/qpid/RefCounted.h
+++ b/qpid/cpp/src/qpid/RefCounted.h
@@ -49,15 +49,11 @@ protected:
};
-} // namespace qpid
-
// intrusive_ptr support.
-namespace boost {
-template <typename T>
-inline void intrusive_ptr_add_ref(const T* p) { p->qpid::RefCounted::addRef(); }
-template <typename T>
-inline void intrusive_ptr_release(const T* p) { p->qpid::RefCounted::release(); }
-}
+inline void intrusive_ptr_add_ref(const RefCounted* p) { p->addRef(); }
+inline void intrusive_ptr_release(const RefCounted* p) { p->release(); }
+
+} // namespace qpid
#endif /*!QPID_REFCOUNTED_H*/
diff --git a/qpid/cpp/src/qpid/UrlArray.cpp b/qpid/cpp/src/qpid/UrlArray.cpp
index 489309c8ad..9ebacbd945 100644
--- a/qpid/cpp/src/qpid/UrlArray.cpp
+++ b/qpid/cpp/src/qpid/UrlArray.cpp
@@ -20,6 +20,8 @@
*/
#include "UrlArray.h"
+#include <qpid/framing/FieldValue.h>
+
namespace qpid {
std::vector<Url> urlArrayToVector(const framing::Array& array) {
diff --git a/qpid/cpp/src/qpid/UrlArray.h b/qpid/cpp/src/qpid/UrlArray.h
index 8b11df5c73..ce9e42f248 100644
--- a/qpid/cpp/src/qpid/UrlArray.h
+++ b/qpid/cpp/src/qpid/UrlArray.h
@@ -23,7 +23,6 @@
*/
#include "qpid/framing/Array.h"
-#include "qpid/framing/FieldValue.h"
#include "qpid/Url.h"
#include <vector>
diff --git a/qpid/cpp/src/qpid/acl/Acl.cpp b/qpid/cpp/src/qpid/acl/Acl.cpp
index 007fceb444..d941577f6a 100644
--- a/qpid/cpp/src/qpid/acl/Acl.cpp
+++ b/qpid/cpp/src/qpid/acl/Acl.cpp
@@ -17,6 +17,7 @@
*/
#include "qpid/acl/Acl.h"
+#include "qpid/acl/AclConnectionCounter.h"
#include "qpid/acl/AclData.h"
#include "qpid/acl/AclValidator.h"
#include "qpid/sys/Mutex.h"
@@ -26,8 +27,11 @@
#include "qpid/Options.h"
#include "qpid/log/Logger.h"
#include "qpid/types/Variant.h"
+#include "qmf/org/apache/qpid/acl/ArgsAclLookup.h"
+#include "qmf/org/apache/qpid/acl/ArgsAclLookupPublish.h"
#include "qmf/org/apache/qpid/acl/Package.h"
#include "qmf/org/apache/qpid/acl/EventAllow.h"
+#include "qmf/org/apache/qpid/acl/EventConnectionDeny.h"
#include "qmf/org/apache/qpid/acl/EventDeny.h"
#include "qmf/org/apache/qpid/acl/EventFileLoaded.h"
#include "qmf/org/apache/qpid/acl/EventFileLoadFailed.h"
@@ -35,7 +39,6 @@
#include <map>
#include <boost/shared_ptr.hpp>
-#include <boost/utility/in_place_factory.hpp>
using namespace std;
using namespace qpid::acl;
@@ -47,7 +50,8 @@ using qpid::management::Manageable;
using qpid::management::Args;
namespace _qmf = qmf::org::apache::qpid::acl;
-Acl::Acl (AclValues& av, Broker& b): aclValues(av), broker(&b), transferAcl(false), mgmtObject(0)
+Acl::Acl (AclValues& av, Broker& b): aclValues(av), broker(&b), transferAcl(false), mgmtObject(0),
+ connectionCounter(new ConnectionCounter(*this, aclValues.aclMaxConnectPerUser, aclValues.aclMaxConnectPerIp, aclValues.aclMaxConnectTotal))
{
agent = broker->getManagementAgent();
@@ -56,16 +60,30 @@ Acl::Acl (AclValues& av, Broker& b): aclValues(av), broker(&b), transferAcl(fals
_qmf::Package packageInit(agent);
mgmtObject = new _qmf::Acl (agent, this, broker);
agent->addObject (mgmtObject);
+ mgmtObject->set_maxConnections(aclValues.aclMaxConnectTotal);
+ mgmtObject->set_maxConnectionsPerIp(aclValues.aclMaxConnectPerIp);
+ mgmtObject->set_maxConnectionsPerUser(aclValues.aclMaxConnectPerUser);
}
std::string errorString;
if (!readAclFile(errorString)){
- throw Exception("Could not read ACL file " + errorString);
if (mgmtObject!=0) mgmtObject->set_enforcingAcl(0);
+ throw Exception("Could not read ACL file " + errorString);
}
+ broker->getConnectionObservers().add(connectionCounter);
QPID_LOG(info, "ACL Plugin loaded");
if (mgmtObject!=0) mgmtObject->set_enforcingAcl(1);
}
+
+void Acl::reportConnectLimit(const std::string user, const std::string addr)
+{
+ if (mgmtObject!=0)
+ mgmtObject->inc_connectionDenyCount();
+
+ agent->raiseEvent(_qmf::EventConnectionDeny(user, addr));
+}
+
+
bool Acl::authorise(
const std::string& id,
const Action& action,
@@ -106,6 +124,11 @@ bool Acl::authorise(
}
+bool Acl::approveConnection(const qpid::broker::Connection& conn)
+{
+ return connectionCounter->approveConnection(conn);
+}
+
bool Acl::result(
const AclResult& aclreslt,
const std::string& id,
@@ -193,14 +216,89 @@ bool Acl::readAclFile(std::string& aclFile, std::string& errorText) {
return true;
}
-Acl::~Acl(){}
+
+//
+// management lookup function performs general query on acl engine
+//
+Manageable::status_t Acl::lookup(qpid::management::Args& args, std::string& text)
+{
+ _qmf::ArgsAclLookup& ioArgs = (_qmf::ArgsAclLookup&) args;
+ Manageable::status_t result(STATUS_USER);
+
+ try {
+ ObjectType objType = AclHelper::getObjectType(ioArgs.i_object);
+ Action action = AclHelper::getAction( ioArgs.i_action);
+ std::map<Property, std::string> propertyMap;
+ for (::qpid::types::Variant::Map::const_iterator
+ iMapIter = ioArgs.i_propertyMap.begin();
+ iMapIter != ioArgs.i_propertyMap.end();
+ iMapIter++)
+ {
+ Property property = AclHelper::getProperty(iMapIter->first);
+ propertyMap.insert(make_pair(property, iMapIter->second));
+ }
+
+ boost::shared_ptr<AclData> dataLocal;
+ {
+ Mutex::ScopedLock locker(dataLock);
+ dataLocal = data; //rcu copy
+ }
+ AclResult aclResult = dataLocal->lookup(
+ ioArgs.i_userId,
+ action,
+ objType,
+ ioArgs.i_objectName,
+ &propertyMap);
+
+ ioArgs.o_result = AclHelper::getAclResultStr(aclResult);
+ result = STATUS_OK;
+
+ } catch (const std::exception& e) {
+ std::ostringstream oss;
+ oss << "AclLookup invalid name : " << e.what();
+ ioArgs.o_result = oss.str();
+ text = oss.str();
+ }
+
+ return result;
+}
+
+
+//
+// management lookupPublish function performs fastpath
+// PUBLISH EXCHANGE query on acl engine
+//
+Manageable::status_t Acl::lookupPublish(qpid::management::Args& args, std::string& /*text*/)
+{
+ _qmf::ArgsAclLookupPublish& ioArgs = (_qmf::ArgsAclLookupPublish&) args;
+ boost::shared_ptr<AclData> dataLocal;
+ {
+ Mutex::ScopedLock locker(dataLock);
+ dataLocal = data; //rcu copy
+ }
+ AclResult aclResult = dataLocal->lookup(
+ ioArgs.i_userId,
+ ACT_PUBLISH,
+ OBJ_EXCHANGE,
+ ioArgs.i_exchangeName,
+ ioArgs.i_routingKey);
+
+ ioArgs.o_result = AclHelper::getAclResultStr(aclResult);
+
+ return STATUS_OK;
+}
+
+
+Acl::~Acl(){
+ broker->getConnectionObservers().remove(connectionCounter);
+}
ManagementObject* Acl::GetManagementObject(void) const
{
return (ManagementObject*) mgmtObject;
}
-Manageable::status_t Acl::ManagementMethod (uint32_t methodId, Args& /*args*/, string& text)
+Manageable::status_t Acl::ManagementMethod (uint32_t methodId, Args& args, string& text)
{
Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
QPID_LOG (debug, "ACL: Queue::ManagementMethod [id=" << methodId << "]");
@@ -214,6 +312,14 @@ Manageable::status_t Acl::ManagementMethod (uint32_t methodId, Args& /*args*/, s
else
status = Manageable::STATUS_USER;
break;
+
+ case _qmf::Acl::METHOD_LOOKUP :
+ status = lookup(args, text);
+ break;
+
+ case _qmf::Acl::METHOD_LOOKUPPUBLISH :
+ status = lookupPublish(args, text);
+ break;
}
return status;
diff --git a/qpid/cpp/src/qpid/acl/Acl.h b/qpid/cpp/src/qpid/acl/Acl.h
index cc90fa4097..4893f71ef2 100644
--- a/qpid/cpp/src/qpid/acl/Acl.h
+++ b/qpid/cpp/src/qpid/acl/Acl.h
@@ -30,6 +30,7 @@
#include "qmf/org/apache/qpid/acl/Acl.h"
#include "qpid/sys/Mutex.h"
+#include <boost/shared_ptr.hpp>
#include <map>
#include <string>
@@ -37,12 +38,17 @@
namespace qpid {
namespace broker {
class Broker;
+class Connection;
}
namespace acl {
+class ConnectionCounter;
struct AclValues {
- std::string aclFile;
+ std::string aclFile;
+ uint16_t aclMaxConnectPerUser;
+ uint16_t aclMaxConnectPerIp;
+ uint16_t aclMaxConnectTotal;
};
@@ -50,54 +56,61 @@ class Acl : public broker::AclModule, public RefCounted, public management::Mana
{
private:
- acl::AclValues aclValues;
- broker::Broker* broker;
- bool transferAcl;
- boost::shared_ptr<AclData> data;
- qmf::org::apache::qpid::acl::Acl* mgmtObject; // mgnt owns lifecycle
- qpid::management::ManagementAgent* agent;
- mutable qpid::sys::Mutex dataLock;
+ acl::AclValues aclValues;
+ broker::Broker* broker;
+ bool transferAcl;
+ boost::shared_ptr<AclData> data;
+ qmf::org::apache::qpid::acl::Acl* mgmtObject; // mgnt owns lifecycle
+ qpid::management::ManagementAgent* agent;
+ mutable qpid::sys::Mutex dataLock;
+ boost::shared_ptr<ConnectionCounter> connectionCounter;
public:
- Acl (AclValues& av, broker::Broker& b);
-
- void initialize();
-
- inline virtual bool doTransferAcl() {return transferAcl;};
-
- // create specilied authorise methods for cases that need faster matching as needed.
- virtual bool authorise(
- const std::string& id,
- const Action& action,
- const ObjectType& objType,
- const std::string& name,
- std::map<Property,
- std::string>* params=0);
-
- virtual bool authorise(
- const std::string& id,
- const Action& action,
- const ObjectType& objType,
- const std::string& ExchangeName,
- const std::string& RoutingKey);
-
- virtual ~Acl();
+ Acl (AclValues& av, broker::Broker& b);
+
+ /** reportConnectLimit
+ * issue management counts and alerts for denied connections
+ */
+ void reportConnectLimit(const std::string user, const std::string addr);
+
+ inline virtual bool doTransferAcl() {
+ return transferAcl;
+ };
+
+// create specilied authorise methods for cases that need faster matching as needed.
+ virtual bool authorise(
+ const std::string& id,
+ const Action& action,
+ const ObjectType& objType,
+ const std::string& name,
+ std::map<Property, std::string>* params=0);
+
+ virtual bool authorise(
+ const std::string& id,
+ const Action& action,
+ const ObjectType& objType,
+ const std::string& ExchangeName,
+ const std::string& RoutingKey);
+
+ virtual bool approveConnection(const broker::Connection& connection);
+
+ virtual ~Acl();
private:
- bool result(
- const AclResult& aclreslt,
- const std::string& id,
- const Action& action,
- const ObjectType& objType,
- const std::string& name);
- bool readAclFile(std::string& errorText);
- bool readAclFile(std::string& aclFile, std::string& errorText);
- virtual qpid::management::ManagementObject* GetManagementObject(void) const;
- virtual management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args, std::string& text);
+ bool result(
+ const AclResult& aclreslt,
+ const std::string& id,
+ const Action& action,
+ const ObjectType& objType,
+ const std::string& name);
+ bool readAclFile(std::string& errorText);
+ bool readAclFile(std::string& aclFile, std::string& errorText);
+ Manageable::status_t lookup (management::Args& args, std::string& text);
+ Manageable::status_t lookupPublish(management::Args& args, std::string& text);
+ virtual qpid::management::ManagementObject* GetManagementObject(void) const;
+ virtual management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args, std::string& text);
};
-
-
}} // namespace qpid::acl
#endif // QPID_ACL_ACL_H
diff --git a/qpid/cpp/src/qpid/acl/AclConnectionCounter.cpp b/qpid/cpp/src/qpid/acl/AclConnectionCounter.cpp
new file mode 100644
index 0000000000..052fa3c222
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclConnectionCounter.cpp
@@ -0,0 +1,324 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "AclConnectionCounter.h"
+#include "Acl.h"
+#include "qpid/broker/Connection.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Mutex.h"
+#include <assert.h>
+#include <sstream>
+
+using namespace qpid::sys;
+
+namespace qpid {
+namespace acl {
+
+//
+// This module instantiates a broker::ConnectionObserver and limits client
+// connections by counting connections per user name, per client IP address
+// and per total connection count.
+//
+
+
+//
+//
+//
+ConnectionCounter::ConnectionCounter(Acl& a, uint16_t nl, uint16_t hl, uint16_t tl) :
+ acl(a), nameLimit(nl), hostLimit(hl), totalLimit(tl), totalCurrentConnections(0) {}
+
+ConnectionCounter::~ConnectionCounter() {}
+
+
+//
+// limitApproveLH
+//
+// Connection creation approver. Return true only if user is under limit.
+// Called with lock held.
+//
+bool ConnectionCounter::limitApproveLH(
+ connectCountsMap_t& theMap,
+ const std::string& theName,
+ uint16_t theLimit,
+ bool emitLog) {
+
+ bool result(true);
+ if (theLimit > 0) {
+ uint16_t count;
+ connectCountsMap_t::iterator eRef = theMap.find(theName);
+ if (eRef != theMap.end()) {
+ count = (uint16_t)(*eRef).second;
+ result = count <= theLimit;
+ } else {
+ // Not found
+ count = 0;
+ }
+ if (emitLog) {
+ QPID_LOG(trace, "ACL ConnectionApprover IP=" << theName
+ << " limit=" << theLimit
+ << " curValue=" << count
+ << " result=" << (result ? "allow" : "deny"));
+ }
+ }
+ return result;
+}
+
+
+//
+// countConnectionLH
+//
+// Increment the name's count in map and return a comparison against the limit.
+// called with dataLock already taken
+//
+bool ConnectionCounter::countConnectionLH(
+ connectCountsMap_t& theMap,
+ const std::string& theName,
+ uint16_t theLimit,
+ bool emitLog) {
+
+ bool result(true);
+ uint16_t count(0);
+ if (theLimit > 0) {
+ connectCountsMap_t::iterator eRef = theMap.find(theName);
+ if (eRef != theMap.end()) {
+ count = (uint16_t)(*eRef).second + 1;
+ (*eRef).second = count;
+ result = count <= theLimit;
+ } else {
+ theMap[theName] = count = 1;
+ }
+ if (emitLog) {
+ QPID_LOG(trace, "ACL ConnectionApprover user=" << theName
+ << " limit=" << theLimit
+ << " curValue=" << count
+ << " result=" << (result ? "allow" : "deny"));
+ }
+ }
+ return result;
+}
+
+
+//
+// releaseLH
+//
+// Decrement the name's count in map.
+// called with dataLock already taken
+//
+void ConnectionCounter::releaseLH(
+ connectCountsMap_t& theMap, const std::string& theName, uint16_t theLimit) {
+
+ if (theLimit > 0) {
+ connectCountsMap_t::iterator eRef = theMap.find(theName);
+ if (eRef != theMap.end()) {
+ uint16_t count = (uint16_t) (*eRef).second;
+ assert (count > 0);
+ if (1 == count) {
+ theMap.erase (eRef);
+ } else {
+ (*eRef).second = count - 1;
+ }
+ } else {
+ // User had no connections.
+ QPID_LOG(notice, "ACL ConnectionCounter Connection for '" << theName
+ << "' not found in connection count pool");
+ }
+ }
+}
+
+
+//
+// connection - called during Connection's constructor
+//
+void ConnectionCounter::connection(broker::Connection& connection) {
+ QPID_LOG(trace, "ACL ConnectionCounter new connection: " << connection.getMgmtId());
+
+ const std::string& hostName(getClientHost(connection.getMgmtId()));
+
+ Mutex::ScopedLock locker(dataLock);
+
+ // Total connections goes up
+ totalCurrentConnections += 1;
+
+ // Record the fact that this connection exists
+ connectProgressMap[connection.getMgmtId()] = C_CREATED;
+
+ // Count the connection from this host.
+ (void) countConnectionLH(connectByHostMap, hostName, hostLimit, false);
+}
+
+
+//
+// closed - called during Connection's destructor
+//
+void ConnectionCounter::closed(broker::Connection& connection) {
+ QPID_LOG(trace, "ACL ConnectionCounter closed: " << connection.getMgmtId()
+ << ", userId:" << connection.getUserId());
+
+ Mutex::ScopedLock locker(dataLock);
+
+ connectCountsMap_t::iterator eRef = connectProgressMap.find(connection.getMgmtId());
+ if (eRef != connectProgressMap.end()) {
+ if ((*eRef).second == C_OPENED){
+ // Normal case: connection was created and opened.
+ // Decrement user in-use counts
+ releaseLH(connectByNameMap,
+ connection.getUserId(),
+ nameLimit);
+ } else {
+ // Connection was created but not opened.
+ // Don't decrement user count.
+ }
+
+ // Decrement host in-use count.
+ releaseLH(connectByHostMap,
+ getClientHost(connection.getMgmtId()),
+ hostLimit);
+
+ // destroy connection progress indicator
+ connectProgressMap.erase(eRef);
+
+ } else {
+ // connection not found in progress map
+ QPID_LOG(notice, "ACL ConnectionCounter closed info for '" << connection.getMgmtId()
+ << "' not found in connection state pool");
+ }
+
+ // total connections
+ totalCurrentConnections -= 1;
+}
+
+
+//
+// approveConnection
+// check total connections, connections from IP, connections by user and
+// disallow if over any limit
+//
+bool ConnectionCounter::approveConnection(const broker::Connection& connection)
+{
+ const std::string& hostName(getClientHost(connection.getMgmtId()));
+ const std::string& userName( connection.getUserId());
+
+ Mutex::ScopedLock locker(dataLock);
+
+ // Bump state from CREATED to OPENED
+ (void) countConnectionLH(connectProgressMap, connection.getMgmtId(),
+ C_OPENED, false);
+
+ // Approve total connections
+ bool okTotal = true;
+ if (totalLimit > 0) {
+ okTotal = totalCurrentConnections <= totalLimit;
+ if (!connection.isShadow()) {
+ QPID_LOG(trace, "ACL ConnectionApprover totalLimit=" << totalLimit
+ << " curValue=" << totalCurrentConnections
+ << " result=" << (okTotal ? "allow" : "deny"));
+ }
+ }
+
+ // Approve by IP host connections
+ bool okByIP = limitApproveLH(connectByHostMap, hostName, hostLimit, !connection.isShadow());
+
+ // Count and Approve the connection by the user
+ bool okByUser = countConnectionLH(connectByNameMap, userName, nameLimit, !connection.isShadow());
+
+ if (!connection.isShadow()) {
+ // Emit separate log for each disapproval
+ if (!okTotal) {
+ QPID_LOG(error, "Client max total connection count limit of " << totalLimit
+ << " exceeded by '"
+ << connection.getMgmtId() << "', user: '"
+ << userName << "'. Connection refused");
+ }
+ if (!okByIP) {
+ QPID_LOG(error, "Client max per-host connection count limit of "
+ << hostLimit << " exceeded by '"
+ << connection.getMgmtId() << "', user: '"
+ << userName << "'. Connection refused.");
+ }
+ if (!okByUser) {
+ QPID_LOG(error, "Client max per-user connection count limit of "
+ << nameLimit << " exceeded by '"
+ << connection.getMgmtId() << "', user: '"
+ << userName << "'. Connection refused.");
+ }
+
+ // Count/Event once for each disapproval
+ bool result = okTotal && okByIP && okByUser;
+ if (!result) {
+ acl.reportConnectLimit(userName, hostName);
+ }
+
+ return result;
+ } else {
+ // Always allow shadow connections
+ if (!okTotal) {
+ QPID_LOG(warning, "Client max total connection count limit of " << totalLimit
+ << " exceeded by '"
+ << connection.getMgmtId() << "', user: '"
+ << userName << "' but still within tolerance. Cluster connection allowed");
+ }
+ if (!okByIP) {
+ QPID_LOG(warning, "Client max per-host connection count limit of "
+ << hostLimit << " exceeded by '"
+ << connection.getMgmtId() << "', user: '"
+ << userName << "' but still within tolerance. Cluster connection allowed");
+ }
+ if (!okByUser) {
+ QPID_LOG(warning, "Client max per-user connection count limit of "
+ << nameLimit << " exceeded by '"
+ << connection.getMgmtId() << "', user: '"
+ << userName << "' but still within tolerance. Cluster connection allowed");
+ }
+ if (okTotal && okByIP && okByUser) {
+ QPID_LOG(debug, "Cluster client connection: '"
+ << connection.getMgmtId() << "', user '"
+ << userName << "' allowed");
+ }
+ return true;
+ }
+}
+
+//
+// getClientIp - given a connection's mgmtId return the client host part.
+//
+// TODO: Ideally this would be a method of the connection itself.
+// TODO: Verify it works with rdma connection names.
+//
+std::string ConnectionCounter::getClientHost(const std::string mgmtId)
+{
+ size_t hyphen = mgmtId.find('-');
+ if (std::string::npos != hyphen) {
+ size_t colon = mgmtId.find_last_of(':');
+ if (std::string::npos != colon) {
+ // trailing colon found
+ return mgmtId.substr(hyphen+1, colon - hyphen - 1);
+ } else {
+ // colon not found - use everything after hyphen
+ return mgmtId.substr(hyphen+1);
+ }
+ }
+
+ // no hyphen found - use whole string
+ assert(false);
+ return mgmtId;
+}
+
+}} // namespace qpid::ha
diff --git a/qpid/cpp/src/qpid/acl/AclConnectionCounter.h b/qpid/cpp/src/qpid/acl/AclConnectionCounter.h
new file mode 100644
index 0000000000..eec8e90256
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclConnectionCounter.h
@@ -0,0 +1,101 @@
+#ifndef QPID_ACL_CONNECTIONCOUNTER_H
+#define QPID_ACL_CONNECTIONCOUNTER_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/broker/ConnectionObserver.h"
+#include "qpid/sys/Mutex.h"
+#include <boost/iterator/iterator_concepts.hpp>
+
+#include <map>
+
+namespace qpid {
+
+namespace broker {
+class Connection;
+}
+
+namespace acl {
+class Acl;
+
+ /**
+ * Terminate client connections when a user tries to create 'too many'.
+ * Terminate hostIp connections when an IP host tries to create 'too many'.
+ */
+class ConnectionCounter : public broker::ConnectionObserver
+{
+private:
+ typedef std::map<std::string, uint32_t> connectCountsMap_t;
+ enum CONNECTION_PROGRESS { C_CREATED=1, C_OPENED=2 };
+
+ Acl& acl;
+ uint16_t nameLimit;
+ uint16_t hostLimit;
+ uint16_t totalLimit;
+ uint16_t totalCurrentConnections;
+ qpid::sys::Mutex dataLock;
+
+ /** Records per-connection state */
+ connectCountsMap_t connectProgressMap;
+
+ /** Records per-username counts */
+ connectCountsMap_t connectByNameMap;
+
+ /** Records per-host counts */
+ connectCountsMap_t connectByHostMap;
+
+ /** Given a connection's management ID, return the client host name */
+ std::string getClientHost(const std::string mgmtId);
+
+ /** Return approval for proposed connection */
+ bool limitApproveLH(connectCountsMap_t& theMap,
+ const std::string& theName,
+ uint16_t theLimit,
+ bool emitLog);
+
+ /** Record a connection.
+ * @return indication if user/host is over its limit */
+ bool countConnectionLH(connectCountsMap_t& theMap,
+ const std::string& theName,
+ uint16_t theLimit,
+ bool emitLog);
+
+ /** Release a connection */
+ void releaseLH(connectCountsMap_t& theMap,
+ const std::string& theName,
+ uint16_t theLimit);
+
+public:
+ ConnectionCounter(Acl& acl, uint16_t nl, uint16_t hl, uint16_t tl);
+ ~ConnectionCounter();
+
+ // ConnectionObserver interface
+ void connection(broker::Connection& connection);
+ void closed(broker::Connection& connection);
+
+ // Connection counting
+ bool approveConnection(const broker::Connection& conn);
+};
+
+}} // namespace qpid::ha
+
+#endif /*!QPID_ACL_CONNECTIONCOUNTER_H*/
diff --git a/qpid/cpp/src/qpid/acl/AclData.cpp b/qpid/cpp/src/qpid/acl/AclData.cpp
index 03a8a19db9..da7f240a9b 100644
--- a/qpid/cpp/src/qpid/acl/AclData.cpp
+++ b/qpid/cpp/src/qpid/acl/AclData.cpp
@@ -60,7 +60,7 @@ namespace acl {
// matchProp
//
// Compare a rule's property name with a lookup name,
- // The rule's name may contains a trailing '*' to specify a wildcard match.
+ // The rule's name may contain a trailing '*' to specify a wildcard match.
//
bool AclData::matchProp(const std::string& ruleStr,
const std::string& lookupStr)
@@ -312,14 +312,14 @@ namespace acl {
const Action& action,
const ObjectType& objType,
const std::string& /*Exchange*/ name,
- const std::string& RoutingKey)
+ const std::string& routingKey)
{
QPID_LOG(debug, "ACL: Lookup for id:" << id
<< " action:" << AclHelper::getActionStr((Action) action)
<< " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType)
<< " exchange name:" << name
- << " with routing key " << RoutingKey);
+ << " with routing key " << routingKey);
AclResult aclresult = decisionMode;
@@ -337,6 +337,8 @@ namespace acl {
{
rsItr--;
+ QPID_LOG(debug, "ACL: checking rule " << rsItr->toString());
+
// loop the names looking for match
bool match =true;
for (specPropertyMapItr pMItr = rsItr->props.begin();
@@ -364,17 +366,17 @@ namespace acl {
break;
case acl::SPECPROP_ROUTINGKEY:
- if (matchProp(pMItr->second, RoutingKey))
+ if (matchProp(pMItr->second, routingKey))
{
QPID_LOG(debug, "ACL: lookup key name '"
- << name << "' matched with rule routing key '"
+ << routingKey << "' matched with rule routing key '"
<< pMItr->second << "'");
}
else
{
match= false;
QPID_LOG(debug, "ACL: lookup key name '"
- << name << "' did not match with rule routing key '"
+ << routingKey << "' did not match with rule routing key '"
<< pMItr->second << "'");
}
break;
diff --git a/qpid/cpp/src/qpid/acl/AclPlugin.cpp b/qpid/cpp/src/qpid/acl/AclPlugin.cpp
index d611797c49..ebf5e90afe 100644
--- a/qpid/cpp/src/qpid/acl/AclPlugin.cpp
+++ b/qpid/cpp/src/qpid/acl/AclPlugin.cpp
@@ -39,8 +39,13 @@ struct AclOptions : public Options {
AclValues& values;
AclOptions(AclValues& v) : Options("ACL Options"), values(v) {
+ values.aclMaxConnectTotal = 500;
addOptions()
- ("acl-file", optValue(values.aclFile, "FILE"), "The policy file to load from, loaded from data dir");
+ ("acl-file", optValue(values.aclFile, "FILE"), "The policy file to load from, loaded from data dir")
+ ("max-connections" , optValue(values.aclMaxConnectTotal, "N"), "The maximum combined number of connections allowed. 0 implies no limit.")
+ ("max-connections-per-user", optValue(values.aclMaxConnectPerUser, "N"), "The maximum number of connections allowed per user. 0 implies no limit.")
+ ("max-connections-per-ip" , optValue(values.aclMaxConnectPerIp, "N"), "The maximum number of connections allowed per host IP address. 0 implies no limit.")
+ ;
}
};
@@ -67,7 +72,6 @@ struct AclPlugin : public Plugin {
oss << b.getDataDir().getPath() << "/" << values.aclFile;
values.aclFile = oss.str();
}
-
acl = new Acl(values, b);
b.setAcl(acl.get());
b.addFinalizer(boost::bind(&AclPlugin::shutdown, this));
diff --git a/qpid/cpp/src/qpid/acl/AclReader.cpp b/qpid/cpp/src/qpid/acl/AclReader.cpp
index 80debf1bd1..0830f3f4f4 100644
--- a/qpid/cpp/src/qpid/acl/AclReader.cpp
+++ b/qpid/cpp/src/qpid/acl/AclReader.cpp
@@ -314,7 +314,17 @@ namespace acl {
if (contFlag) {
gmCitr citr = groups.find(groupName);
for (unsigned i = 0; i < toksSize; i++) {
- if (!isValidUserName(toks[i])) return false;
+ if (isValidGroupName(toks[i])) {
+ if (toks[i] == groupName) {
+ QPID_LOG(debug, "ACL: Line: " << lineNumber
+ << ", Ignoring recursive sub-group \"" << toks[i] << "\".");
+ continue;
+ } else if (groups.find(toks[i]) == groups.end()) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Sub-group \"" << toks[i] << "\" not defined yet.";
+ return false;
+ }
+ } else if (!isValidUserName(toks[i])) return false;
addName(toks[i], citr->second);
}
} else {
@@ -332,7 +342,17 @@ namespace acl {
gmCitr citr = addGroup(toks[1]);
if (citr == groups.end()) return false;
for (unsigned i = 2; i < toksSize; i++) {
- if (!isValidUserName(toks[i])) return false;
+ if (isValidGroupName(toks[i])) {
+ if (toks[i] == groupName) {
+ QPID_LOG(debug, "ACL: Line: " << lineNumber
+ << ", Ignoring recursive sub-group \"" << toks[i] << "\".");
+ continue;
+ } else if (groups.find(toks[i]) == groups.end()) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Sub-group \"" << toks[i] << "\" not defined yet.";
+ return false;
+ }
+ } else if (!isValidUserName(toks[i])) return false;
addName(toks[i], citr->second);
}
}
@@ -356,7 +376,7 @@ namespace acl {
void AclReader::addName(const std::string& name, nameSetPtr groupNameSet) {
gmCitr citr = groups.find(name);
- if (citr != groups.end() && citr->first != name){
+ if (citr != groups.end()) {
// This is a previously defined group: add all the names in that group to this group
groupNameSet->insert(citr->second->begin(), citr->second->end());
} else {
diff --git a/qpid/cpp/src/qpid/acl/management-schema.xml b/qpid/cpp/src/qpid/acl/management-schema.xml
index 7f48a9be34..f52c251bed 100644
--- a/qpid/cpp/src/qpid/acl/management-schema.xml
+++ b/qpid/cpp/src/qpid/acl/management-schema.xml
@@ -17,14 +17,49 @@
-->
<class name="Acl">
- <property name="brokerRef" type="objId" references="org.apache.qpid.broker:Broker" access="RO" index="y" parentRef="y"/>
- <property name="policyFile" type="lstr" access="RO" desc="Name of the policy file"/>
- <property name="enforcingAcl" type="bool" access="RO" desc="Currently Enforcing ACL"/>
- <property name="transferAcl" type="bool" access="RO" desc="Any transfer ACL rules in force"/>
- <property name="lastAclLoad" type="absTime" access="RO" desc="Timestamp of last successful load of ACL"/>
- <statistic name="aclDenyCount" type="count64" unit="request" desc="Number of ACL requests denied"/>
+ <property name="brokerRef" type="objId" references="org.apache.qpid.broker:Broker" access="RO" index="y" parentRef="y"/>
+ <property name="policyFile" type="lstr" access="RO" desc="Name of the policy file"/>
+ <property name="enforcingAcl" type="bool" access="RO" desc="Currently Enforcing ACL"/>
+ <property name="transferAcl" type="bool" access="RO" desc="Any transfer ACL rules in force"/>
+ <property name="lastAclLoad" type="absTime" access="RO" desc="Timestamp of last successful load of ACL"/>
+ <property name="maxConnections" type="uint16" access="RO" desc="Maximum allowed connections"/>
+ <property name="maxConnectionsPerIp" type="uint16" access="RO" desc="Maximum allowed connections"/>
+ <property name="maxConnectionsPerUser" type="uint16" access="RO" desc="Maximum allowed connections"/>
+ <statistic name="aclDenyCount" type="count64" unit="request" desc="Number of ACL requests denied"/>
+ <statistic name="connectionDenyCount" type="count64" unit="connection" desc="Number of connections denied"/>
<method name="reloadACLFile" desc="Reload the ACL file"/>
+
+ <!--
+ Lookup is a general object lookup
+ User Name
+ Action
+ Object
+ Object Name
+ Property Map consisting of <"name" "value"> string pairs.
+ -->
+ <method name="Lookup" desc="Lookup: user action object [objectName [propertyMap]]">
+ <arg name="userId" dir="I" type="lstr"/>
+ <arg name="action" dir="I" type="lstr"/>
+ <arg name="object" dir="I" type="lstr"/>
+ <arg name="objectName" dir="I" type="lstr"/>
+ <arg name="propertyMap" dir="I" type="map"/>
+ <arg name="result" dir="O" type="lstr"/>
+ </method>
+
+ <!--
+ LookupPublish is a specific lookup for a PUBLISH EXCHANGE fastpath
+ User Name
+ Exchange Name
+ Routing Key
+ -->
+ <method name="LookupPublish" desc="Lookup PUBLISH EXCHANGE: user exchangeName routingKey">
+ <arg name="userId" dir="I" type="lstr"/>
+ <arg name="exchangeName" dir="I" type="lstr"/>
+ <arg name="routingKey" dir="I" type="lstr"/>
+ <arg name="result" dir="O" type="lstr"/>
+ </method>
+
</class>
<eventArguments>
@@ -34,10 +69,12 @@
<arg name="objectType" type="sstr"/>
<arg name="reason" type="lstr"/>
<arg name="userId" type="sstr"/>
+ <arg name="clientAddr" type="sstr"/>
</eventArguments>
<event name="allow" sev="inform" args="userId, action, objectType, objectName, arguments"/>
<event name="deny" sev="notice" args="userId, action, objectType, objectName, arguments"/>
+ <event name="connectionDeny" sev="notice" args="userId, clientAddr"/>
<event name="fileLoaded" sev="inform" args="userId"/>
<event name="fileLoadFailed" sev="error" args="userId, reason"/>
diff --git a/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp
index f183ff8e0c..09b7fa58e9 100644
--- a/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp
+++ b/qpid/cpp/src/qpid/agent/ManagementAgentImpl.cpp
@@ -31,9 +31,11 @@
#include <fstream>
#include <boost/lexical_cast.hpp>
+namespace qpid {
+namespace management {
+
using namespace qpid::client;
using namespace qpid::framing;
-using namespace qpid::management;
using namespace qpid::sys;
using namespace std;
using std::stringstream;
@@ -1260,7 +1262,7 @@ void ManagementAgentImpl::ConnectionThread::run()
int totalSleep = 0;
do {
sys::Mutex::ScopedUnlock _unlock(connLock);
- ::sleep(delayMin);
+ qpid::sys::sleep(delayMin);
totalSleep += delayMin;
} while (totalSleep < delay && !shutdown);
sleeping = false;
@@ -1396,8 +1398,11 @@ void ManagementAgentImpl::PublishThread::run()
sleepTime = 1;
while (totalSleep < agent.getInterval() && !shutdown) {
- ::sleep(sleepTime);
+ qpid::sys::sleep(sleepTime);
totalSleep += sleepTime;
}
}
}
+
+}}
+
diff --git a/qpid/cpp/src/qpid/broker/AclModule.h b/qpid/cpp/src/qpid/broker/AclModule.h
index be143a23e7..7c180439cf 100644
--- a/qpid/cpp/src/qpid/broker/AclModule.h
+++ b/qpid/cpp/src/qpid/broker/AclModule.h
@@ -22,6 +22,7 @@
#include "qpid/RefCounted.h"
+#include "qpid/Exception.h"
#include <boost/shared_ptr.hpp>
#include <map>
#include <set>
@@ -68,7 +69,6 @@ namespace acl {
PROP_DURABLE,
PROP_OWNER,
PROP_ROUTINGKEY,
- PROP_PASSIVE,
PROP_AUTODELETE,
PROP_EXCLUSIVE,
PROP_TYPE,
@@ -88,7 +88,6 @@ namespace acl {
SPECPROP_DURABLE = PROP_DURABLE,
SPECPROP_OWNER = PROP_OWNER,
SPECPROP_ROUTINGKEY = PROP_ROUTINGKEY,
- SPECPROP_PASSIVE = PROP_PASSIVE,
SPECPROP_AUTODELETE = PROP_AUTODELETE,
SPECPROP_EXCLUSIVE = PROP_EXCLUSIVE,
SPECPROP_TYPE = PROP_TYPE,
@@ -114,6 +113,7 @@ namespace acl {
namespace broker {
+ class Connection;
class AclModule
{
@@ -140,6 +140,11 @@ namespace broker {
// Add specialized authorise() methods as required.
+ /** Approve connection by counting connections total, per-IP, and
+ * per-user.
+ */
+ virtual bool approveConnection (const Connection& connection)=0;
+
virtual ~AclModule() {};
};
} // namespace broker
@@ -156,7 +161,7 @@ namespace acl {
if (str.compare("broker") == 0) return OBJ_BROKER;
if (str.compare("link") == 0) return OBJ_LINK;
if (str.compare("method") == 0) return OBJ_METHOD;
- throw str;
+ throw qpid::Exception(str);
}
static inline std::string getObjectTypeStr(const ObjectType o) {
switch (o) {
@@ -179,7 +184,7 @@ namespace acl {
if (str.compare("delete") == 0) return ACT_DELETE;
if (str.compare("purge") == 0) return ACT_PURGE;
if (str.compare("update") == 0) return ACT_UPDATE;
- throw str;
+ throw qpid::Exception(str);
}
static inline std::string getActionStr(const Action a) {
switch (a) {
@@ -201,7 +206,6 @@ namespace acl {
if (str.compare("durable") == 0) return PROP_DURABLE;
if (str.compare("owner") == 0) return PROP_OWNER;
if (str.compare("routingkey") == 0) return PROP_ROUTINGKEY;
- if (str.compare("passive") == 0) return PROP_PASSIVE;
if (str.compare("autodelete") == 0) return PROP_AUTODELETE;
if (str.compare("exclusive") == 0) return PROP_EXCLUSIVE;
if (str.compare("type") == 0) return PROP_TYPE;
@@ -212,7 +216,7 @@ namespace acl {
if (str.compare("policytype") == 0) return PROP_POLICYTYPE;
if (str.compare("maxqueuesize") == 0) return PROP_MAXQUEUESIZE;
if (str.compare("maxqueuecount") == 0) return PROP_MAXQUEUECOUNT;
- throw str;
+ throw qpid::Exception(str);
}
static inline std::string getPropertyStr(const Property p) {
switch (p) {
@@ -220,7 +224,6 @@ namespace acl {
case PROP_DURABLE: return "durable";
case PROP_OWNER: return "owner";
case PROP_ROUTINGKEY: return "routingkey";
- case PROP_PASSIVE: return "passive";
case PROP_AUTODELETE: return "autodelete";
case PROP_EXCLUSIVE: return "exclusive";
case PROP_TYPE: return "type";
@@ -240,7 +243,6 @@ namespace acl {
if (str.compare("durable") == 0) return SPECPROP_DURABLE;
if (str.compare("owner") == 0) return SPECPROP_OWNER;
if (str.compare("routingkey") == 0) return SPECPROP_ROUTINGKEY;
- if (str.compare("passive") == 0) return SPECPROP_PASSIVE;
if (str.compare("autodelete") == 0) return SPECPROP_AUTODELETE;
if (str.compare("exclusive") == 0) return SPECPROP_EXCLUSIVE;
if (str.compare("type") == 0) return SPECPROP_TYPE;
@@ -256,7 +258,7 @@ namespace acl {
// Allow old names in ACL file as aliases for newly-named properties
if (str.compare("maxqueuesize") == 0) return SPECPROP_MAXQUEUESIZEUPPERLIMIT;
if (str.compare("maxqueuecount") == 0) return SPECPROP_MAXQUEUECOUNTUPPERLIMIT;
- throw str;
+ throw qpid::Exception(str);
}
static inline std::string getPropertyStr(const SpecProperty p) {
switch (p) {
@@ -264,7 +266,6 @@ namespace acl {
case SPECPROP_DURABLE: return "durable";
case SPECPROP_OWNER: return "owner";
case SPECPROP_ROUTINGKEY: return "routingkey";
- case SPECPROP_PASSIVE: return "passive";
case SPECPROP_AUTODELETE: return "autodelete";
case SPECPROP_EXCLUSIVE: return "exclusive";
case SPECPROP_TYPE: return "type";
@@ -286,7 +287,7 @@ namespace acl {
if (str.compare("allow-log") == 0) return ALLOWLOG;
if (str.compare("deny") == 0) return DENY;
if (str.compare("deny-log") == 0) return DENYLOG;
- throw str;
+ throw qpid::Exception(str);
}
static inline std::string getAclResultStr(const AclResult r) {
switch (r) {
@@ -325,7 +326,6 @@ namespace acl {
propSetPtr p1(new propSet);
p1->insert(PROP_TYPE);
p1->insert(PROP_ALTERNATE);
- p1->insert(PROP_PASSIVE);
p1->insert(PROP_DURABLE);
propSetPtr p2(new propSet);
@@ -350,7 +350,6 @@ namespace acl {
propSetPtr p4(new propSet);
p4->insert(PROP_ALTERNATE);
- p4->insert(PROP_PASSIVE);
p4->insert(PROP_DURABLE);
p4->insert(PROP_EXCLUSIVE);
p4->insert(PROP_AUTODELETE);
diff --git a/qpid/cpp/src/qpid/broker/Bridge.cpp b/qpid/cpp/src/qpid/broker/Bridge.cpp
index 9a1f4be468..d1706b5907 100644
--- a/qpid/cpp/src/qpid/broker/Bridge.cpp
+++ b/qpid/cpp/src/qpid/broker/Bridge.cpp
@@ -57,22 +57,25 @@ void Bridge::PushHandler::handle(framing::AMQFrame& frame)
conn->received(frame);
}
-Bridge::Bridge(Link* _link, framing::ChannelId _id, CancellationListener l,
- const _qmf::ArgsLinkBridge& _args,
- InitializeCallback init) :
- link(_link), id(_id), args(_args), mgmtObject(0),
- listener(l), name(Uuid(true).str()), queueName("qpid.bridge_queue_"), persistenceId(0),
- initialize(init)
+Bridge::Bridge(const std::string& _name, Link* _link, framing::ChannelId _id,
+ CancellationListener l, const _qmf::ArgsLinkBridge& _args,
+ InitializeCallback init, const std::string& _queueName, const string& ae) :
+ link(_link), channel(_id), args(_args), mgmtObject(0),
+ listener(l), name(_name),
+ queueName(_queueName.empty() ? "qpid.bridge_queue_" + name + "_" + link->getBroker()->getFederationTag()
+ : _queueName),
+ altEx(ae), persistenceId(0),
+ connState(0), conn(0), initialize(init), detached(false),
+ useExistingQueue(!_queueName.empty()),
+ sessionName("qpid.bridge_session_" + name + "_" + link->getBroker()->getFederationTag())
{
- std::stringstream title;
- title << id << "_" << name;
- queueName += title.str();
ManagementAgent* agent = link->getBroker()->getManagementAgent();
if (agent != 0) {
mgmtObject = new _qmf::Bridge
- (agent, this, link, id, args.i_durable, args.i_src, args.i_dest,
+ (agent, this, link, name, args.i_durable, args.i_src, args.i_dest,
args.i_key, args.i_srcIsQueue, args.i_srcIsLocal,
args.i_tag, args.i_excludes, args.i_dynamic, args.i_sync);
+ mgmtObject->set_channelId(channel);
agent->addObject(mgmtObject);
}
QPID_LOG(debug, "Bridge " << name << " created from " << args.i_src << " to " << args.i_dest);
@@ -85,25 +88,27 @@ Bridge::~Bridge()
void Bridge::create(Connection& c)
{
+ detached = false; // Reset detached in case we are recovering.
connState = &c;
conn = &c;
FieldTable options;
if (args.i_sync) options.setInt("qpid.sync_frequency", args.i_sync);
- SessionHandler& sessionHandler = c.getChannel(id);
+ SessionHandler& sessionHandler = c.getChannel(channel);
+ sessionHandler.setErrorListener(shared_from_this());
if (args.i_srcIsLocal) {
if (args.i_dynamic)
throw Exception("Dynamic routing not supported for push routes");
// Point the bridging commands at the local connection handler
pushHandler.reset(new PushHandler(&c));
- channelHandler.reset(new framing::ChannelHandler(id, pushHandler.get()));
+ channelHandler.reset(new framing::ChannelHandler(channel, pushHandler.get()));
session.reset(new framing::AMQP_ServerProxy::Session(*channelHandler));
peer.reset(new framing::AMQP_ServerProxy(*channelHandler));
- session->attach(name, false);
+ session->attach(sessionName, false);
session->commandPoint(0,0);
} else {
- sessionHandler.attachAs(name);
+ sessionHandler.attachAs(sessionName);
// Point the bridging commands at the remote peer broker
peer.reset(new framing::AMQP_ServerProxy(sessionHandler.out));
}
@@ -112,7 +117,7 @@ void Bridge::create(Connection& c)
if (initialize) initialize(*this, sessionHandler);
else if (args.i_srcIsQueue) {
peer->getMessage().subscribe(args.i_src, args.i_dest, args.i_sync ? 0 : 1, 0, false, "", 0, options);
- peer->getMessage().flow(args.i_dest, 0, 0xFFFFFFFF);
+ peer->getMessage().flow(args.i_dest, 0, args.i_sync ? 2 * args.i_sync : 0xFFFFFFFF);
peer->getMessage().flow(args.i_dest, 1, 0xFFFFFFFF);
QPID_LOG(debug, "Activated bridge " << name << " for route from queue " << args.i_src << " to " << args.i_dest);
} else {
@@ -135,12 +140,13 @@ void Bridge::create(Connection& c)
}
bool durable = false;//should this be an arg, or would we use srcIsQueue for durable queues?
- bool autoDelete = !durable;//auto delete transient queues?
- peer->getQueue().declare(queueName, "", false, durable, true, autoDelete, queueSettings);
+ bool exclusive = !useExistingQueue; // only exclusive if the queue is owned by the bridge
+ bool autoDelete = exclusive && !durable;//auto delete transient queues?
+ peer->getQueue().declare(queueName, altEx, false, durable, exclusive, autoDelete, queueSettings);
if (!args.i_dynamic)
peer->getExchange().bind(queueName, args.i_src, args.i_key, FieldTable());
- peer->getMessage().subscribe(queueName, args.i_dest, 1, 0, false, "", 0, FieldTable());
- peer->getMessage().flow(args.i_dest, 0, 0xFFFFFFFF);
+ peer->getMessage().subscribe(queueName, args.i_dest, (useExistingQueue && args.i_sync) ? 0 : 1, 0, false, "", 0, options);
+ peer->getMessage().flow(args.i_dest, 0, (useExistingQueue && args.i_sync) ? 2 * args.i_sync : 0xFFFFFFFF);
peer->getMessage().flow(args.i_dest, 1, 0xFFFFFFFF);
if (args.i_dynamic) {
@@ -160,11 +166,12 @@ void Bridge::cancel(Connection&)
{
if (resetProxy()) {
peer->getMessage().cancel(args.i_dest);
- peer->getSession().detach(name);
+ peer->getSession().detach(sessionName);
}
QPID_LOG(debug, "Cancelled bridge " << name);
}
+/** Notify the bridge that the connection has closed */
void Bridge::closed()
{
if (args.i_dynamic) {
@@ -174,24 +181,32 @@ void Bridge::closed()
QPID_LOG(debug, "Closed bridge " << name);
}
-void Bridge::destroy()
+/** Shut down the bridge */
+void Bridge::close()
{
- listener(this);
+ listener(this); // ask the LinkRegistry to destroy us
}
-bool Bridge::isSessionReady() const
+void Bridge::setPersistenceId(uint64_t pId) const
{
- SessionHandler& sessionHandler = conn->getChannel(id);
- return sessionHandler.ready();
+ persistenceId = pId;
}
-void Bridge::setPersistenceId(uint64_t pId) const
+
+const std::string Bridge::ENCODED_IDENTIFIER("bridge.v2");
+const std::string Bridge::ENCODED_IDENTIFIER_V1("bridge");
+
+bool Bridge::isEncodedBridge(const std::string& key)
{
- persistenceId = pId;
+ return key == ENCODED_IDENTIFIER || key == ENCODED_IDENTIFIER_V1;
}
+
Bridge::shared_ptr Bridge::decode(LinkRegistry& links, Buffer& buffer)
{
+ string kind;
+ buffer.getShortString(kind);
+
string host;
uint16_t port;
string src;
@@ -199,9 +214,33 @@ Bridge::shared_ptr Bridge::decode(LinkRegistry& links, Buffer& buffer)
string key;
string id;
string excludes;
+ string name;
+
+ Link::shared_ptr link;
+ if (kind == ENCODED_IDENTIFIER_V1) {
+ /** previous versions identified the bridge by host:port, not by name, and
+ * transport wasn't provided. Try to find a link using those paramters.
+ */
+ buffer.getShortString(host);
+ port = buffer.getShort();
+
+ link = links.getLink(host, port);
+ if (!link) {
+ QPID_LOG(error, "Bridge::decode() failed: cannot find Link for host=" << host << ", port=" << port);
+ return Bridge::shared_ptr();
+ }
+ } else {
+ string linkName;
+
+ buffer.getShortString(name);
+ buffer.getShortString(linkName);
+ link = links.getLink(linkName);
+ if (!link) {
+ QPID_LOG(error, "Bridge::decode() failed: cannot find Link named='" << linkName << "'");
+ return Bridge::shared_ptr();
+ }
+ }
- buffer.getShortString(host);
- port = buffer.getShort();
bool durable(buffer.getOctet());
buffer.getShortString(src);
buffer.getShortString(dest);
@@ -213,15 +252,21 @@ Bridge::shared_ptr Bridge::decode(LinkRegistry& links, Buffer& buffer)
bool dynamic(buffer.getOctet());
uint16_t sync = buffer.getShort();
- return links.declare(host, port, durable, src, dest, key,
- is_queue, is_local, id, excludes, dynamic, sync).first;
+ if (kind == ENCODED_IDENTIFIER_V1) {
+ /** previous versions did not provide a name for the bridge, so create one
+ */
+ name = createName(link->getName(), src, dest, key);
+ }
+
+ return links.declare(name, *link, durable, src, dest, key, is_queue,
+ is_local, id, excludes, dynamic, sync).first;
}
void Bridge::encode(Buffer& buffer) const
{
- buffer.putShortString(string("bridge"));
- buffer.putShortString(link->getHost());
- buffer.putShort(link->getPort());
+ buffer.putShortString(ENCODED_IDENTIFIER);
+ buffer.putShortString(name);
+ buffer.putShortString(link->getName());
buffer.putOctet(args.i_durable ? 1 : 0);
buffer.putShortString(args.i_src);
buffer.putShortString(args.i_dest);
@@ -236,9 +281,9 @@ void Bridge::encode(Buffer& buffer) const
uint32_t Bridge::encodedSize() const
{
- return link->getHost().size() + 1 // short-string (host)
- + 7 // short-string ("bridge")
- + 2 // port
+ return ENCODED_IDENTIFIER.size() + 1 // +1 byte length
+ + name.size() + 1
+ + link->getName().size() + 1
+ 1 // durable
+ args.i_src.size() + 1
+ args.i_dest.size() + 1
@@ -262,7 +307,8 @@ management::Manageable::status_t Bridge::ManagementMethod(uint32_t methodId,
{
if (methodId == _qmf::Bridge::METHOD_CLOSE) {
//notify that we are closed
- destroy();
+ QPID_LOG(debug, "Bridge::close() method called on bridge '" << name << "'");
+ close();
return management::Manageable::STATUS_OK;
} else {
return management::Manageable::STATUS_UNKNOWN_METHOD;
@@ -309,7 +355,7 @@ void Bridge::sendReorigin()
}
bool Bridge::resetProxy()
{
- SessionHandler& sessionHandler = conn->getChannel(id);
+ SessionHandler& sessionHandler = conn->getChannel(channel);
if (!sessionHandler.getSession()) peer.reset();
else peer.reset(new framing::AMQP_ServerProxy(sessionHandler.out));
return peer.get();
@@ -321,7 +367,7 @@ void Bridge::ioThreadPropagateBinding(const string& queue, const string& exchang
peer->getExchange().bind(queue, exchange, key, args);
} else {
QPID_LOG(error, "Cannot propagate binding for dynamic bridge as session has been detached, deleting dynamic bridge");
- destroy();
+ close();
}
}
@@ -336,4 +382,38 @@ const string& Bridge::getLocalTag() const
return link->getBroker()->getFederationTag();
}
+// SessionHandler::ErrorListener methods.
+void Bridge::connectionException(
+ framing::connection::CloseCode code, const std::string& msg)
+{
+ if (errorListener) errorListener->connectionException(code, msg);
+}
+
+void Bridge::channelException(
+ framing::session::DetachCode code, const std::string& msg)
+{
+ if (errorListener) errorListener->channelException(code, msg);
+}
+
+void Bridge::executionException(
+ framing::execution::ErrorCode code, const std::string& msg)
+{
+ if (errorListener) errorListener->executionException(code, msg);
+}
+
+void Bridge::detach() {
+ detached = true;
+ if (errorListener) errorListener->detach();
+}
+
+std::string Bridge::createName(const std::string& linkName,
+ const std::string& src,
+ const std::string& dest,
+ const std::string& key)
+{
+ std::stringstream keystream;
+ keystream << linkName << "!" << src << "!" << dest << "!" << key;
+ return keystream.str();
+}
+
}}
diff --git a/qpid/cpp/src/qpid/broker/Bridge.h b/qpid/cpp/src/qpid/broker/Bridge.h
index b849b11ba8..ee298afd45 100644
--- a/qpid/cpp/src/qpid/broker/Bridge.h
+++ b/qpid/cpp/src/qpid/broker/Bridge.h
@@ -29,10 +29,12 @@
#include "qpid/framing/FieldTable.h"
#include "qpid/management/Manageable.h"
#include "qpid/broker/Exchange.h"
+#include "qpid/broker/SessionHandler.h"
#include "qmf/org/apache/qpid/broker/ArgsLinkBridge.h"
#include "qmf/org/apache/qpid/broker/Bridge.h"
#include <boost/function.hpp>
+#include <boost/enable_shared_from_this.hpp>
#include <memory>
namespace qpid {
@@ -42,28 +44,33 @@ class Connection;
class ConnectionState;
class Link;
class LinkRegistry;
-class SessionHandler;
-class Bridge : public PersistableConfig, public management::Manageable, public Exchange::DynamicBridge
+class Bridge : public PersistableConfig,
+ public management::Manageable,
+ public Exchange::DynamicBridge,
+ public SessionHandler::ErrorListener,
+ public boost::enable_shared_from_this<Bridge>
{
-public:
+ public:
typedef boost::shared_ptr<Bridge> shared_ptr;
typedef boost::function<void(Bridge*)> CancellationListener;
typedef boost::function<void(Bridge&, SessionHandler&)> InitializeCallback;
- Bridge(Link* link, framing::ChannelId id, CancellationListener l,
+ Bridge(const std::string& name, Link* link, framing::ChannelId id, CancellationListener l,
const qmf::org::apache::qpid::broker::ArgsLinkBridge& args,
- InitializeCallback init
+ InitializeCallback init, const std::string& queueName="",
+ const std::string& altExchange=""
);
~Bridge();
- void create(Connection& c);
- void cancel(Connection& c);
- void closed();
- void destroy();
+ QPID_BROKER_EXTERN void close();
bool isDurable() { return args.i_durable; }
+ Link *getLink() const { return link; }
+ const std::string getSrc() const { return args.i_src; }
+ const std::string getDest() const { return args.i_dest; }
+ const std::string getKey() const { return args.i_key; }
- bool isSessionReady() const;
+ bool isDetached() const { return detached; }
management::ManagementObject* GetManagementObject() const;
management::Manageable::status_t ManagementMethod(uint32_t methodId,
@@ -76,7 +83,11 @@ public:
uint32_t encodedSize() const;
void encode(framing::Buffer& buffer) const;
const std::string& getName() const { return name; }
+
+ static const std::string ENCODED_IDENTIFIER;
+ static const std::string ENCODED_IDENTIFIER_V1;
static Bridge::shared_ptr decode(LinkRegistry& links, framing::Buffer& buffer);
+ static bool isEncodedBridge(const std::string& key);
// Exchange::DynamicBridge methods
void propagateBinding(const std::string& key, const std::string& tagList, const std::string& op, const std::string& origin, qpid::framing::FieldTable* extra_args=0);
@@ -89,7 +100,20 @@ public:
std::string getQueueName() const { return queueName; }
const qmf::org::apache::qpid::broker::ArgsLinkBridge& getArgs() { return args; }
-private:
+ /** create a name for a bridge (if none supplied by user config) */
+ static std::string createName(const std::string& linkName,
+ const std::string& src,
+ const std::string& dest,
+ const std::string& key);
+
+ // SessionHandler::ErrorListener methods.
+ void connectionException(framing::connection::CloseCode code, const std::string& msg);
+ void channelException(framing::session::DetachCode, const std::string& msg);
+ void executionException(framing::execution::ErrorCode, const std::string& msg);
+ void detach();
+
+ void setErrorListener(boost::shared_ptr<ErrorListener> e) { errorListener = e; }
+ private:
struct PushHandler : framing::FrameHandler {
PushHandler(Connection* c) { conn = c; }
void handle(framing::AMQFrame& frame);
@@ -101,19 +125,30 @@ private:
std::auto_ptr<framing::AMQP_ServerProxy::Session> session;
std::auto_ptr<framing::AMQP_ServerProxy> peer;
- Link* link;
- framing::ChannelId id;
+ Link* const link;
+ const framing::ChannelId channel;
qmf::org::apache::qpid::broker::ArgsLinkBridge args;
qmf::org::apache::qpid::broker::Bridge* mgmtObject;
CancellationListener listener;
std::string name;
std::string queueName;
+ std::string altEx;
mutable uint64_t persistenceId;
ConnectionState* connState;
Connection* conn;
InitializeCallback initialize;
-
+ bool detached; // Set when session is detached.
bool resetProxy();
+
+ // connection Management (called by owning Link)
+ void create(Connection& c);
+ void cancel(Connection& c);
+ void closed();
+ friend class Link; // to call create, cancel, closed()
+ boost::shared_ptr<ErrorListener> errorListener;
+
+ const bool useExistingQueue;
+ const std::string sessionName;
};
diff --git a/qpid/cpp/src/qpid/broker/Broker.cpp b/qpid/cpp/src/qpid/broker/Broker.cpp
index 02111b4387..c202d9c4e8 100644
--- a/qpid/cpp/src/qpid/broker/Broker.cpp
+++ b/qpid/cpp/src/qpid/broker/Broker.cpp
@@ -108,7 +108,6 @@ Broker::Options::Options(const std::string& name) :
noDataDir(0),
port(DEFAULT_PORT),
workerThreads(5),
- maxConnections(500),
connectionBacklog(10),
enableMgmt(1),
mgmtPublish(1),
@@ -128,8 +127,10 @@ Broker::Options::Options(const std::string& name) :
queueFlowResumeRatio(70),
queueThresholdEventRatio(80),
defaultMsgGroup("qpid.no-group"),
- timestampRcvMsgs(false), // set the 0.10 timestamp delivery property
- linkMaintenanceInterval(2)
+ timestampRcvMsgs(false), // set the 0.10 timestamp delivery property
+ linkMaintenanceInterval(2),
+ linkHeartbeatInterval(120),
+ maxNegotiateTime(2000) // 2s
{
int c = sys::SystemInfo::concurrency();
workerThreads=c+1;
@@ -146,7 +147,6 @@ Broker::Options::Options(const std::string& name) :
("no-data-dir", optValue(noDataDir), "Don't use a data directory. No persistent configuration will be loaded or stored")
("port,p", optValue(port,"PORT"), "Tells the broker to listen on PORT")
("worker-threads", optValue(workerThreads, "N"), "Sets the broker thread pool size")
- ("max-connections", optValue(maxConnections, "N"), "Sets the maximum allowed connections")
("connection-backlog", optValue(connectionBacklog, "N"), "Sets the connection backlog limit for the server socket")
("mgmt-enable,m", optValue(enableMgmt,"yes|no"), "Enable Management")
("mgmt-publish", optValue(mgmtPublish,"yes|no"), "Enable Publish of Management Data ('no' implies query-only)")
@@ -171,6 +171,9 @@ Broker::Options::Options(const std::string& name) :
("default-message-group", optValue(defaultMsgGroup, "GROUP-IDENTIFER"), "Group identifier to assign to messages delivered to a message group queue that do not contain an identifier.")
("enable-timestamp", optValue(timestampRcvMsgs, "yes|no"), "Add current time to each received message.")
("link-maintenace-interval", optValue(linkMaintenanceInterval, "SECONDS"))
+ ("link-heartbeat-interval", optValue(linkHeartbeatInterval, "SECONDS"))
+ ("max-negotiate-time", optValue(maxNegotiateTime, "MilliSeconds"), "Maximum time a connection can take to send the initial protocol negotiation")
+ ("federation-tag", optValue(fedTag, "NAME"), "Override the federation tag")
;
}
@@ -208,7 +211,6 @@ Broker::Broker(const Broker::Options& conf) :
inCluster(false),
clusterUpdatee(false),
expiryPolicy(new ExpiryPolicy),
- connectionCounter(conf.maxConnections),
getKnownBrokers(boost::bind(&Broker::getKnownBrokersImpl, this)),
deferDelivery(boost::bind(&Broker::deferDeliveryImpl, this, _1, _2))
{
@@ -227,7 +229,6 @@ Broker::Broker(const Broker::Options& conf) :
mgmtObject->set_systemRef(system->GetManagementObject()->getObjectId());
mgmtObject->set_port(conf.port);
mgmtObject->set_workerThreads(conf.workerThreads);
- mgmtObject->set_maxConns(conf.maxConnections);
mgmtObject->set_connBacklog(conf.connectionBacklog);
mgmtObject->set_mgmtPubInterval(conf.mgmtPubInterval);
mgmtObject->set_mgmtPublish(conf.mgmtPublish);
@@ -244,8 +245,11 @@ Broker::Broker(const Broker::Options& conf) :
// management schema correct.
Vhost* vhost = new Vhost(this, this);
vhostObject = Vhost::shared_ptr(vhost);
- framing::Uuid uuid(managementAgent->getUuid());
- federationTag = uuid.str();
+ if (conf.fedTag.empty()) {
+ framing::Uuid uuid(managementAgent->getUuid());
+ federationTag = uuid.str();
+ } else
+ federationTag = conf.fedTag;
vhostObject->setFederationTag(federationTag);
queues.setParent(vhost);
@@ -254,8 +258,11 @@ Broker::Broker(const Broker::Options& conf) :
} else {
// Management is disabled so there is no broker management ID.
// Create a unique uuid to use as the federation tag.
- framing::Uuid uuid(true);
- federationTag = uuid.str();
+ if (conf.fedTag.empty()) {
+ framing::Uuid uuid(true);
+ federationTag = uuid.str();
+ } else
+ federationTag = conf.fedTag;
}
QueuePolicy::setDefaultMaxSize(conf.queueLimit);
@@ -346,7 +353,7 @@ Broker::Broker(const Broker::Options& conf) :
knownBrokers.push_back(Url(conf.knownHosts));
}
- } catch (const std::exception& /*e*/) {
+ } catch (const std::exception&) {
finalize();
throw;
}
@@ -438,7 +445,7 @@ Manageable* Broker::GetVhostObject(void) const
Manageable::status_t Broker::ManagementMethod (uint32_t methodId,
Args& args,
- string&)
+ string& text)
{
Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
@@ -453,6 +460,14 @@ Manageable::status_t Broker::ManagementMethod (uint32_t methodId,
status = Manageable::STATUS_OK;
break;
case _qmf::Broker::METHOD_CONNECT : {
+ /** Management is creating a Link to a remote broker using the host and port of
+ * the remote. This (old) interface does not allow management to specify a name
+ * for the link, nor does it allow multiple Links to the same remote. Use the
+ * "create()" broker method if these features are needed.
+ * TBD: deprecate this interface.
+ */
+ QPID_LOG(info, "The Broker::connect() method will be removed in a future release of QPID."
+ " Please use the Broker::create() method with type='link' instead.");
_qmf::ArgsBrokerConnect& hp=
dynamic_cast<_qmf::ArgsBrokerConnect&>(args);
@@ -461,13 +476,24 @@ Manageable::status_t Broker::ManagementMethod (uint32_t methodId,
"; durable=" << (hp.i_durable?"T":"F") << "; authMech=\"" << hp.i_authMechanism << "\"");
if (!getProtocolFactory(transport)) {
QPID_LOG(error, "Transport '" << transport << "' not supported");
+ text = "transport type not supported";
return Manageable::STATUS_NOT_IMPLEMENTED;
}
- std::pair<Link::shared_ptr, bool> response =
- links.declare (hp.i_host, hp.i_port, transport, hp.i_durable,
- hp.i_authMechanism, hp.i_username, hp.i_password);
- if (hp.i_durable && response.second)
- store->create(*response.first);
+
+ // Does a link to the remote already exist? If so, re-use the existing link
+ // - this behavior is backward compatible with previous releases.
+ if (!links.getLink(hp.i_host, hp.i_port, transport)) {
+ // new link, need to generate a unique name for it
+ std::pair<Link::shared_ptr, bool> response =
+ links.declare(Link::createName(transport, hp.i_host, hp.i_port),
+ hp.i_host, hp.i_port, transport,
+ hp.i_durable, hp.i_authMechanism, hp.i_username, hp.i_password);
+ if (!response.first) {
+ text = "Unable to create Link";
+ status = Manageable::STATUS_PARAMETER_INVALID;
+ break;
+ }
+ }
status = Manageable::STATUS_OK;
break;
}
@@ -538,6 +564,8 @@ const std::string TYPE_QUEUE("queue");
const std::string TYPE_EXCHANGE("exchange");
const std::string TYPE_TOPIC("topic");
const std::string TYPE_BINDING("binding");
+const std::string TYPE_LINK("link");
+const std::string TYPE_BRIDGE("bridge");
const std::string DURABLE("durable");
const std::string AUTO_DELETE("auto-delete");
const std::string ALTERNATE_EXCHANGE("alternate-exchange");
@@ -549,6 +577,26 @@ const std::string ATTRIBUTE_TIMESTAMP_0_10("timestamp-0.10");
const std::string _TRUE("true");
const std::string _FALSE("false");
+
+// parameters for creating a Link object, see mgmt schema
+const std::string HOST("host");
+const std::string PORT("port");
+const std::string TRANSPORT("transport");
+const std::string AUTH_MECHANISM("authMechanism");
+const std::string USERNAME("username");
+const std::string PASSWORD("password");
+
+// parameters for creating a Bridge object, see mgmt schema
+const std::string LINK("link");
+const std::string SRC("src");
+const std::string DEST("dest");
+const std::string KEY("key");
+const std::string TAG("tag");
+const std::string EXCLUDES("excludes");
+const std::string SRC_IS_QUEUE("srcIsQueue");
+const std::string SRC_IS_LOCAL("srcIsLocal");
+const std::string DYNAMIC("dynamic");
+const std::string SYNC("sync");
}
struct InvalidBindingIdentifier : public qpid::Exception
@@ -598,6 +646,25 @@ struct UnknownObjectType : public qpid::Exception
std::string getPrefix() const { return "unknown object type"; }
};
+struct ReservedObjectName : public qpid::Exception
+{
+ ReservedObjectName(const std::string& type) : qpid::Exception(type) {}
+ std::string getPrefix() const { return std::string("names prefixed with '")
+ + QPID_NAME_PREFIX + std::string("' are reserved"); }
+};
+
+struct UnsupportedTransport : public qpid::Exception
+{
+ UnsupportedTransport(const std::string& type) : qpid::Exception(type) {}
+ std::string getPrefix() const { return "transport is not supported"; }
+};
+
+struct InvalidParameter : public qpid::Exception
+{
+ InvalidParameter(const std::string& type) : qpid::Exception(type) {}
+ std::string getPrefix() const { return "invalid parameter to method call"; }
+};
+
void Broker::createObject(const std::string& type, const std::string& name,
const Variant::Map& properties, bool /*strict*/, const ConnectionState* context)
{
@@ -669,6 +736,113 @@ void Broker::createObject(const std::string& type, const std::string& name,
amqp_0_10::translate(extensions, arguments);
bind(binding.queue, binding.exchange, binding.key, arguments, userId, connectionId);
+
+ } else if (type == TYPE_LINK) {
+
+ QPID_LOG (debug, "createObject: Link; name=" << name << "; args=" << properties );
+
+ if (name.compare(0, QPID_NAME_PREFIX.length(), QPID_NAME_PREFIX) == 0) {
+ QPID_LOG(error, "Link name='" << name << "' cannot use the reserved prefix '" << QPID_NAME_PREFIX << "'");
+ throw ReservedObjectName(name);
+ }
+
+ std::string host;
+ uint16_t port = 0;
+ std::string transport = TCP_TRANSPORT;
+ bool durable = false;
+ std::string authMech, username, password;
+
+ for (Variant::Map::const_iterator i = properties.begin(); i != properties.end(); ++i) {
+ if (i->first == HOST) host = i->second.asString();
+ else if (i->first == PORT) port = i->second.asUint16();
+ else if (i->first == TRANSPORT) transport = i->second.asString();
+ else if (i->first == DURABLE) durable = bool(i->second);
+ else if (i->first == AUTH_MECHANISM) authMech = i->second.asString();
+ else if (i->first == USERNAME) username = i->second.asString();
+ else if (i->first == PASSWORD) password = i->second.asString();
+ else {
+ // TODO: strict checking here
+ }
+ }
+
+ if (!getProtocolFactory(transport)) {
+ QPID_LOG(error, "Transport '" << transport << "' not supported.");
+ throw UnsupportedTransport(transport);
+ }
+
+ std::pair<boost::shared_ptr<Link>, bool> rc;
+ rc = links.declare(name, host, port, transport, durable, authMech, username, password);
+ if (!rc.first) {
+ QPID_LOG (error, "Failed to create Link object, name=" << name << " remote=" << host << ":" << port <<
+ "; transport=" << transport << "; durable=" << (durable?"T":"F") << "; authMech=\"" << authMech << "\"");
+ throw InvalidParameter(name);
+ }
+ if (!rc.second) {
+ QPID_LOG (error, "Failed to create a new Link object, name=" << name << " already exists.");
+ throw ObjectAlreadyExists(name);
+ }
+
+ } else if (type == TYPE_BRIDGE) {
+
+ QPID_LOG (debug, "createObject: Bridge; name=" << name << "; args=" << properties );
+
+ if (name.compare(0, QPID_NAME_PREFIX.length(), QPID_NAME_PREFIX) == 0) {
+ QPID_LOG(error, "Bridge name='" << name << "' cannot use the reserved prefix '" << QPID_NAME_PREFIX << "'");
+ throw ReservedObjectName(name);
+ }
+
+ std::string linkName;
+ std::string src;
+ std::string dest;
+ std::string key;
+ std::string id;
+ std::string excludes;
+ std::string queueName;
+ bool durable = false;
+ bool srcIsQueue = false;
+ bool srcIsLocal = false;
+ bool dynamic = false;
+ uint16_t sync = 0;
+
+ for (Variant::Map::const_iterator i = properties.begin(); i != properties.end(); ++i) {
+
+ if (i->first == LINK) linkName = i->second.asString();
+ else if (i->first == SRC) src = i->second.asString();
+ else if (i->first == DEST) dest = i->second.asString();
+ else if (i->first == KEY) key = i->second.asString();
+ else if (i->first == TAG) id = i->second.asString();
+ else if (i->first == EXCLUDES) excludes = i->second.asString();
+ else if (i->first == SRC_IS_QUEUE) srcIsQueue = bool(i->second);
+ else if (i->first == SRC_IS_LOCAL) srcIsLocal = bool(i->second);
+ else if (i->first == DYNAMIC) dynamic = bool(i->second);
+ else if (i->first == SYNC) sync = i->second.asUint16();
+ else if (i->first == DURABLE) durable = bool(i->second);
+ else if (i->first == QUEUE_NAME) queueName = i->second.asString();
+ else {
+ // TODO: strict checking here
+ }
+ }
+
+ boost::shared_ptr<Link> link;
+ if (linkName.empty() || !(link = links.getLink(linkName))) {
+ QPID_LOG(error, "Link '" << linkName << "' not found; bridge create failed.");
+ throw InvalidParameter(name);
+ }
+ std::pair<Bridge::shared_ptr, bool> rc =
+ links.declare(name, *link, durable, src, dest, key, srcIsQueue, srcIsLocal, id, excludes,
+ dynamic, sync,
+ 0,
+ queueName);
+
+ if (!rc.first) {
+ QPID_LOG (error, "Failed to create Bridge object, name=" << name << " link=" << linkName <<
+ "; src=" << src << "; dest=" << dest << "; key=" << key);
+ throw InvalidParameter(name);
+ }
+ if (!rc.second) {
+ QPID_LOG (error, "Failed to create a new Bridge object, name=" << name << " already exists.");
+ throw ObjectAlreadyExists(name);
+ }
} else {
throw UnknownObjectType(type);
}
@@ -691,6 +865,16 @@ void Broker::deleteObject(const std::string& type, const std::string& name,
} else if (type == TYPE_BINDING) {
BindingIdentifier binding(name);
unbind(binding.queue, binding.exchange, binding.key, userId, connectionId);
+ } else if (type == TYPE_LINK) {
+ boost::shared_ptr<Link> link = links.getLink(name);
+ if (link) {
+ link->close();
+ }
+ } else if (type == TYPE_BRIDGE) {
+ boost::shared_ptr<Bridge> bridge = links.getBridge(name);
+ if (bridge) {
+ bridge->close();
+ }
} else {
throw UnknownObjectType(type);
}
@@ -883,7 +1067,6 @@ std::pair<boost::shared_ptr<Queue>, bool> Broker::createQueue(
if (acl) {
std::map<acl::Property, std::string> params;
params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange));
- params.insert(make_pair(acl::PROP_PASSIVE, _FALSE));
params.insert(make_pair(acl::PROP_DURABLE, durable ? _TRUE : _FALSE));
params.insert(make_pair(acl::PROP_EXCLUSIVE, owner ? _TRUE : _FALSE));
params.insert(make_pair(acl::PROP_AUTODELETE, autodelete ? _TRUE : _FALSE));
@@ -954,7 +1137,6 @@ std::pair<Exchange::shared_ptr, bool> Broker::createExchange(
std::map<acl::Property, std::string> params;
params.insert(make_pair(acl::PROP_TYPE, type));
params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange));
- params.insert(make_pair(acl::PROP_PASSIVE, _FALSE));
params.insert(make_pair(acl::PROP_DURABLE, durable ? _TRUE : _FALSE));
if (!acl->authorise(userId,acl::ACT_CREATE,acl::OBJ_EXCHANGE,name,&params) )
throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange create request from " << userId));
@@ -1044,6 +1226,7 @@ void Broker::bind(const std::string& queueName,
throw framing::NotFoundException(QPID_MSG("Bind failed. No such exchange: " << exchangeName));
} else {
if (queue->bind(exchange, key, arguments)) {
+ getConfigurationObservers().bind(exchange, queue, key, arguments);
if (managementAgent.get()) {
managementAgent->raiseEvent(_qmf::EventBind(connectionId, userId, exchangeName,
queueName, key, ManagementAgent::toMap(arguments)));
@@ -1079,6 +1262,8 @@ void Broker::unbind(const std::string& queueName,
if (exchange->isDurable() && queue->isDurable()) {
store->unbind(*exchange, *queue, key, qpid::framing::FieldTable());
}
+ getConfigurationObservers().unbind(
+ exchange, queue, key, framing::FieldTable());
if (managementAgent.get()) {
managementAgent->raiseEvent(_qmf::EventUnbind(connectionId, userId, exchangeName, queueName, key));
}
@@ -1086,5 +1271,19 @@ void Broker::unbind(const std::string& queueName,
}
}
+// FIXME aconway 2012-04-27: access to linkClientProperties is
+// not properly thread safe, you could lose fields if 2 threads
+// attempt to add a field concurrently.
+
+framing::FieldTable Broker::getLinkClientProperties() const {
+ sys::Mutex::ScopedLock l(linkClientPropertiesLock);
+ return linkClientProperties;
+}
+
+void Broker::setLinkClientProperties(const framing::FieldTable& ft) {
+ sys::Mutex::ScopedLock l(linkClientPropertiesLock);
+ linkClientProperties = ft;
+}
+
}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/Broker.h b/qpid/cpp/src/qpid/broker/Broker.h
index 543d42e002..c385a3ec56 100644
--- a/qpid/cpp/src/qpid/broker/Broker.h
+++ b/qpid/cpp/src/qpid/broker/Broker.h
@@ -39,6 +39,7 @@
#include "qpid/broker/ExpiryPolicy.h"
#include "qpid/broker/ConsumerFactory.h"
#include "qpid/broker/ConnectionObservers.h"
+#include "qpid/broker/ConfigurationObservers.h"
#include "qpid/management/Manageable.h"
#include "qpid/management/ManagementAgent.h"
#include "qmf/org/apache/qpid/broker/Broker.h"
@@ -63,8 +64,8 @@
namespace qpid {
namespace sys {
- class ProtocolFactory;
- class Poller;
+class ProtocolFactory;
+class Poller;
}
struct Url;
@@ -90,7 +91,7 @@ class Broker : public sys::Runnable, public Plugin::Target,
public management::Manageable,
public RefCounted
{
-public:
+ public:
struct Options : public qpid::Options {
static const std::string DEFAULT_DATA_DIR_LOCATION;
@@ -102,7 +103,6 @@ public:
std::string dataDir;
uint16_t port;
int workerThreads;
- int maxConnections;
int connectionBacklog;
bool enableMgmt;
bool mgmtPublish;
@@ -126,31 +126,14 @@ public:
std::string defaultMsgGroup;
bool timestampRcvMsgs;
double linkMaintenanceInterval; // FIXME aconway 2012-02-13: consistent parsing of SECONDS values.
+ uint16_t linkHeartbeatInterval;
+ uint32_t maxNegotiateTime; // Max time in ms for connection with no negotiation
+ std::string fedTag;
private:
std::string getHome();
};
- class ConnectionCounter {
- int maxConnections;
- int connectionCount;
- sys::Mutex connectionCountLock;
- public:
- ConnectionCounter(int mc): maxConnections(mc),connectionCount(0) {};
- void inc_connectionCount() {
- sys::ScopedLock<sys::Mutex> l(connectionCountLock);
- connectionCount++;
- }
- void dec_connectionCount() {
- sys::ScopedLock<sys::Mutex> l(connectionCountLock);
- connectionCount--;
- }
- bool allowConnection() {
- sys::ScopedLock<sys::Mutex> l(connectionCountLock);
- return (maxConnections <= connectionCount);
- }
- };
-
private:
typedef std::map<std::string, boost::shared_ptr<sys::ProtocolFactory> > ProtocolFactoryMap;
@@ -182,6 +165,7 @@ public:
AclModule* acl;
DataDir dataDir;
ConnectionObservers connectionObservers;
+ ConfigurationObservers configurationObservers;
QueueRegistry queues;
ExchangeRegistry exchanges;
@@ -202,9 +186,11 @@ public:
bool recovery;
bool inCluster, clusterUpdatee;
boost::intrusive_ptr<ExpiryPolicy> expiryPolicy;
- ConnectionCounter connectionCounter;
ConsumerFactories consumerFactories;
+ mutable sys::Mutex linkClientPropertiesLock;
+ framing::FieldTable linkClientProperties;
+
public:
QPID_BROKER_EXTERN virtual ~Broker();
@@ -315,8 +301,6 @@ public:
management::ManagementAgent* getManagementAgent() { return managementAgent.get(); }
- ConnectionCounter& getConnectionCounter() {return connectionCounter;}
-
/**
* Never true in a stand-alone broker. In a cluster, return true
* to defer delivery of messages deliveredg in a cluster-unsafe
@@ -375,6 +359,14 @@ public:
ConsumerFactories& getConsumerFactories() { return consumerFactories; }
ConnectionObservers& getConnectionObservers() { return connectionObservers; }
+ ConfigurationObservers& getConfigurationObservers() { return configurationObservers; }
+
+ /** Properties to be set on outgoing link connections */
+ QPID_BROKER_EXTERN framing::FieldTable getLinkClientProperties() const;
+ QPID_BROKER_EXTERN void setLinkClientProperties(const framing::FieldTable&);
+
+ /** Information identifying this system */
+ boost::shared_ptr<const System> getSystem() const { return systemObject; }
};
}}
diff --git a/qpid/cpp/src/qpid/broker/ConfigurationObserver.h b/qpid/cpp/src/qpid/broker/ConfigurationObserver.h
new file mode 100644
index 0000000000..701043db40
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/ConfigurationObserver.h
@@ -0,0 +1,61 @@
+#ifndef QPID_BROKER_CONFIGURATIONOBSERVER_H
+#define QPID_BROKER_CONFIGURATIONOBSERVER_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include <boost/shared_ptr.hpp>
+#include <string>
+
+namespace qpid {
+
+namespace framing {
+class FieldTable;
+}
+
+namespace broker {
+class Queue;
+class Exchange;
+
+
+/**
+ * Observer for changes to configuration (aka wiring)
+ */
+class ConfigurationObserver
+{
+ public:
+ virtual ~ConfigurationObserver() {}
+ virtual void queueCreate(const boost::shared_ptr<Queue>&) {}
+ virtual void queueDestroy(const boost::shared_ptr<Queue>&) {}
+ virtual void exchangeCreate(const boost::shared_ptr<Exchange>&) {}
+ virtual void exchangeDestroy(const boost::shared_ptr<Exchange>&) {}
+ virtual void bind(const boost::shared_ptr<Exchange>& ,
+ const boost::shared_ptr<Queue>& ,
+ const std::string& /*key*/,
+ const framing::FieldTable& /*args*/) {}
+ virtual void unbind(const boost::shared_ptr<Exchange>&,
+ const boost::shared_ptr<Queue>& ,
+ const std::string& /*key*/,
+ const framing::FieldTable& /*args*/) {}
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_CONFIGURATIONOBSERVER_H*/
diff --git a/qpid/cpp/src/qpid/broker/ConfigurationObservers.h b/qpid/cpp/src/qpid/broker/ConfigurationObservers.h
new file mode 100644
index 0000000000..4c1159747d
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/ConfigurationObservers.h
@@ -0,0 +1,72 @@
+#ifndef QPID_BROKER_CONFIGURATIONOBSERVERS_H
+#define QPID_BROKER_CONFIGURATIONOBSERVERS_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "ConfigurationObserver.h"
+#include "Observers.h"
+#include "qpid/sys/Mutex.h"
+
+namespace qpid {
+namespace broker {
+
+/**
+ * A configuration observer that delegates to a collection of
+ * configuration observers.
+ *
+ * THREAD SAFE
+ */
+class ConfigurationObservers : public ConfigurationObserver,
+ public Observers<ConfigurationObserver>
+{
+ public:
+ void queueCreate(const boost::shared_ptr<Queue>& q) {
+ each(boost::bind(&ConfigurationObserver::queueCreate, _1, q));
+ }
+ void queueDestroy(const boost::shared_ptr<Queue>& q) {
+ each(boost::bind(&ConfigurationObserver::queueDestroy, _1, q));
+ }
+ void exchangeCreate(const boost::shared_ptr<Exchange>& e) {
+ each(boost::bind(&ConfigurationObserver::exchangeCreate, _1, e));
+ }
+ void exchangeDestroy(const boost::shared_ptr<Exchange>& e) {
+ each(boost::bind(&ConfigurationObserver::exchangeDestroy, _1, e));
+ }
+ void bind(const boost::shared_ptr<Exchange>& exchange,
+ const boost::shared_ptr<Queue>& queue,
+ const std::string& key,
+ const framing::FieldTable& args) {
+ each(boost::bind(
+ &ConfigurationObserver::bind, _1, exchange, queue, key, args));
+ }
+ void unbind(const boost::shared_ptr<Exchange>& exchange,
+ const boost::shared_ptr<Queue>& queue,
+ const std::string& key,
+ const framing::FieldTable& args) {
+ each(boost::bind(
+ &ConfigurationObserver::unbind, _1, exchange, queue, key, args));
+ }
+};
+
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_CONFIGURATIONOBSERVERS_H*/
diff --git a/qpid/cpp/src/qpid/broker/Connection.cpp b/qpid/cpp/src/qpid/broker/Connection.cpp
index 5e339cec03..85914b6b3a 100644
--- a/qpid/cpp/src/qpid/broker/Connection.cpp
+++ b/qpid/cpp/src/qpid/broker/Connection.cpp
@@ -43,7 +43,7 @@
#include <iostream>
#include <assert.h>
-
+using std::string;
using namespace qpid::sys;
using namespace qpid::framing;
@@ -87,10 +87,14 @@ Connection::Connection(ConnectionOutputHandler* out_,
bool link_,
uint64_t objectId_,
bool shadow_,
- bool delayManagement) :
+ bool delayManagement,
+ bool authenticated_
+) :
ConnectionState(out_, broker_),
securitySettings(external),
- adapter(*this, link_, shadow_),
+ shadow(shadow_),
+ authenticated(authenticated_),
+ adapter(*this, link_),
link(link_),
mgmtClosing(false),
mgmtId(mgmtId_),
@@ -100,14 +104,12 @@ Connection::Connection(ConnectionOutputHandler* out_,
timer(broker_.getTimer()),
errorListener(0),
objectId(objectId_),
- shadow(shadow_),
outboundTracker(*this)
{
outboundTracker.wrap(out);
broker.getConnectionObservers().connection(*this);
// In a cluster, allow adding the management object to be delayed.
if (!delayManagement) addManagementObject();
- if (!isShadow()) broker.getConnectionCounter().inc_connectionCount();
}
void Connection::addManagementObject() {
@@ -148,8 +150,9 @@ Connection::~Connection()
heartbeatTimer->cancel();
if (timeoutTimer)
timeoutTimer->cancel();
-
- if (!isShadow()) broker.getConnectionCounter().dec_connectionCount();
+ if (linkHeartbeatTimer) {
+ linkHeartbeatTimer->cancel();
+ }
}
void Connection::received(framing::AMQFrame& frame) {
@@ -300,6 +303,9 @@ void Connection::close(connection::CloseCode code, const string& text)
heartbeatTimer->cancel();
if (timeoutTimer)
timeoutTimer->cancel();
+ if (linkHeartbeatTimer) {
+ linkHeartbeatTimer->cancel();
+ }
adapter.close(code, text);
//make sure we delete dangling pointers from outputTasks before deleting sessions
outputTasks.removeAll();
@@ -313,6 +319,9 @@ void Connection::sendClose() {
heartbeatTimer->cancel();
if (timeoutTimer)
timeoutTimer->cancel();
+ if (linkHeartbeatTimer) {
+ linkHeartbeatTimer->cancel();
+ }
adapter.close(connection::CLOSE_CODE_NORMAL, "OK");
getOutput().close();
}
@@ -326,6 +335,9 @@ void Connection::closed(){ // Physically closed, suspend open sessions.
heartbeatTimer->cancel();
if (timeoutTimer)
timeoutTimer->cancel();
+ if (linkHeartbeatTimer) {
+ linkHeartbeatTimer->cancel();
+ }
try {
while (!channels.empty())
ptr_map_ptr(channels.begin())->handleDetach();
@@ -435,6 +447,31 @@ struct ConnectionHeartbeatTask : public sys::TimerTask {
}
};
+class LinkHeartbeatTask : public qpid::sys::TimerTask {
+ sys::Timer& timer;
+ Connection& connection;
+ bool heartbeatSeen;
+
+ void fire() {
+ if (!heartbeatSeen) {
+ QPID_LOG(error, "Federation link connection " << connection.getMgmtId() << " missed 2 heartbeats - closing connection");
+ connection.abort();
+ } else {
+ heartbeatSeen = false;
+ // Setup next firing
+ setupNextFire();
+ timer.add(this);
+ }
+ }
+
+public:
+ LinkHeartbeatTask(sys::Timer& t, qpid::sys::Duration period, Connection& c) :
+ TimerTask(period, "LinkHeartbeatTask"), timer(t), connection(c), heartbeatSeen(false) {}
+
+ void heartbeatReceived() { heartbeatSeen = true; }
+};
+
+
void Connection::abort()
{
// Make sure that we don't try to send a heartbeat as we're
@@ -460,10 +497,21 @@ void Connection::setHeartbeatInterval(uint16_t heartbeat)
}
}
+void Connection::startLinkHeartbeatTimeoutTask() {
+ if (!linkHeartbeatTimer && heartbeat > 0) {
+ linkHeartbeatTimer = new LinkHeartbeatTask(timer, 2 * heartbeat * TIME_SEC, *this);
+ timer.add(linkHeartbeatTimer);
+ }
+}
+
void Connection::restartTimeout()
{
if (timeoutTimer)
timeoutTimer->touch();
+
+ if (linkHeartbeatTimer) {
+ static_cast<LinkHeartbeatTask*>(linkHeartbeatTimer.get())->heartbeatReceived();
+ }
}
bool Connection::isOpen() { return adapter.isOpen(); }
diff --git a/qpid/cpp/src/qpid/broker/Connection.h b/qpid/cpp/src/qpid/broker/Connection.h
index 858ab6f7f4..97de44f94d 100644
--- a/qpid/cpp/src/qpid/broker/Connection.h
+++ b/qpid/cpp/src/qpid/broker/Connection.h
@@ -86,7 +86,8 @@ class Connection : public sys::ConnectionInputHandler,
bool isLink = false,
uint64_t objectId = 0,
bool shadow=false,
- bool delayManagement = false);
+ bool delayManagement = false,
+ bool authenticated=true);
~Connection ();
@@ -113,15 +114,20 @@ class Connection : public sys::ConnectionInputHandler,
void requestIOProcessing (boost::function0<void>);
void recordFromServer (const framing::AMQFrame& frame);
void recordFromClient (const framing::AMQFrame& frame);
+
+ // gets for configured federation links
std::string getAuthMechanism();
std::string getAuthCredentials();
std::string getUsername();
std::string getPassword();
std::string getHost();
uint16_t getPort();
+
void notifyConnectionForced(const std::string& text);
void setUserId(const std::string& uid);
void raiseConnectEvent();
+
+ // credentials for connected client
const std::string& getUserId() const { return ConnectionState::getUserId(); }
const std::string& getMgmtId() const { return mgmtId; }
management::ManagementAgent* getAgent() const { return agent; }
@@ -144,7 +150,10 @@ class Connection : public sys::ConnectionInputHandler,
void setSecureConnection(SecureConnection* secured);
/** True if this is a shadow connection in a cluster. */
- bool isShadow() { return shadow; }
+ bool isShadow() const { return shadow; }
+
+ /** True if this connection is authenticated */
+ bool isAuthenticated() const { return authenticated; }
// Used by cluster to update connection status
sys::AggregateOutput& getOutputTasks() { return outputTasks; }
@@ -161,6 +170,7 @@ class Connection : public sys::ConnectionInputHandler,
bool isOpen();
bool isLink() { return link; }
+ void startLinkHeartbeatTimeoutTask();
// Used by cluster during catch-up, see cluster::OutputInterceptor
void doIoCallbacks();
@@ -174,6 +184,8 @@ class Connection : public sys::ConnectionInputHandler,
ChannelMap channels;
qpid::sys::SecuritySettings securitySettings;
+ bool shadow;
+ bool authenticated;
ConnectionHandler adapter;
const bool link;
bool mgmtClosing;
@@ -184,11 +196,10 @@ class Connection : public sys::ConnectionInputHandler,
LinkRegistry& links;
management::ManagementAgent* agent;
sys::Timer& timer;
- boost::intrusive_ptr<sys::TimerTask> heartbeatTimer;
+ boost::intrusive_ptr<sys::TimerTask> heartbeatTimer, linkHeartbeatTimer;
boost::intrusive_ptr<ConnectionTimeoutTask> timeoutTimer;
ErrorListener* errorListener;
uint64_t objectId;
- bool shadow;
framing::FieldTable clientProperties;
/**
diff --git a/qpid/cpp/src/qpid/broker/ConnectionFactory.cpp b/qpid/cpp/src/qpid/broker/ConnectionFactory.cpp
index 9e0020812b..d5d24ca629 100644
--- a/qpid/cpp/src/qpid/broker/ConnectionFactory.cpp
+++ b/qpid/cpp/src/qpid/broker/ConnectionFactory.cpp
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -40,11 +40,6 @@ ConnectionFactory::~ConnectionFactory() {}
sys::ConnectionCodec*
ConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, const std::string& id,
const SecuritySettings& external) {
- if (broker.getConnectionCounter().allowConnection())
- {
- QPID_LOG(error, "Client max connection count limit exceeded: " << broker.getOptions().maxConnections << " connection refused");
- return 0;
- }
if (v == ProtocolVersion(0, 10)) {
ConnectionPtr c(new amqp_0_10::Connection(out, id, false));
c->setInputHandler(InputPtr(new broker::Connection(c.get(), broker, id, external, false)));
@@ -62,5 +57,5 @@ ConnectionFactory::create(sys::OutputControl& out, const std::string& id,
return c.release();
}
-
+
}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/ConnectionHandler.cpp b/qpid/cpp/src/qpid/broker/ConnectionHandler.cpp
index f1d43c5cdb..a22972ddd2 100644
--- a/qpid/cpp/src/qpid/broker/ConnectionHandler.cpp
+++ b/qpid/cpp/src/qpid/broker/ConnectionHandler.cpp
@@ -28,6 +28,7 @@
#include "qpid/framing/AllInvoker.h"
#include "qpid/framing/ConnectionStartOkBody.h"
#include "qpid/framing/enum.h"
+#include "qpid/framing/FieldValue.h"
#include "qpid/log/Statement.h"
#include "qpid/sys/SecurityLayer.h"
#include "qpid/broker/AclModule.h"
@@ -35,6 +36,9 @@
using namespace qpid;
using namespace qpid::broker;
+
+using std::string;
+
using namespace qpid::framing;
using qpid::sys::SecurityLayer;
namespace _qmf = qmf::org::apache::qpid::broker;
@@ -102,9 +106,10 @@ void ConnectionHandler::setSecureConnection(SecureConnection* secured)
handler->secured = secured;
}
-ConnectionHandler::ConnectionHandler(Connection& connection, bool isClient, bool isShadow) : handler(new Handler(connection, isClient, isShadow)) {}
+ConnectionHandler::ConnectionHandler(Connection& connection, bool isClient) :
+ handler(new Handler(connection, isClient)) {}
-ConnectionHandler::Handler::Handler(Connection& c, bool isClient, bool isShadow) :
+ConnectionHandler::Handler::Handler(Connection& c, bool isClient) :
proxy(c.getOutput()),
connection(c), serverMode(!isClient), secured(0),
isOpen(false)
@@ -115,14 +120,13 @@ ConnectionHandler::Handler::Handler(Connection& c, bool isClient, bool isShadow)
properties.setString(QPID_FED_TAG, connection.getBroker().getFederationTag());
- authenticator = SaslAuthenticator::createAuthenticator(c, isShadow);
+ authenticator = SaslAuthenticator::createAuthenticator(c);
authenticator->getMechanisms(mechanisms);
Array locales(0x95);
boost::shared_ptr<FieldValue> l(new Str16Value(en_US));
locales.add(l);
proxy.start(properties, mechanisms, locales);
-
}
maxFrameSize = (64 * 1024) - 1;
@@ -317,7 +321,7 @@ void ConnectionHandler::Handler::start(const FieldTable& serverProperties,
connection.setFederationPeerTag(serverProperties.getAsString(QPID_FED_TAG));
}
- FieldTable ft;
+ FieldTable ft = connection.getBroker().getLinkClientProperties();
ft.setInt(QPID_FED_LINK,1);
ft.setString(QPID_FED_TAG, connection.getBroker().getFederationTag());
@@ -366,8 +370,14 @@ void ConnectionHandler::Handler::tune(uint16_t channelMax,
maxFrameSize = std::min(maxFrameSize, maxFrameSizeProposed);
connection.setFrameMax(maxFrameSize);
- connection.setHeartbeat(heartbeatMax);
- proxy.tuneOk(channelMax, maxFrameSize, heartbeatMax);
+ // this method is only ever called when this Connection
+ // is a federation link where this Broker is acting as
+ // a client to another Broker
+ uint16_t hb = std::min(connection.getBroker().getOptions().linkHeartbeatInterval, heartbeatMax);
+ connection.setHeartbeat(hb);
+ connection.startLinkHeartbeatTimeoutTask();
+
+ proxy.tuneOk(channelMax, maxFrameSize, hb);
proxy.open("/", Array(), true);
}
diff --git a/qpid/cpp/src/qpid/broker/ConnectionHandler.h b/qpid/cpp/src/qpid/broker/ConnectionHandler.h
index 2e25543308..9346e7b1ac 100644
--- a/qpid/cpp/src/qpid/broker/ConnectionHandler.h
+++ b/qpid/cpp/src/qpid/broker/ConnectionHandler.h
@@ -61,7 +61,7 @@ class ConnectionHandler : public framing::FrameHandler
SecureConnection* secured;
bool isOpen;
- Handler(Connection& connection, bool isClient, bool isShadow=false);
+ Handler(Connection& connection, bool isClient);
~Handler();
void startOk(const qpid::framing::ConnectionStartOkBody& body);
void startOk(const qpid::framing::FieldTable& clientProperties,
@@ -99,7 +99,7 @@ class ConnectionHandler : public framing::FrameHandler
bool handle(const qpid::framing::AMQMethodBody& method);
public:
- ConnectionHandler(Connection& connection, bool isClient, bool isShadow=false );
+ ConnectionHandler(Connection& connection, bool isClient );
void close(framing::connection::CloseCode code, const std::string& text);
void heartbeat();
void handle(framing::AMQFrame& frame);
diff --git a/qpid/cpp/src/qpid/broker/ConnectionObservers.h b/qpid/cpp/src/qpid/broker/ConnectionObservers.h
index 07e515f3c9..e9014c80c3 100644
--- a/qpid/cpp/src/qpid/broker/ConnectionObservers.h
+++ b/qpid/cpp/src/qpid/broker/ConnectionObservers.h
@@ -23,9 +23,7 @@
*/
#include "ConnectionObserver.h"
-#include "qpid/sys/Mutex.h"
-#include <set>
-#include <algorithm>
+#include "Observers.h"
namespace qpid {
namespace broker {
@@ -35,18 +33,10 @@ namespace broker {
* Calling a ConnectionObserver function will call that function on each observer.
* THREAD SAFE.
*/
-class ConnectionObservers : public ConnectionObserver {
+class ConnectionObservers : public ConnectionObserver,
+ public Observers<ConnectionObserver>
+{
public:
- void add(boost::shared_ptr<ConnectionObserver> observer) {
- sys::Mutex::ScopedLock l(lock);
- observers.insert(observer);
- }
-
- void remove(boost::shared_ptr<ConnectionObserver> observer) {
- sys::Mutex::ScopedLock l(lock);
- observers.erase(observer);
- }
-
void connection(Connection& c) {
each(boost::bind(&ConnectionObserver::connection, _1, boost::ref(c)));
}
@@ -62,16 +52,6 @@ class ConnectionObservers : public ConnectionObserver {
void forced(Connection& c, const std::string& text) {
each(boost::bind(&ConnectionObserver::forced, _1, boost::ref(c), text));
}
-
- private:
- typedef std::set<boost::shared_ptr<ConnectionObserver> > Observers;
- sys::Mutex lock;
- Observers observers;
-
- template <class F> void each(F f) {
- sys::Mutex::ScopedLock l(lock);
- std::for_each(observers.begin(), observers.end(), f);
- }
};
}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/Daemon.h b/qpid/cpp/src/qpid/broker/Daemon.h
index a9cd98bce2..2bb9fc5577 100644
--- a/qpid/cpp/src/qpid/broker/Daemon.h
+++ b/qpid/cpp/src/qpid/broker/Daemon.h
@@ -74,7 +74,6 @@ class Daemon : private boost::noncopyable
pid_t pid;
int pipeFds[2];
- int lockFileFd;
std::string lockFile;
std::string pidDir;
};
diff --git a/qpid/cpp/src/qpid/broker/DirectExchange.cpp b/qpid/cpp/src/qpid/broker/DirectExchange.cpp
index 5d9aea7509..2fa7ce0fc5 100644
--- a/qpid/cpp/src/qpid/broker/DirectExchange.cpp
+++ b/qpid/cpp/src/qpid/broker/DirectExchange.cpp
@@ -26,6 +26,9 @@
#include <iostream>
using namespace qpid::broker;
+
+using std::string;
+
using namespace qpid::framing;
using namespace qpid::sys;
using qpid::management::Manageable;
diff --git a/qpid/cpp/src/qpid/broker/DtxManager.cpp b/qpid/cpp/src/qpid/broker/DtxManager.cpp
index febd547478..d482c2c327 100644
--- a/qpid/cpp/src/qpid/broker/DtxManager.cpp
+++ b/qpid/cpp/src/qpid/broker/DtxManager.cpp
@@ -21,6 +21,7 @@
#include "qpid/broker/DtxManager.h"
#include "qpid/broker/DtxTimeout.h"
#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/StructHelper.h"
#include "qpid/log/Statement.h"
#include "qpid/sys/Timer.h"
#include "qpid/ptr_map.h"
@@ -55,7 +56,7 @@ void DtxManager::recover(const std::string& xid, std::auto_ptr<TPCTransactionCon
bool DtxManager::prepare(const std::string& xid)
{
- QPID_LOG(debug, "preparing: " << xid);
+ QPID_LOG(debug, "preparing: " << convert(xid));
try {
return getWork(xid)->prepare();
} catch (DtxTimeoutException& e) {
@@ -66,7 +67,7 @@ bool DtxManager::prepare(const std::string& xid)
bool DtxManager::commit(const std::string& xid, bool onePhase)
{
- QPID_LOG(debug, "committing: " << xid);
+ QPID_LOG(debug, "committing: " << convert(xid));
try {
bool result = getWork(xid)->commit(onePhase);
remove(xid);
@@ -79,7 +80,7 @@ bool DtxManager::commit(const std::string& xid, bool onePhase)
void DtxManager::rollback(const std::string& xid)
{
- QPID_LOG(debug, "rolling back: " << xid);
+ QPID_LOG(debug, "rolling back: " << convert(xid));
try {
getWork(xid)->rollback();
remove(xid);
@@ -94,7 +95,7 @@ DtxWorkRecord* DtxManager::getWork(const std::string& xid)
Mutex::ScopedLock locker(lock);
WorkMap::iterator i = work.find(xid);
if (i == work.end()) {
- throw NotFoundException(QPID_MSG("Unrecognised xid " << xid));
+ throw NotFoundException(QPID_MSG("Unrecognised xid " << convert(xid)));
}
return ptr_map_ptr(i);
}
@@ -109,7 +110,7 @@ void DtxManager::remove(const std::string& xid)
Mutex::ScopedLock locker(lock);
WorkMap::iterator i = work.find(xid);
if (i == work.end()) {
- throw NotFoundException(QPID_MSG("Unrecognised xid " << xid));
+ throw NotFoundException(QPID_MSG("Unrecognised xid " << convert(xid)));
} else {
work.erase(i);
}
@@ -120,7 +121,7 @@ DtxWorkRecord* DtxManager::createWork(const std::string& xid)
Mutex::ScopedLock locker(lock);
WorkMap::iterator i = work.find(xid);
if (i != work.end()) {
- throw NotAllowedException(QPID_MSG("Xid " << xid << " is already known (use 'join' to add work to an existing xid)"));
+ throw NotAllowedException(QPID_MSG("Xid " << convert(xid) << " is already known (use 'join' to add work to an existing xid)"));
} else {
std::string ncxid = xid; // Work around const correctness problems in ptr_map.
return ptr_map_ptr(work.insert(ncxid, new DtxWorkRecord(ncxid, store)).first);
@@ -175,3 +176,19 @@ void DtxManager::setStore (TransactionalStore* _store)
{
store = _store;
}
+
+std::string DtxManager::convert(const qpid::framing::Xid& xid)
+{
+ qpid::framing::StructHelper helper;
+ std::string encoded;
+ helper.encode(xid, encoded);
+ return encoded;
+}
+
+qpid::framing::Xid DtxManager::convert(const std::string& xid)
+{
+ qpid::framing::StructHelper helper;
+ qpid::framing::Xid decoded;
+ helper.decode(decoded, xid);
+ return decoded;
+}
diff --git a/qpid/cpp/src/qpid/broker/DtxManager.h b/qpid/cpp/src/qpid/broker/DtxManager.h
index 11895695a3..6f03189f66 100644
--- a/qpid/cpp/src/qpid/broker/DtxManager.h
+++ b/qpid/cpp/src/qpid/broker/DtxManager.h
@@ -26,6 +26,7 @@
#include "qpid/broker/DtxWorkRecord.h"
#include "qpid/broker/TransactionalStore.h"
#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/Xid.h"
#include "qpid/sys/Mutex.h"
#include "qpid/ptr_map.h"
@@ -74,6 +75,8 @@ public:
}
DtxWorkRecord* getWork(const std::string& xid);
bool exists(const std::string& xid);
+ static std::string convert(const framing::Xid& xid);
+ static framing::Xid convert(const std::string& xid);
};
}
diff --git a/qpid/cpp/src/qpid/broker/DtxWorkRecord.cpp b/qpid/cpp/src/qpid/broker/DtxWorkRecord.cpp
index a413fe418d..2c26fec49f 100644
--- a/qpid/cpp/src/qpid/broker/DtxWorkRecord.cpp
+++ b/qpid/cpp/src/qpid/broker/DtxWorkRecord.cpp
@@ -19,6 +19,7 @@
*
*/
#include "qpid/broker/DtxWorkRecord.h"
+#include "qpid/broker/DtxManager.h"
#include "qpid/framing/reply_exceptions.h"
#include <boost/format.hpp>
#include <boost/mem_fn.hpp>
@@ -73,7 +74,7 @@ bool DtxWorkRecord::commit(bool onePhase)
if (prepared) {
//already prepared i.e. 2pc
if (onePhase) {
- throw IllegalStateException(QPID_MSG("Branch with xid " << xid << " has been prepared, one-phase option not valid!"));
+ throw IllegalStateException(QPID_MSG("Branch with xid " << DtxManager::convert(xid) << " has been prepared, one-phase option not valid!"));
}
store->commit(*txn);
@@ -84,7 +85,7 @@ bool DtxWorkRecord::commit(bool onePhase)
} else {
//1pc commit optimisation, don't need a 2pc transaction context:
if (!onePhase) {
- throw IllegalStateException(QPID_MSG("Branch with xid " << xid << " has not been prepared, one-phase option required!"));
+ throw IllegalStateException(QPID_MSG("Branch with xid " << DtxManager::convert(xid) << " has not been prepared, one-phase option required!"));
}
std::auto_ptr<TransactionContext> localtxn = store->begin();
if (prepare(localtxn.get())) {
@@ -116,10 +117,10 @@ void DtxWorkRecord::add(DtxBuffer::shared_ptr ops)
{
Mutex::ScopedLock locker(lock);
if (expired) {
- throw DtxTimeoutException(QPID_MSG("Branch with xid " << xid << " has timed out."));
+ throw DtxTimeoutException(QPID_MSG("Branch with xid " << DtxManager::convert(xid) << " has timed out."));
}
if (completed) {
- throw CommandInvalidException(QPID_MSG("Branch with xid " << xid << " has been completed!"));
+ throw CommandInvalidException(QPID_MSG("Branch with xid " << DtxManager::convert(xid) << " has been completed!"));
}
work.push_back(ops);
}
@@ -133,7 +134,7 @@ bool DtxWorkRecord::check()
//iterate through all DtxBuffers and ensure they are all ended
for (Work::iterator i = work.begin(); i != work.end(); i++) {
if (!(*i)->isEnded()) {
- throw IllegalStateException(QPID_MSG("Branch with xid " << xid << " not completed!"));
+ throw IllegalStateException(QPID_MSG("Branch with xid " << DtxManager::convert(xid) << " not completed!"));
} else if ((*i)->isRollbackOnly()) {
rolledback = true;
}
diff --git a/qpid/cpp/src/qpid/broker/Exchange.cpp b/qpid/cpp/src/qpid/broker/Exchange.cpp
index f311b79578..82d4b4df15 100644
--- a/qpid/cpp/src/qpid/broker/Exchange.cpp
+++ b/qpid/cpp/src/qpid/broker/Exchange.cpp
@@ -32,7 +32,11 @@
#include "qpid/sys/ExceptionHolder.h"
#include <stdexcept>
-using namespace qpid::broker;
+namespace qpid {
+namespace broker {
+
+using std::string;
+
using namespace qpid::framing;
using qpid::framing::Buffer;
using qpid::framing::FieldTable;
@@ -165,7 +169,7 @@ void Exchange::routeIVE(){
Exchange::Exchange (const string& _name, Manageable* parent, Broker* b) :
- name(_name), durable(false), persistenceId(0), sequence(false),
+ name(_name), durable(false), alternateUsers(0), persistenceId(0), sequence(false),
sequenceNo(0), ive(false), mgmtExchange(0), brokerMgmtObject(0), broker(b), destroyed(false)
{
if (parent != 0 && broker != 0)
@@ -408,3 +412,6 @@ bool Exchange::routeWithAlternate(Deliverable& msg)
}
return msg.delivered;
}
+
+}}
+
diff --git a/qpid/cpp/src/qpid/broker/ExchangeRegistry.cpp b/qpid/cpp/src/qpid/broker/ExchangeRegistry.cpp
index fca77f7ddd..b31c7bd7b8 100644
--- a/qpid/cpp/src/qpid/broker/ExchangeRegistry.cpp
+++ b/qpid/cpp/src/qpid/broker/ExchangeRegistry.cpp
@@ -19,11 +19,13 @@
*
*/
+#include "qpid/broker/Broker.h"
#include "qpid/broker/ExchangeRegistry.h"
#include "qpid/broker/DirectExchange.h"
#include "qpid/broker/FanOutExchange.h"
#include "qpid/broker/HeadersExchange.h"
#include "qpid/broker/TopicExchange.h"
+#include "qpid/broker/Link.h"
#include "qpid/management/ManagementDirectExchange.h"
#include "qpid/management/ManagementTopicExchange.h"
#include "qpid/framing/reply_exceptions.h"
@@ -41,36 +43,42 @@ pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, c
pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, const string& type,
bool durable, const FieldTable& args){
- RWlock::ScopedWlock locker(lock);
- ExchangeMap::iterator i = exchanges.find(name);
- if (i == exchanges.end()) {
- Exchange::shared_ptr exchange;
-
- if (type == TopicExchange::typeName){
- exchange = Exchange::shared_ptr(new TopicExchange(name, durable, args, parent, broker));
- }else if(type == DirectExchange::typeName){
- exchange = Exchange::shared_ptr(new DirectExchange(name, durable, args, parent, broker));
- }else if(type == FanOutExchange::typeName){
- exchange = Exchange::shared_ptr(new FanOutExchange(name, durable, args, parent, broker));
- }else if (type == HeadersExchange::typeName) {
- exchange = Exchange::shared_ptr(new HeadersExchange(name, durable, args, parent, broker));
- }else if (type == ManagementDirectExchange::typeName) {
- exchange = Exchange::shared_ptr(new ManagementDirectExchange(name, durable, args, parent, broker));
- }else if (type == ManagementTopicExchange::typeName) {
- exchange = Exchange::shared_ptr(new ManagementTopicExchange(name, durable, args, parent, broker));
- }else{
- FunctionMap::iterator i = factory.find(type);
- if (i == factory.end()) {
- throw UnknownExchangeTypeException();
- } else {
- exchange = i->second(name, durable, args, parent, broker);
+ Exchange::shared_ptr exchange;
+ std::pair<Exchange::shared_ptr, bool> result;
+ {
+ RWlock::ScopedWlock locker(lock);
+ ExchangeMap::iterator i = exchanges.find(name);
+ if (i == exchanges.end()) {
+ if (type == TopicExchange::typeName){
+ exchange = Exchange::shared_ptr(new TopicExchange(name, durable, args, parent, broker));
+ }else if(type == DirectExchange::typeName){
+ exchange = Exchange::shared_ptr(new DirectExchange(name, durable, args, parent, broker));
+ }else if(type == FanOutExchange::typeName){
+ exchange = Exchange::shared_ptr(new FanOutExchange(name, durable, args, parent, broker));
+ }else if (type == HeadersExchange::typeName) {
+ exchange = Exchange::shared_ptr(new HeadersExchange(name, durable, args, parent, broker));
+ }else if (type == ManagementDirectExchange::typeName) {
+ exchange = Exchange::shared_ptr(new ManagementDirectExchange(name, durable, args, parent, broker));
+ }else if (type == ManagementTopicExchange::typeName) {
+ exchange = Exchange::shared_ptr(new ManagementTopicExchange(name, durable, args, parent, broker));
+ }else if (type == Link::exchangeTypeName) {
+ exchange = Link::linkExchangeFactory(name);
+ }else{
+ FunctionMap::iterator i = factory.find(type);
+ if (i == factory.end()) {
+ throw UnknownExchangeTypeException();
+ } else {
+ exchange = i->second(name, durable, args, parent, broker);
+ }
}
+ exchanges[name] = exchange;
+ result = std::pair<Exchange::shared_ptr, bool>(exchange, true);
+ } else {
+ result = std::pair<Exchange::shared_ptr, bool>(i->second, false);
}
- exchanges[name] = exchange;
- return std::pair<Exchange::shared_ptr, bool>(exchange, true);
- } else {
- return std::pair<Exchange::shared_ptr, bool>(i->second, false);
}
+ if (broker && exchange) broker->getConfigurationObservers().exchangeCreate(exchange);
+ return result;
}
void ExchangeRegistry::destroy(const string& name){
@@ -79,12 +87,17 @@ void ExchangeRegistry::destroy(const string& name){
(name == "amq.direct" || name == "amq.fanout" || name == "amq.topic" || name == "amq.match")) ||
name == "qpid.management")
throw framing::NotAllowedException(QPID_MSG("Cannot delete default exchange: '" << name << "'"));
- RWlock::ScopedWlock locker(lock);
- ExchangeMap::iterator i = exchanges.find(name);
- if (i != exchanges.end()) {
- i->second->destroy();
- exchanges.erase(i);
+ Exchange::shared_ptr exchange;
+ {
+ RWlock::ScopedWlock locker(lock);
+ ExchangeMap::iterator i = exchanges.find(name);
+ if (i != exchanges.end()) {
+ exchange = i->second;
+ i->second->destroy();
+ exchanges.erase(i);
+ }
}
+ if (broker && exchange) broker->getConfigurationObservers().exchangeDestroy(exchange);
}
Exchange::shared_ptr ExchangeRegistry::find(const string& name){
diff --git a/qpid/cpp/src/qpid/broker/Fairshare.cpp b/qpid/cpp/src/qpid/broker/Fairshare.cpp
index 313aa746f1..7cdad1a44f 100644
--- a/qpid/cpp/src/qpid/broker/Fairshare.cpp
+++ b/qpid/cpp/src/qpid/broker/Fairshare.cpp
@@ -21,6 +21,7 @@
#include "qpid/broker/Fairshare.h"
#include "qpid/broker/QueuedMessage.h"
#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/FieldValue.h"
#include "qpid/log/Statement.h"
#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
diff --git a/qpid/cpp/src/qpid/broker/FanOutExchange.cpp b/qpid/cpp/src/qpid/broker/FanOutExchange.cpp
index 2bce99b6fe..56c894c129 100644
--- a/qpid/cpp/src/qpid/broker/FanOutExchange.cpp
+++ b/qpid/cpp/src/qpid/broker/FanOutExchange.cpp
@@ -24,6 +24,9 @@
#include <algorithm>
using namespace qpid::broker;
+
+using std::string;
+
using namespace qpid::framing;
using namespace qpid::sys;
namespace _qmf = qmf::org::apache::qpid::broker;
diff --git a/qpid/cpp/src/qpid/broker/HeadersExchange.cpp b/qpid/cpp/src/qpid/broker/HeadersExchange.cpp
index 6648ae0422..9975d26c72 100644
--- a/qpid/cpp/src/qpid/broker/HeadersExchange.cpp
+++ b/qpid/cpp/src/qpid/broker/HeadersExchange.cpp
@@ -26,6 +26,9 @@
using namespace qpid::broker;
+
+using std::string;
+
using namespace qpid::framing;
using namespace qpid::sys;
namespace _qmf = qmf::org::apache::qpid::broker;
diff --git a/qpid/cpp/src/qpid/broker/LegacyLVQ.cpp b/qpid/cpp/src/qpid/broker/LegacyLVQ.cpp
index 49c0a32c19..f1deddf4c8 100644
--- a/qpid/cpp/src/qpid/broker/LegacyLVQ.cpp
+++ b/qpid/cpp/src/qpid/broker/LegacyLVQ.cpp
@@ -28,16 +28,26 @@ namespace broker {
LegacyLVQ::LegacyLVQ(const std::string& k, bool b, Broker* br) : MessageMap(k), noBrowse(b), broker(br) {}
void LegacyLVQ::setNoBrowse(bool b)
-{
+{
noBrowse = b;
}
+bool LegacyLVQ::deleted(const QueuedMessage& message)
+{
+ Ordering::iterator i = messages.find(message.position);
+ if (i != messages.end() && i->second.payload == message.payload) {
+ erase(i);
+ return true;
+ } else {
+ return false;
+ }
+}
bool LegacyLVQ::acquire(const framing::SequenceNumber& position, QueuedMessage& message)
{
Ordering::iterator i = messages.find(position);
- if (i != messages.end() && i->second.payload == message.payload) {
+ if (i != messages.end() && i->second.payload == message.payload && i->second.status == QueuedMessage::AVAILABLE) {
+ i->second.status = QueuedMessage::ACQUIRED;
message = i->second;
- erase(i);
return true;
} else {
return false;
@@ -66,12 +76,17 @@ bool LegacyLVQ::push(const QueuedMessage& added, QueuedMessage& removed)
}
const QueuedMessage& LegacyLVQ::replace(const QueuedMessage& original, const QueuedMessage& update)
-{
+{
//add the new message into the original position of the replaced message
Ordering::iterator i = messages.find(original.position);
- i->second = update;
- i->second.position = original.position;
- return i->second;
+ if (i != messages.end()) {
+ i->second = update;
+ i->second.position = original.position;
+ return i->second;
+ } else {
+ QPID_LOG(error, "Failed to replace message at " << original.position);
+ return update;
+ }
}
void LegacyLVQ::removeIf(Predicate p)
diff --git a/qpid/cpp/src/qpid/broker/LegacyLVQ.h b/qpid/cpp/src/qpid/broker/LegacyLVQ.h
index 695e51131d..9355069f37 100644
--- a/qpid/cpp/src/qpid/broker/LegacyLVQ.h
+++ b/qpid/cpp/src/qpid/broker/LegacyLVQ.h
@@ -40,6 +40,7 @@ class LegacyLVQ : public MessageMap
{
public:
LegacyLVQ(const std::string& key, bool noBrowse = false, Broker* broker = 0);
+ bool deleted(const QueuedMessage&);
bool acquire(const framing::SequenceNumber&, QueuedMessage&);
bool browse(const framing::SequenceNumber&, QueuedMessage&, bool);
bool push(const QueuedMessage& added, QueuedMessage& removed);
diff --git a/qpid/cpp/src/qpid/broker/Link.cpp b/qpid/cpp/src/qpid/broker/Link.cpp
index 56a90e7fb7..1be388b989 100644
--- a/qpid/cpp/src/qpid/broker/Link.cpp
+++ b/qpid/cpp/src/qpid/broker/Link.cpp
@@ -31,6 +31,8 @@
#include "qpid/framing/enum.h"
#include "qpid/framing/reply_exceptions.h"
#include "qpid/broker/AclModule.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/UrlArray.h"
namespace qpid {
namespace broker {
@@ -48,6 +50,13 @@ using std::stringstream;
using std::string;
namespace _qmf = ::qmf::org::apache::qpid::broker;
+
+namespace {
+ const std::string FAILOVER_EXCHANGE("amq.failover");
+ const std::string FAILOVER_HEADER_KEY("amq.failover");
+}
+
+
struct LinkTimerTask : public sys::TimerTask {
LinkTimerTask(Link& l, sys::Timer& t)
: TimerTask(int64_t(l.getBroker()->getOptions().linkMaintenanceInterval*
@@ -65,19 +74,73 @@ struct LinkTimerTask : public sys::TimerTask {
sys::Timer& timer;
};
-Link::Link(LinkRegistry* _links,
- MessageStore* _store,
+
+
+/** LinkExchange is used by the link to subscribe to the remote broker's amq.failover exchange.
+ */
+class LinkExchange : public broker::Exchange
+{
+public:
+ LinkExchange(const std::string& name) : Exchange(name), link(0) {}
+ ~LinkExchange() {};
+ std::string getType() const { return Link::exchangeTypeName; }
+
+ // Exchange methods - set up to prevent binding/unbinding etc from clients!
+ bool bind(boost::shared_ptr<broker::Queue>, const std::string&, const framing::FieldTable*) { return false; }
+ bool unbind(boost::shared_ptr<broker::Queue>, const std::string&, const framing::FieldTable*) { return false; }
+ bool isBound(boost::shared_ptr<broker::Queue>, const std::string* const, const framing::FieldTable* const) {return false;}
+
+ // Process messages sent from the remote's amq.failover exchange by extracting the failover URLs
+ // and saving them should the Link need to reconnect.
+ void route(broker::Deliverable& msg)
+ {
+ if (!link) return;
+ const framing::FieldTable* headers = msg.getMessage().getApplicationHeaders();
+ framing::Array addresses;
+ if (headers && headers->getArray(FAILOVER_HEADER_KEY, addresses)) {
+ // convert the Array of addresses to a single Url container for used with setUrl():
+ std::vector<Url> urlVec;
+ Url urls;
+ urlVec = urlArrayToVector(addresses);
+ for(size_t i = 0; i < urlVec.size(); ++i)
+ urls.insert(urls.end(), urlVec[i].begin(), urlVec[i].end());
+ QPID_LOG(debug, "Remote broker has provided these failover addresses= " << urls);
+ link->setUrl(urls);
+ }
+ }
+
+ void setLink(Link *_link)
+ {
+ assert(!link);
+ link = _link;
+ }
+
+private:
+ Link *link;
+};
+
+
+boost::shared_ptr<Exchange> Link::linkExchangeFactory( const std::string& _name )
+{
+ return Exchange::shared_ptr(new LinkExchange(_name));
+}
+
+Link::Link(const string& _name,
+ LinkRegistry* _links,
const string& _host,
uint16_t _port,
const string& _transport,
+ DestroyedListener l,
bool _durable,
const string& _authMechanism,
const string& _username,
const string& _password,
Broker* _broker,
- Manageable* parent)
- : links(_links), store(_store), host(_host), port(_port),
- transport(_transport),
+ Manageable* parent,
+ bool failover_)
+ : name(_name), links(_links),
+ configuredTransport(_transport), configuredHost(_host), configuredPort(_port),
+ host(_host), port(_port), transport(_transport),
durable(_durable),
authMechanism(_authMechanism), username(_username), password(_password),
persistenceId(0), mgmtObject(0), broker(_broker), state(0),
@@ -88,14 +151,20 @@ Link::Link(LinkRegistry* _links,
channelCounter(1),
connection(0),
agent(0),
- timerTask(new LinkTimerTask(*this, broker->getTimer()))
+ listener(l),
+ timerTask(new LinkTimerTask(*this, broker->getTimer())),
+ failover(failover_),
+ failoverChannel(0)
{
if (parent != 0 && broker != 0)
{
agent = broker->getManagementAgent();
if (agent != 0)
{
- mgmtObject = new _qmf::Link(agent, this, parent, _host, _port, _transport, _durable);
+ mgmtObject = new _qmf::Link(agent, this, parent, name, durable);
+ mgmtObject->set_host(host);
+ mgmtObject->set_port(port);
+ mgmtObject->set_transport(transport);
agent->addObject(mgmtObject, 0, durable);
}
}
@@ -106,15 +175,29 @@ Link::Link(LinkRegistry* _links,
startConnectionLH();
}
broker->getTimer().add(timerTask);
+
+ if (failover) {
+ stringstream exchangeName;
+ exchangeName << "qpid.link." << name;
+ std::pair<Exchange::shared_ptr, bool> rc =
+ broker->getExchanges().declare(exchangeName.str(), exchangeTypeName);
+ failoverExchange = boost::static_pointer_cast<LinkExchange>(rc.first);
+ assert(failoverExchange);
+ failoverExchange->setLink(this);
+ }
}
Link::~Link ()
{
- if (state == STATE_OPERATIONAL && connection != 0)
- connection->close(CLOSE_CODE_CONNECTION_FORCED, "closed by management");
+ if (state == STATE_OPERATIONAL && connection != 0) {
+ closeConnection("closed by management");
+ }
if (mgmtObject != 0)
mgmtObject->resourceDestroy ();
+
+ if (failover)
+ broker->getExchanges().destroy(failoverExchange->getName());
}
void Link::setStateLH (int newState)
@@ -172,6 +255,7 @@ void Link::established(Connection* c)
currentInterval = 1;
visitCount = 0;
connection = c;
+
if (closing)
destroy();
else // Process any IO tasks bridges added before established.
@@ -180,14 +264,34 @@ void Link::established(Connection* c)
void Link::setUrl(const Url& u) {
+ QPID_LOG(info, "Setting remote broker failover addresses for link '" << getName() << "' to these urls: " << u);
Mutex::ScopedLock mutex(lock);
url = u;
reconnectNext = 0;
}
+
+namespace {
+class DetachedCallback : public SessionHandler::ErrorListener {
+ public:
+ DetachedCallback(const Link& link) : name(link.getName()) {}
+ void connectionException(framing::connection::CloseCode, const std::string&) {}
+ void channelException(framing::session::DetachCode, const std::string&) {}
+ void executionException(framing::execution::ErrorCode, const std::string&) {}
+ void detach() {}
+ private:
+ const std::string name;
+};
+}
+
void Link::opened() {
Mutex::ScopedLock mutex(lock);
if (!connection) return;
+
+ if (!hideManagement() && connection->GetManagementObject()) {
+ mgmtObject->set_connectionRef(connection->GetManagementObject()->getObjectId());
+ }
+
// Get default URL from known-hosts if not already set
if (url.empty()) {
const std::vector<Url>& known = connection->getKnownHosts();
@@ -198,6 +302,45 @@ void Link::opened() {
reconnectNext = 0;
QPID_LOG(debug, "Known hosts for peer of inter-broker link: " << url);
}
+
+ if (failover) {
+ //
+ // attempt to subscribe to failover exchange for updates from remote
+ //
+
+ const std::string queueName = "qpid.link." + framing::Uuid(true).str();
+ failoverChannel = nextChannel();
+
+ SessionHandler& sessionHandler = connection->getChannel(failoverChannel);
+ sessionHandler.setErrorListener(
+ boost::shared_ptr<SessionHandler::ErrorListener>(new DetachedCallback(*this)));
+ failoverSession = queueName;
+ sessionHandler.attachAs(failoverSession);
+
+ framing::AMQP_ServerProxy remoteBroker(sessionHandler.out);
+
+ remoteBroker.getQueue().declare(queueName,
+ "", // alt-exchange
+ false, // passive
+ false, // durable
+ true, // exclusive
+ true, // auto-delete
+ FieldTable());
+ remoteBroker.getExchange().bind(queueName,
+ FAILOVER_EXCHANGE,
+ "", // no key
+ FieldTable());
+ remoteBroker.getMessage().subscribe(queueName,
+ failoverExchange->getName(),
+ 1, // implied-accept mode
+ 0, // pre-acquire mode
+ false, // exclusive
+ "", // resume-id
+ 0, // resume-ttl
+ FieldTable());
+ remoteBroker.getMessage().flow(failoverExchange->getName(), 0, 0xFFFFFFFF);
+ remoteBroker.getMessage().flow(failoverExchange->getName(), 1, 0xFFFFFFFF);
+ }
}
void Link::closed(int, std::string text)
@@ -206,11 +349,14 @@ void Link::closed(int, std::string text)
QPID_LOG (info, "Inter-broker link disconnected from " << host << ":" << port << " " << text);
connection = 0;
- if (state == STATE_OPERATIONAL) {
- stringstream addr;
- addr << host << ":" << port;
- if (!hideManagement() && agent)
+
+ if (!hideManagement()) {
+ mgmtObject->set_connectionRef(qpid::management::ObjectId());
+ if (state == STATE_OPERATIONAL && agent) {
+ stringstream addr;
+ addr << host << ":" << port;
agent->raiseEvent(_qmf::EventBrokerLinkDown(addr.str()));
+ }
}
for (Bridges::iterator i = active.begin(); i != active.end(); i++) {
@@ -225,22 +371,19 @@ void Link::closed(int, std::string text)
if (!hideManagement())
mgmtObject->set_lastError (text);
}
-
- if (closing)
- destroy();
}
-// Called in connection IO thread.
+// Called in connection IO thread, cleans up the connection before destroying Link
void Link::destroy ()
{
Bridges toDelete;
+
+ timerTask->cancel(); // call prior to locking so maintenance visit can finish
{
Mutex::ScopedLock mutex(lock);
- QPID_LOG (info, "Inter-broker link to " << host << ":" << port << " removed by management");
- if (connection)
- connection->close(CLOSE_CODE_CONNECTION_FORCED, "closed by management");
- connection = 0;
+ QPID_LOG (info, "Inter-broker link to " << configuredHost << ":" << configuredPort << " removed by management");
+ closeConnection("closed by management");
setStateLH(STATE_CLOSED);
// Move the bridges to be deleted into a local vector so there is no
@@ -254,14 +397,13 @@ void Link::destroy ()
for (Bridges::iterator i = created.begin(); i != created.end(); i++)
toDelete.push_back(*i);
created.clear();
-
- timerTask->cancel();
}
+
// Now delete all bridges on this link (don't hold the lock for this).
for (Bridges::iterator i = toDelete.begin(); i != toDelete.end(); i++)
- (*i)->destroy();
+ (*i)->close();
toDelete.clear();
- links->destroy (host, port);
+ listener(this); // notify LinkRegistry that this Link has been destroyed
}
void Link::add(Bridge::shared_ptr bridge)
@@ -303,13 +445,13 @@ void Link::ioThreadProcessing()
{
Mutex::ScopedLock mutex(lock);
- if (state != STATE_OPERATIONAL)
+ if (state != STATE_OPERATIONAL || closing)
return;
// check for bridge session errors and recover
if (!active.empty()) {
Bridges::iterator removed = std::remove_if(
- active.begin(), active.end(), !boost::bind(&Bridge::isSessionReady, _1));
+ active.begin(), active.end(), boost::bind(&Bridge::isDetached, _1));
for (Bridges::iterator i = removed; i != active.end(); ++i) {
Bridge::shared_ptr bridge = *i;
bridge->closed();
@@ -340,7 +482,7 @@ void Link::ioThreadProcessing()
void Link::maintenanceVisit ()
{
Mutex::ScopedLock mutex(lock);
-
+ if (closing) return;
if (state == STATE_WAITING)
{
visitCount++;
@@ -358,19 +500,23 @@ void Link::maintenanceVisit ()
}
else if (state == STATE_OPERATIONAL && (!active.empty() || !created.empty() || !cancellations.empty()) && connection != 0)
connection->requestIOProcessing (boost::bind(&Link::ioThreadProcessing, this));
- }
+}
void Link::reconnectLH(const Address& a)
{
host = a.host;
port = a.port;
transport = a.protocol;
- startConnectionLH();
+
if (!hideManagement()) {
stringstream errorString;
- errorString << "Failed over to " << a;
+ errorString << "Failing over to " << a;
mgmtObject->set_lastError(errorString.str());
+ mgmtObject->set_host(host);
+ mgmtObject->set_port(port);
+ mgmtObject->set_transport(transport);
}
+ startConnectionLH();
}
bool Link::tryFailoverLH() {
@@ -379,15 +525,14 @@ bool Link::tryFailoverLH() {
if (url.empty()) return false;
Address next = url[reconnectNext++];
if (next.host != host || next.port != port || next.protocol != transport) {
- links->changeAddress(Address(transport, host, port), next);
- QPID_LOG(debug, "Inter-broker link failing over to " << next.host << ":" << next.port);
+ QPID_LOG(notice, "Inter-broker link '" << name << "' failing over to " << next);
reconnectLH(next);
return true;
}
return false;
}
-// Management updates for a linke are inconsistent in a cluster, so they are
+// Management updates for a link are inconsistent in a cluster, so they are
// suppressed.
bool Link::hideManagement() const {
return !mgmtObject || ( broker && broker->isInCluster());
@@ -396,7 +541,8 @@ bool Link::hideManagement() const {
uint Link::nextChannel()
{
Mutex::ScopedLock mutex(lock);
-
+ if (channelCounter >= framing::CHANNEL_MAX)
+ channelCounter = 1;
return channelCounter++;
}
@@ -415,18 +561,34 @@ void Link::setPersistenceId(uint64_t id) const
const string& Link::getName() const
{
- return host;
+ return name;
+}
+
+const std::string Link::ENCODED_IDENTIFIER("link.v2");
+const std::string Link::ENCODED_IDENTIFIER_V1("link");
+
+bool Link::isEncodedLink(const std::string& key)
+{
+ return key == ENCODED_IDENTIFIER || key == ENCODED_IDENTIFIER_V1;
}
Link::shared_ptr Link::decode(LinkRegistry& links, Buffer& buffer)
{
+ string kind;
+ buffer.getShortString(kind);
+
string host;
uint16_t port;
string transport;
string authMechanism;
string username;
string password;
+ string name;
+ if (kind == ENCODED_IDENTIFIER) {
+ // newer version provides a link name.
+ buffer.getShortString(name);
+ }
buffer.getShortString(host);
port = buffer.getShort();
buffer.getShortString(transport);
@@ -435,15 +597,24 @@ Link::shared_ptr Link::decode(LinkRegistry& links, Buffer& buffer)
buffer.getShortString(username);
buffer.getShortString(password);
- return links.declare(host, port, transport, durable, authMechanism, username, password).first;
+ if (kind == ENCODED_IDENTIFIER_V1) {
+ /** previous versions identified the Link by host:port, there was no name
+ * assigned. So create a name for the new Link.
+ */
+ name = createName(transport, host, port);
+ }
+
+ return links.declare(name, host, port, transport, durable, authMechanism,
+ username, password).first;
}
void Link::encode(Buffer& buffer) const
{
- buffer.putShortString(string("link"));
- buffer.putShortString(host);
- buffer.putShort(port);
- buffer.putShortString(transport);
+ buffer.putShortString(ENCODED_IDENTIFIER);
+ buffer.putShortString(name);
+ buffer.putShortString(configuredHost);
+ buffer.putShort(configuredPort);
+ buffer.putShortString(configuredTransport);
buffer.putOctet(durable ? 1 : 0);
buffer.putShortString(authMechanism);
buffer.putShortString(username);
@@ -452,10 +623,11 @@ void Link::encode(Buffer& buffer) const
uint32_t Link::encodedSize() const
{
- return host.size() + 1 // short-string (host)
- + 5 // short-string ("link")
+ return ENCODED_IDENTIFIER.size() + 1 // +1 byte length
+ + name.size() + 1
+ + configuredHost.size() + 1 // short-string (host)
+ 2 // port
- + transport.size() + 1 // short-string(transport)
+ + configuredTransport.size() + 1 // short-string(transport)
+ 1 // durable
+ authMechanism.size() + 1
+ username.size() + 1
@@ -468,6 +640,7 @@ ManagementObject* Link::GetManagementObject (void) const
}
void Link::close() {
+ QPID_LOG(debug, "Link::close(), link=" << name );
Mutex::ScopedLock mutex(lock);
if (!closing) {
closing = true;
@@ -488,36 +661,31 @@ Manageable::status_t Link::ManagementMethod (uint32_t op, Args& args, string& te
return Manageable::STATUS_OK;
case _qmf::Link::METHOD_BRIDGE :
+ /* TBD: deprecate this interface in favor of the Broker::create() method. The
+ * Broker::create() method allows the user to assign a name to the bridge.
+ */
+ QPID_LOG(info, "The Link::bridge() method will be removed in a future release of QPID."
+ " Please use the Broker::create() method with type='bridge' instead.");
_qmf::ArgsLinkBridge& iargs = (_qmf::ArgsLinkBridge&) args;
- QPID_LOG(debug, "Link::bridge() request received");
-
- // Durable bridges are only valid on durable links
- if (iargs.i_durable && !durable) {
- text = "Can't create a durable route on a non-durable link";
- return Manageable::STATUS_USER;
- }
-
- if (iargs.i_dynamic) {
- Exchange::shared_ptr exchange = getBroker()->getExchanges().get(iargs.i_src);
- if (exchange.get() == 0) {
- text = "Exchange not found";
- return Manageable::STATUS_USER;
- }
- if (!exchange->supportsDynamicBinding()) {
- text = "Exchange type does not support dynamic routing";
- return Manageable::STATUS_USER;
+ QPID_LOG(debug, "Link::bridge() request received; src=" << iargs.i_src <<
+ "; dest=" << iargs.i_dest << "; key=" << iargs.i_key);
+
+ // Does a bridge already exist that has the src/dest/key? If so, re-use the
+ // existing bridge - this behavior is backward compatible with previous releases.
+ Bridge::shared_ptr bridge = links->getBridge(*this, iargs.i_src, iargs.i_dest, iargs.i_key);
+ if (!bridge) {
+ // need to create a new bridge on this link.
+ std::pair<Bridge::shared_ptr, bool> rc =
+ links->declare( Bridge::createName(name, iargs.i_src, iargs.i_dest, iargs.i_key),
+ *this, iargs.i_durable,
+ iargs.i_src, iargs.i_dest, iargs.i_key, iargs.i_srcIsQueue,
+ iargs.i_srcIsLocal, iargs.i_tag, iargs.i_excludes,
+ iargs.i_dynamic, iargs.i_sync);
+ if (!rc.first) {
+ text = "invalid parameters";
+ return Manageable::STATUS_PARAMETER_INVALID;
}
}
-
- std::pair<Bridge::shared_ptr, bool> result =
- links->declare (host, port, iargs.i_durable, iargs.i_src,
- iargs.i_dest, iargs.i_key, iargs.i_srcIsQueue,
- iargs.i_srcIsLocal, iargs.i_tag, iargs.i_excludes,
- iargs.i_dynamic, iargs.i_sync);
-
- if (result.second && iargs.i_durable)
- store->create(*result.first);
-
return Manageable::STATUS_OK;
}
@@ -539,4 +707,82 @@ void Link::setPassive(bool passive)
}
}
+
+/** utility to clean up connection resources correctly */
+void Link::closeConnection( const std::string& reason)
+{
+ if (connection != 0) {
+ // cancel our subscription to the failover exchange
+ if (failover) {
+ SessionHandler& sessionHandler = connection->getChannel(failoverChannel);
+ if (sessionHandler.getSession()) {
+ framing::AMQP_ServerProxy remoteBroker(sessionHandler.out);
+ remoteBroker.getMessage().cancel(failoverExchange->getName());
+ remoteBroker.getSession().detach(failoverSession);
+ }
+ }
+ connection->close(CLOSE_CODE_CONNECTION_FORCED, reason);
+ connection = 0;
+ }
+}
+
+/** returns the current remote's address, and connection state */
+bool Link::getRemoteAddress(qpid::Address& addr) const
+{
+ addr.protocol = transport;
+ addr.host = host;
+ addr.port = port;
+
+ return state == STATE_OPERATIONAL;
+}
+
+
+// FieldTable keys for internal state data
+namespace {
+ const std::string FAILOVER_ADDRESSES("failover-addresses");
+ const std::string FAILOVER_INDEX("failover-index");
+}
+
+void Link::getState(framing::FieldTable& state) const
+{
+ state.clear();
+ Mutex::ScopedLock mutex(lock);
+ if (!url.empty()) {
+ state.setString(FAILOVER_ADDRESSES, url.str());
+ state.setInt(FAILOVER_INDEX, reconnectNext);
+ }
+}
+
+void Link::setState(const framing::FieldTable& state)
+{
+ Mutex::ScopedLock mutex(lock);
+ if (state.isSet(FAILOVER_ADDRESSES)) {
+ Url failovers(state.getAsString(FAILOVER_ADDRESSES));
+ setUrl(failovers);
+ }
+ if (state.isSet(FAILOVER_INDEX)) {
+ reconnectNext = state.getAsInt(FAILOVER_INDEX);
+ }
+}
+
+std::string Link::createName(const std::string& transport,
+ const std::string& host,
+ uint16_t port)
+{
+ stringstream linkName;
+ linkName << QPID_NAME_PREFIX << transport << std::string(":")
+ << host << std::string(":") << port;
+ return linkName.str();
+}
+
+
+bool Link::pendingConnection(const std::string& _host, uint16_t _port) const
+{
+ Mutex::ScopedLock mutex(lock);
+ return (isConnecting() && _port == port && _host == host);
+}
+
+
+const std::string Link::exchangeTypeName("qpid.LinkExchange");
+
}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/Link.h b/qpid/cpp/src/qpid/broker/Link.h
index c7c8209db3..c92b368b0e 100644
--- a/qpid/cpp/src/qpid/broker/Link.h
+++ b/qpid/cpp/src/qpid/broker/Link.h
@@ -25,7 +25,6 @@
#include <boost/shared_ptr.hpp>
#include "qpid/Url.h"
#include "qpid/broker/BrokerImportExport.h"
-#include "qpid/broker/MessageStore.h"
#include "qpid/broker/PersistableConfig.h"
#include "qpid/broker/Bridge.h"
#include "qpid/broker/BrokerImportExport.h"
@@ -47,16 +46,25 @@ namespace broker {
class LinkRegistry;
class Broker;
class Connection;
+class LinkExchange;
class Link : public PersistableConfig, public management::Manageable {
private:
- sys::Mutex lock;
+ mutable sys::Mutex lock;
+ const std::string name;
LinkRegistry* links;
- MessageStore* store;
- std::string host;
- uint16_t port;
- std::string transport;
- bool durable;
+
+ // these remain constant across failover - used to identify this link
+ const std::string configuredTransport;
+ const std::string configuredHost;
+ const uint16_t configuredPort;
+ // these reflect the current address of remote - will change during failover
+ std::string host;
+ uint16_t port;
+ std::string transport;
+
+ bool durable;
+
std::string authMechanism;
std::string username;
std::string password;
@@ -77,8 +85,12 @@ class Link : public PersistableConfig, public management::Manageable {
uint channelCounter;
Connection* connection;
management::ManagementAgent* agent;
-
+ boost::function<void(Link*)> listener;
boost::intrusive_ptr<sys::TimerTask> timerTask;
+ boost::shared_ptr<broker::LinkExchange> failoverExchange; // subscribed to remote's amq.failover exchange
+ bool failover; // Do we subscribe to a failover exchange?
+ uint failoverChannel;
+ std::string failoverSession;
static const int STATE_WAITING = 1;
static const int STATE_CONNECTING = 2;
@@ -91,37 +103,51 @@ class Link : public PersistableConfig, public management::Manageable {
void setStateLH (int newState);
void startConnectionLH(); // Start the IO Connection
- void destroy(); // Called when mgmt deletes this link
+ void destroy(); // Cleanup connection before link goes away
void ioThreadProcessing(); // Called on connection's IO thread by request
bool tryFailoverLH(); // Called during maintenance visit
bool hideManagement() const;
+ void reconnectLH(const Address&); //called by LinkRegistry
- void established(Connection*); // Called when connection is create
+ // connection management (called by LinkRegistry)
+ void established(Connection*); // Called when connection is created
void opened(); // Called when connection is open (after create)
void closed(int, std::string); // Called when connection goes away
- void reconnectLH(const Address&); //called by LinkRegistry
+ void notifyConnectionForced(const std::string text);
+ void closeConnection(const std::string& reason);
+ bool pendingConnection(const std::string& host, uint16_t port) const; // is Link trying to connect to this remote?
- friend class LinkRegistry; // to call established, opened, closed
+ friend class LinkRegistry; // to call established, opened, closed
public:
typedef boost::shared_ptr<Link> shared_ptr;
+ typedef boost::function<void(Link*)> DestroyedListener;
- Link(LinkRegistry* links,
- MessageStore* store,
+ Link(const std::string& name,
+ LinkRegistry* links,
const std::string& host,
uint16_t port,
const std::string& transport,
+ DestroyedListener l,
bool durable,
const std::string& authMechanism,
const std::string& username,
const std::string& password,
Broker* broker,
- management::Manageable* parent = 0);
+ management::Manageable* parent = 0,
+ bool failover=true);
virtual ~Link();
- std::string getHost() { return host; }
- uint16_t getPort() { return port; }
- std::string getTransport() { return transport; }
+ /** these return the *configured* transport/host/port, which does not change over the
+ lifetime of the Link */
+ std::string getHost() const { return configuredHost; }
+ uint16_t getPort() const { return configuredPort; }
+ std::string getTransport() const { return configuredTransport; }
+
+ /** returns the current address of the remote, which may be different from the
+ configured transport/host/port due to failover. Returns true if connection is
+ active */
+ QPID_BROKER_EXTERN bool getRemoteAddress(qpid::Address& addr) const;
bool isDurable() { return durable; }
void maintenanceVisit ();
@@ -130,15 +156,17 @@ class Link : public PersistableConfig, public management::Manageable {
void cancel(Bridge::shared_ptr);
QPID_BROKER_EXTERN void setUrl(const Url&); // Set URL for reconnection.
- QPID_BROKER_EXTERN void close(); // Close the link from within the broker.
+
+ // Close the link.
+ QPID_BROKER_EXTERN void close();
std::string getAuthMechanism() { return authMechanism; }
std::string getUsername() { return username; }
std::string getPassword() { return password; }
Broker* getBroker() { return broker; }
- void notifyConnectionForced(const std::string text);
void setPassive(bool p);
+ bool isConnecting() const { return state == STATE_CONNECTING; }
// PersistableConfig:
void setPersistenceId(uint64_t id) const;
@@ -147,12 +175,27 @@ class Link : public PersistableConfig, public management::Manageable {
void encode(framing::Buffer& buffer) const;
const std::string& getName() const;
+ static const std::string ENCODED_IDENTIFIER;
+ static const std::string ENCODED_IDENTIFIER_V1;
static Link::shared_ptr decode(LinkRegistry& links, framing::Buffer& buffer);
+ static bool isEncodedLink(const std::string& key);
// Manageable entry points
management::ManagementObject* GetManagementObject(void) const;
management::Manageable::status_t ManagementMethod(uint32_t, management::Args&, std::string&);
+ // manage the exchange owned by this link
+ static const std::string exchangeTypeName;
+ static boost::shared_ptr<Exchange> linkExchangeFactory(const std::string& name);
+
+ // replicate internal state of this Link for clustering
+ void getState(framing::FieldTable& state) const;
+ void setState(const framing::FieldTable& state);
+
+ /** create a name for a link (if none supplied by user config) */
+ static std::string createName(const std::string& transport,
+ const std::string& host,
+ uint16_t port);
};
}
}
diff --git a/qpid/cpp/src/qpid/broker/LinkRegistry.cpp b/qpid/cpp/src/qpid/broker/LinkRegistry.cpp
index c6c5a1ac05..75c311c917 100644
--- a/qpid/cpp/src/qpid/broker/LinkRegistry.cpp
+++ b/qpid/cpp/src/qpid/broker/LinkRegistry.cpp
@@ -68,54 +68,92 @@ LinkRegistry::LinkRegistry (Broker* _broker) :
LinkRegistry::~LinkRegistry() {}
+/** find link by the *configured* remote address */
+boost::shared_ptr<Link> LinkRegistry::getLink(const std::string& host,
+ uint16_t port,
+ const std::string& transport)
+{
+ Mutex::ScopedLock locker(lock);
+ for (LinkMap::iterator i = links.begin(); i != links.end(); ++i) {
+ Link::shared_ptr& link = i->second;
+ if (link->getHost() == host &&
+ link->getPort() == port &&
+ (transport.empty() || link->getTransport() == transport))
+ return link;
+ }
+ return boost::shared_ptr<Link>();
+}
-void LinkRegistry::changeAddress(const qpid::Address& oldAddress, const qpid::Address& newAddress)
+/** find link by name */
+boost::shared_ptr<Link> LinkRegistry::getLink(const std::string& name)
{
Mutex::ScopedLock locker(lock);
- std::string oldKey = createKey(oldAddress);
- std::string newKey = createKey(newAddress);
- if (links.find(newKey) != links.end()) {
- QPID_LOG(error, "Attempted to update key from " << oldKey << " to " << newKey << " which is already in use");
- } else {
- LinkMap::iterator i = links.find(oldKey);
- if (i == links.end()) {
- QPID_LOG(error, "Attempted to update key from " << oldKey << " which does not exist, to " << newKey);
- } else {
- links[newKey] = i->second;
- links.erase(oldKey);
- QPID_LOG(info, "Updated link key from " << oldKey << " to " << newKey);
- }
- }
+ LinkMap::iterator l = links.find(name);
+ if (l != links.end())
+ return l->second;
+ return boost::shared_ptr<Link>();
}
-pair<Link::shared_ptr, bool> LinkRegistry::declare(const string& host,
+pair<Link::shared_ptr, bool> LinkRegistry::declare(const string& name,
+ const string& host,
uint16_t port,
const string& transport,
bool durable,
const string& authMechanism,
const string& username,
- const string& password)
+ const string& password,
+ bool failover)
{
Mutex::ScopedLock locker(lock);
- string key = createKey(host, port);
- LinkMap::iterator i = links.find(key);
+ LinkMap::iterator i = links.find(name);
if (i == links.end())
{
Link::shared_ptr link;
- link = Link::shared_ptr (new Link (this, store, host, port, transport, durable,
- authMechanism, username, password,
- broker, parent));
- links[key] = link;
+ link = Link::shared_ptr (
+ new Link (name, this, host, port, transport,
+ boost::bind(&LinkRegistry::linkDestroyed, this, _1),
+ durable, authMechanism, username, password, broker,
+ parent, failover));
+ if (durable && store) store->create(*link);
+ links[name] = link;
+ QPID_LOG(debug, "Creating new link; name=" << name );
return std::pair<Link::shared_ptr, bool>(link, true);
}
return std::pair<Link::shared_ptr, bool>(i->second, false);
}
-pair<Bridge::shared_ptr, bool> LinkRegistry::declare(const std::string& host,
- uint16_t port,
+/** find bridge by link & route info */
+Bridge::shared_ptr LinkRegistry::getBridge(const Link& link,
+ const std::string& src,
+ const std::string& dest,
+ const std::string& key)
+{
+ Mutex::ScopedLock locker(lock);
+ for (BridgeMap::iterator i = bridges.begin(); i != bridges.end(); ++i) {
+ if (i->second->getSrc() == src && i->second->getDest() == dest &&
+ i->second->getKey() == key && i->second->getLink() &&
+ i->second->getLink()->getName() == link.getName()) {
+ return i->second;
+ }
+ }
+ return Bridge::shared_ptr();
+}
+
+/** find bridge by name */
+Bridge::shared_ptr LinkRegistry::getBridge(const std::string& name)
+{
+ Mutex::ScopedLock locker(lock);
+ BridgeMap::iterator b = bridges.find(name);
+ if (b != bridges.end())
+ return b->second;
+ return Bridge::shared_ptr();
+}
+
+pair<Bridge::shared_ptr, bool> LinkRegistry::declare(const std::string& name,
+ Link& link,
bool durable,
const std::string& src,
const std::string& dest,
@@ -126,22 +164,32 @@ pair<Bridge::shared_ptr, bool> LinkRegistry::declare(const std::string& host,
const std::string& excludes,
bool dynamic,
uint16_t sync,
- Bridge::InitializeCallback init
+ Bridge::InitializeCallback init,
+ const std::string& queueName,
+ const std::string& altExchange
)
{
Mutex::ScopedLock locker(lock);
- QPID_LOG(debug, "Bridge declared " << host << ": " << port << " from " << src << " to " << dest << " (" << key << ")");
- string linkKey = createKey(host, port);
- stringstream keystream;
- keystream << linkKey << "!" << src << "!" << dest << "!" << key;
- string bridgeKey = keystream.str();
+ // Durable bridges are only valid on durable links
+ if (durable && !link.isDurable()) {
+ QPID_LOG(error, "Can't create a durable route '" << name << "' on a non-durable link '" << link.getName());
+ return pair<Bridge::shared_ptr, bool>(Bridge::shared_ptr(), false);
+ }
- LinkMap::iterator l = links.find(linkKey);
- if (l == links.end())
- return pair<Bridge::shared_ptr, bool>(Bridge::shared_ptr(), false);
+ if (dynamic) {
+ Exchange::shared_ptr exchange = broker->getExchanges().get(src);
+ if (exchange.get() == 0) {
+ QPID_LOG(error, "Exchange not found, name='" << src << "'" );
+ return pair<Bridge::shared_ptr, bool>(Bridge::shared_ptr(), false);
+ }
+ if (!exchange->supportsDynamicBinding()) {
+ QPID_LOG(error, "Exchange type does not support dynamic routing, name='" << src << "'");
+ return pair<Bridge::shared_ptr, bool>(Bridge::shared_ptr(), false);
+ }
+ }
- BridgeMap::iterator b = bridges.find(bridgeKey);
+ BridgeMap::iterator b = bridges.find(name);
if (b == bridges.end())
{
_qmf::ArgsLinkBridge args;
@@ -159,23 +207,29 @@ pair<Bridge::shared_ptr, bool> LinkRegistry::declare(const std::string& host,
args.i_sync = sync;
bridge = Bridge::shared_ptr
- (new Bridge (l->second.get(), l->second->nextChannel(),
- boost::bind(&LinkRegistry::destroy, this,
- host, port, src, dest, key),
- args, init));
- bridges[bridgeKey] = bridge;
- l->second->add(bridge);
+ (new Bridge (name, &link, link.nextChannel(),
+ boost::bind(&LinkRegistry::destroyBridge, this, _1),
+ args, init, queueName, altExchange));
+ bridges[name] = bridge;
+ link.add(bridge);
+ if (durable && store)
+ store->create(*bridge);
+
+ QPID_LOG(debug, "Bridge '" << name <<"' declared on link '" << link.getName() <<
+ "' from " << src << " to " << dest << " (" << key << ")");
+
return std::pair<Bridge::shared_ptr, bool>(bridge, true);
}
return std::pair<Bridge::shared_ptr, bool>(b->second, false);
}
-void LinkRegistry::destroy(const string& host, const uint16_t port)
+/** called back by the link when it has completed its cleanup and can be removed. */
+void LinkRegistry::linkDestroyed(Link *link)
{
+ QPID_LOG(debug, "LinkRegistry::destroy(); link= " << link->getName());
Mutex::ScopedLock locker(lock);
- string key = createKey(host, port);
- LinkMap::iterator i = links.find(key);
+ LinkMap::iterator i = links.find(link->getName());
if (i != links.end())
{
if (i->second->isDurable() && store)
@@ -184,27 +238,20 @@ void LinkRegistry::destroy(const string& host, const uint16_t port)
}
}
-void LinkRegistry::destroy(const std::string& host,
- const uint16_t port,
- const std::string& src,
- const std::string& dest,
- const std::string& key)
+/** called back by bridge when its destruction has been requested */
+void LinkRegistry::destroyBridge(Bridge *bridge)
{
+ QPID_LOG(debug, "LinkRegistry::destroy(); bridge= " << bridge->getName());
Mutex::ScopedLock locker(lock);
- string linkKey = createKey(host, port);
- stringstream keystream;
- keystream << linkKey << "!" << src << "!" << dest << "!" << key;
- string bridgeKey = keystream.str();
- LinkMap::iterator l = links.find(linkKey);
- if (l == links.end())
- return;
-
- BridgeMap::iterator b = bridges.find(bridgeKey);
+ BridgeMap::iterator b = bridges.find(bridge->getName());
if (b == bridges.end())
return;
- l->second->cancel(b->second);
+ Link *link = b->second->getLink();
+ if (link) {
+ link->cancel(b->second);
+ }
if (b->second->isDurable())
store->destroy(*(b->second));
bridges.erase(b);
@@ -219,28 +266,73 @@ MessageStore* LinkRegistry::getStore() const {
return store;
}
-Link::shared_ptr LinkRegistry::findLink(const std::string& keyOrMgmtId)
-{
- // Convert keyOrMgmtId to a host:port key.
- //
- // TODO aconway 2011-02-01: centralize code that constructs/parses
- // connection management IDs. Currently sys:: protocol factories
- // and IO plugins construct the IDs and LinkRegistry parses them.
- size_t separator = keyOrMgmtId.find('-');
- if (separator == std::string::npos) separator = 0;
- std::string key = keyOrMgmtId.substr(separator+1, std::string::npos);
+namespace {
+ void extractHostPort(const std::string& connId, std::string *host, uint16_t *port)
+ {
+ // Extract host and port of remote broker from connection id string.
+ //
+ // TODO aconway 2011-02-01: centralize code that constructs/parses connection
+ // management IDs. Currently sys:: protocol factories and IO plugins construct the
+ // IDs and LinkRegistry parses them.
+ // KAG: current connection id format assumed:
+ // "localhost:port-remotehost:port". In the case of IpV6, the host addresses are
+ // contained within brackets "[...]", example:
+ // connId="[::1]:36859-[::1]:48603". Liberal use of "asserts" provided to alert us
+ // if this assumption changes!
+ size_t separator = connId.find('-');
+ assert(separator != std::string::npos);
+ std::string remote = connId.substr(separator+1, std::string::npos);
+ separator = remote.rfind(":");
+ assert(separator != std::string::npos);
+ *host = remote.substr(0, separator);
+ // IPv6 - host is bracketed by "[]", strip them
+ if ((*host)[0] == '[' && (*host)[host->length() - 1] == ']') {
+ *host = host->substr(1, host->length() - 2);
+ }
+ try {
+ *port = boost::lexical_cast<uint16_t>(remote.substr(separator+1, std::string::npos));
+ } catch (const boost::bad_lexical_cast&) {
+ QPID_LOG(error, "Invalid format for connection identifier! '" << connId << "'");
+ assert(false);
+ }
+ }
+}
+/** find the Link that corresponds to the given connection */
+Link::shared_ptr LinkRegistry::findLink(const std::string& connId)
+{
Mutex::ScopedLock locker(lock);
- LinkMap::iterator l = links.find(key);
- if (l != links.end()) return l->second;
- else return Link::shared_ptr();
+ ConnectionMap::iterator c = connections.find(connId);
+ if (c != connections.end()) {
+ LinkMap::iterator l = links.find(c->second);
+ if (l != links.end())
+ return l->second;
+ }
+ return Link::shared_ptr();
}
void LinkRegistry::notifyConnection(const std::string& key, Connection* c)
{
- Link::shared_ptr link = findLink(key);
+ // find a link that is attempting to connect to the remote, and
+ // create a mapping from connection id to link
+ QPID_LOG(debug, "LinkRegistry::notifyConnection(); key=" << key );
+ std::string host;
+ uint16_t port;
+ extractHostPort( key, &host, &port );
+ Link::shared_ptr link;
+ {
+ Mutex::ScopedLock locker(lock);
+ for (LinkMap::iterator l = links.begin(); l != links.end(); ++l) {
+ if (l->second->pendingConnection(host, port)) {
+ link = l->second;
+ connections[key] = link->getName();
+ link->established(c);
+ break;
+ }
+ }
+ }
+
if (link) {
- link->established(c);
c->setUserId(str(format("%1%@%2%") % link->getUsername() % realm));
}
}
@@ -299,22 +391,29 @@ std::string LinkRegistry::getUsername(const std::string& key)
return link->getUsername();
}
+/** note: returns the current remote host (may be different from the host originally
+ configured for the Link due to failover) */
std::string LinkRegistry::getHost(const std::string& key)
{
- Link::shared_ptr link = findLink(key);
- if (!link)
- return string();
+ Link::shared_ptr link = findLink(key);
+ if (!link)
+ return string();
- return link->getHost();
+ qpid::Address addr;
+ link->getRemoteAddress(addr);
+ return addr.host;
}
+/** returns the current remote port (ditto above) */
uint16_t LinkRegistry::getPort(const std::string& key)
{
Link::shared_ptr link = findLink(key);
if (!link)
return 0;
- return link->getPort();
+ qpid::Address addr;
+ link->getRemoteAddress(addr);
+ return addr.port;
}
std::string LinkRegistry::getPassword(const std::string& key)
@@ -336,20 +435,6 @@ std::string LinkRegistry::getAuthIdentity(const std::string& key)
}
-std::string LinkRegistry::createKey(const qpid::Address& a) {
- // TODO aconway 2010-05-11: key should also include protocol/transport to
- // be unique. Requires refactor of LinkRegistry interface.
- return createKey(a.host, a.port);
-}
-
-std::string LinkRegistry::createKey(const std::string& host, uint16_t port) {
- // TODO aconway 2010-05-11: key should also include protocol/transport to
- // be unique. Requires refactor of LinkRegistry interface.
- stringstream keystream;
- keystream << host << ":" << port;
- return keystream.str();
-}
-
void LinkRegistry::setPassive(bool p)
{
Mutex::ScopedLock locker(lock);
@@ -362,10 +447,12 @@ void LinkRegistry::setPassive(bool p)
}
void LinkRegistry::eachLink(boost::function<void(boost::shared_ptr<Link>)> f) {
+ Mutex::ScopedLock locker(lock);
for (LinkMap::iterator i = links.begin(); i != links.end(); ++i) f(i->second);
}
void LinkRegistry::eachBridge(boost::function<void(boost::shared_ptr<Bridge>)> f) {
+ Mutex::ScopedLock locker(lock);
for (BridgeMap::iterator i = bridges.begin(); i != bridges.end(); ++i) f(i->second);
}
diff --git a/qpid/cpp/src/qpid/broker/LinkRegistry.h b/qpid/cpp/src/qpid/broker/LinkRegistry.h
index 8e9d2f4b0d..5a39b62bd1 100644
--- a/qpid/cpp/src/qpid/broker/LinkRegistry.h
+++ b/qpid/cpp/src/qpid/broker/LinkRegistry.h
@@ -42,9 +42,11 @@ namespace broker {
class LinkRegistry {
typedef std::map<std::string, boost::shared_ptr<Link> > LinkMap;
typedef std::map<std::string, Bridge::shared_ptr> BridgeMap;
+ typedef std::map<std::string, std::string> ConnectionMap;
- LinkMap links;
- BridgeMap bridges;
+ LinkMap links; /** indexed by name of Link */
+ BridgeMap bridges; /** indexed by name of Bridge */
+ ConnectionMap connections; /** indexed by connection identifier, gives link name */
qpid::sys::Mutex lock;
Broker* broker;
@@ -54,15 +56,18 @@ namespace broker {
std::string realm;
boost::shared_ptr<Link> findLink(const std::string& key);
- static std::string createKey(const Address& address);
- static std::string createKey(const std::string& host, uint16_t port);
- // Methods called by the connection observer.
+ // Methods called by the connection observer, key is connection identifier
void notifyConnection (const std::string& key, Connection* c);
void notifyOpened (const std::string& key);
void notifyClosed (const std::string& key);
void notifyConnectionForced (const std::string& key, const std::string& text);
- friend class LinkRegistryConnectionObserver;
+ friend class LinkRegistryConnectionObserver;
+
+ /** Notify the registry that a Link has been destroyed */
+ void linkDestroyed(Link*);
+ /** Request to destroy a Bridge */
+ void destroyBridge(Bridge*);
public:
QPID_BROKER_EXTERN LinkRegistry (); // Only used in store tests
@@ -70,17 +75,29 @@ namespace broker {
QPID_BROKER_EXTERN ~LinkRegistry();
QPID_BROKER_EXTERN std::pair<boost::shared_ptr<Link>, bool>
- declare(const std::string& host,
+ declare(const std::string& name,
+ const std::string& host,
uint16_t port,
const std::string& transport,
bool durable,
const std::string& authMechanism,
const std::string& username,
- const std::string& password);
+ const std::string& password,
+ bool failover=true);
+
+ /** determine if Link exists */
+ QPID_BROKER_EXTERN boost::shared_ptr<Link>
+ getLink(const std::string& name);
+ /** host,port,transport will be matched against the configured values, which may
+ be different from the current values due to failover */
+ QPID_BROKER_EXTERN boost::shared_ptr<Link>
+ getLink(const std::string& configHost,
+ uint16_t configPort,
+ const std::string& configTransport = std::string());
QPID_BROKER_EXTERN std::pair<Bridge::shared_ptr, bool>
- declare(const std::string& host,
- uint16_t port,
+ declare(const std::string& name,
+ Link& link,
bool durable,
const std::string& src,
const std::string& dest,
@@ -91,16 +108,18 @@ namespace broker {
const std::string& excludes,
bool dynamic,
uint16_t sync,
- Bridge::InitializeCallback=0
+ Bridge::InitializeCallback=0,
+ const std::string& queueName="",
+ const std::string& altExchange=""
);
-
- QPID_BROKER_EXTERN void destroy(const std::string& host, const uint16_t port);
-
- QPID_BROKER_EXTERN void destroy(const std::string& host,
- const uint16_t port,
- const std::string& src,
- const std::string& dest,
- const std::string& key);
+ /** determine if Bridge exists */
+ QPID_BROKER_EXTERN Bridge::shared_ptr
+ getBridge(const std::string& name);
+ QPID_BROKER_EXTERN Bridge::shared_ptr
+ getBridge(const Link& link,
+ const std::string& src,
+ const std::string& dest,
+ const std::string& key);
/**
* Register the manageable parent for declared queues
@@ -126,11 +145,6 @@ namespace broker {
QPID_BROKER_EXTERN uint16_t getPort (const std::string& key);
/**
- * Called by links failing over to new address
- */
- void changeAddress(const Address& oldAddress, const Address& newAddress);
-
- /**
* Called to alter passive state. In passive state the links
* and bridges managed by a link registry will be recorded and
* updated but links won't actually establish connections and
diff --git a/qpid/cpp/src/qpid/broker/Message.cpp b/qpid/cpp/src/qpid/broker/Message.cpp
index 7093a68d6c..4dd8a349dd 100644
--- a/qpid/cpp/src/qpid/broker/Message.cpp
+++ b/qpid/cpp/src/qpid/broker/Message.cpp
@@ -131,12 +131,10 @@ uint32_t Message::getRequiredCredit()
void Message::encode(framing::Buffer& buffer) const
{
- {
- sys::Mutex::ScopedLock l(lock); // prevent header modifications while encoding
- //encode method and header frames
- EncodeFrame f1(buffer);
- frames.map_if(f1, TypeFilter2<METHOD_BODY, HEADER_BODY>());
- }
+ sys::Mutex::ScopedLock l(lock);
+ //encode method and header frames
+ EncodeFrame f1(buffer);
+ frames.map_if(f1, TypeFilter2<METHOD_BODY, HEADER_BODY>());
//then encode the payload of each content frame
framing::EncodeBody f2(buffer);
@@ -145,6 +143,7 @@ void Message::encode(framing::Buffer& buffer) const
void Message::encodeContent(framing::Buffer& buffer) const
{
+ sys::Mutex::ScopedLock l(lock);
//encode the payload of each content frame
EncodeBody f2(buffer);
frames.map_if(f2, TypeFilter<CONTENT_BODY>());
@@ -157,6 +156,7 @@ uint32_t Message::encodedSize() const
uint32_t Message::encodedContentSize() const
{
+ sys::Mutex::ScopedLock l(lock);
return frames.getContentSize();
}
@@ -222,8 +222,9 @@ void Message::releaseContent()
store->stage(pmsg);
staged = true;
}
- //ensure required credit is cached before content frames are released
+ //ensure required credit and size is cached before content frames are released
getRequiredCredit();
+ contentSize();
//remove any content frames from the frameset
frames.remove(TypeFilter<CONTENT_BODY>());
setContentReleased();
@@ -383,6 +384,18 @@ void Message::addTraceId(const std::string& id)
}
}
+void Message::clearTrace()
+{
+ sys::Mutex::ScopedLock l(lock);
+ if (isA<MessageTransferBody>()) {
+ FieldTable& headers = getModifiableProperties<MessageProperties>()->getApplicationHeaders();
+ std::string trace = headers.getAsString(X_QPID_TRACE);
+ if (!trace.empty()) {
+ headers.setString(X_QPID_TRACE, "");
+ }
+ }
+}
+
void Message::setTimestamp()
{
sys::Mutex::ScopedLock l(lock);
diff --git a/qpid/cpp/src/qpid/broker/Message.h b/qpid/cpp/src/qpid/broker/Message.h
index dda45d73e6..90e4eec889 100644
--- a/qpid/cpp/src/qpid/broker/Message.h
+++ b/qpid/cpp/src/qpid/broker/Message.h
@@ -161,6 +161,7 @@ public:
bool isExcluded(const std::vector<std::string>& excludes) const;
void addTraceId(const std::string& id);
+ void clearTrace();
void forcePersistent();
bool isForcedPersistent();
diff --git a/qpid/cpp/src/qpid/broker/MessageDeque.cpp b/qpid/cpp/src/qpid/broker/MessageDeque.cpp
index 709d99876b..83c8ca6868 100644
--- a/qpid/cpp/src/qpid/broker/MessageDeque.cpp
+++ b/qpid/cpp/src/qpid/broker/MessageDeque.cpp
@@ -21,6 +21,7 @@
#include "qpid/broker/MessageDeque.h"
#include "qpid/broker/QueuedMessage.h"
#include "qpid/log/Statement.h"
+#include "assert.h"
namespace qpid {
namespace broker {
@@ -40,12 +41,15 @@ bool MessageDeque::deleted(const QueuedMessage& m)
{
size_t i = index(m.position);
if (i < messages.size()) {
- messages[i].status = QueuedMessage::DELETED;
- clean();
- return true;
- } else {
- return false;
+ QueuedMessage *qm = &messages[i];
+ if (qm->status != QueuedMessage::DELETED) {
+ qm->status = QueuedMessage::DELETED;
+ qm->payload = 0; // message no longer needed
+ clean();
+ return true;
+ }
}
+ return false;
}
size_t MessageDeque::size()
@@ -53,7 +57,7 @@ size_t MessageDeque::size()
return available;
}
-void MessageDeque::release(const QueuedMessage& message)
+QueuedMessage* MessageDeque::releasePtr(const QueuedMessage& message)
{
size_t i = index(message.position);
if (i < messages.size()) {
@@ -62,12 +66,17 @@ void MessageDeque::release(const QueuedMessage& message)
if (head > i) head = i;
m.status = QueuedMessage::AVAILABLE;
++available;
+ return &messages[i];
}
} else {
+ assert(0);
QPID_LOG(error, "Failed to release message at " << message.position << " " << message.payload->getFrames().getContent() << "; no such message (index=" << i << ", size=" << messages.size() << ")");
}
+ return 0;
}
+void MessageDeque::release(const QueuedMessage& message) { releasePtr(message); }
+
bool MessageDeque::acquire(const framing::SequenceNumber& position, QueuedMessage& message)
{
if (position < messages.front().position) return false;
@@ -129,8 +138,7 @@ QueuedMessage padding(uint32_t pos) {
}
} // namespace
-bool MessageDeque::push(const QueuedMessage& added, QueuedMessage& /*not needed*/)
-{
+QueuedMessage* MessageDeque::pushPtr(const QueuedMessage& added) {
//add padding to prevent gaps in sequence, which break the index
//calculation (needed for queue replication)
while (messages.size() && (added.position - messages.back().position) > 1)
@@ -139,7 +147,13 @@ bool MessageDeque::push(const QueuedMessage& added, QueuedMessage& /*not needed*
messages.back().status = QueuedMessage::AVAILABLE;
if (head >= messages.size()) head = messages.size() - 1;
++available;
- return false;//adding a message never causes one to be removed for deque
+ clean(); // QPID-4046: let producer help clean the backlog of deleted messages
+ return &messages.back();
+}
+
+bool MessageDeque::push(const QueuedMessage& added, QueuedMessage& /*not needed*/) {
+ pushPtr(added);
+ return false; // adding a message never causes one to be removed for deque
}
void MessageDeque::updateAcquired(const QueuedMessage& acquired)
@@ -163,12 +177,37 @@ void MessageDeque::updateAcquired(const QueuedMessage& acquired)
}
}
+namespace {
+bool isNotDeleted(const QueuedMessage& qm) { return qm.status != QueuedMessage::DELETED; }
+} // namespace
+
+void MessageDeque::setPosition(const framing::SequenceNumber& n) {
+ size_t i = index(n+1);
+ if (i >= messages.size()) return; // Nothing to do.
+
+ // Assertion to verify the precondition: no messaages after n.
+ assert(std::find_if(messages.begin()+i, messages.end(), &isNotDeleted) ==
+ messages.end());
+ messages.erase(messages.begin()+i, messages.end());
+ if (head >= messages.size()) head = messages.size() - 1;
+ // Re-count the available messages
+ available = 0;
+ for (Deque::iterator i = messages.begin(); i != messages.end(); ++i) {
+ if (i->status == QueuedMessage::AVAILABLE) ++available;
+ }
+}
+
void MessageDeque::clean()
{
- while (messages.size() && messages.front().status == QueuedMessage::DELETED) {
+ // QPID-4046: If a queue has multiple consumers, then it is possible for a large
+ // collection of deleted messages to build up. Limit the number of messages cleaned
+ // up on each call to clean().
+ size_t count = 0;
+ while (messages.size() && messages.front().status == QueuedMessage::DELETED && count < 10) {
messages.pop_front();
- if (head) --head;
+ count += 1;
}
+ head = (head > count) ? head - count : 0;
}
void MessageDeque::foreach(Functor f)
diff --git a/qpid/cpp/src/qpid/broker/MessageDeque.h b/qpid/cpp/src/qpid/broker/MessageDeque.h
index bb5943b09b..c5670b2a72 100644
--- a/qpid/cpp/src/qpid/broker/MessageDeque.h
+++ b/qpid/cpp/src/qpid/broker/MessageDeque.h
@@ -44,10 +44,16 @@ class MessageDeque : public Messages
bool consume(QueuedMessage&);
bool push(const QueuedMessage& added, QueuedMessage& removed);
void updateAcquired(const QueuedMessage& acquired);
-
+ void setPosition(const framing::SequenceNumber&);
void foreach(Functor);
void removeIf(Predicate);
+ // For use by other Messages implementations that use MessageDeque as a FIFO index
+ // and keep pointers to its elements in their own indexing strctures.
+ void clean();
+ QueuedMessage* releasePtr(const QueuedMessage&);
+ QueuedMessage* pushPtr(const QueuedMessage& added);
+
private:
typedef std::deque<QueuedMessage> Deque;
Deque messages;
@@ -55,7 +61,6 @@ class MessageDeque : public Messages
size_t head;
size_t index(const framing::SequenceNumber&);
- void clean();
};
}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/MessageGroupManager.cpp b/qpid/cpp/src/qpid/broker/MessageGroupManager.cpp
index 5f450cd556..15cd56a676 100644
--- a/qpid/cpp/src/qpid/broker/MessageGroupManager.cpp
+++ b/qpid/cpp/src/qpid/broker/MessageGroupManager.cpp
@@ -19,11 +19,13 @@
*
*/
+#include "qpid/broker/MessageGroupManager.h"
+
+#include "qpid/broker/Queue.h"
#include "qpid/framing/FieldTable.h"
-#include "qpid/types/Variant.h"
+#include "qpid/framing/FieldValue.h"
#include "qpid/log/Statement.h"
-#include "qpid/broker/Queue.h"
-#include "qpid/broker/MessageGroupManager.h"
+#include "qpid/types/Variant.h"
using namespace qpid::broker;
@@ -43,9 +45,18 @@ const std::string MessageGroupManager::qpidSharedGroup("qpid.shared_msg_group");
const std::string MessageGroupManager::qpidMessageGroupTimestamp("qpid.group_timestamp");
+/** return an iterator to the message at position, or members.end() if not found */
+MessageGroupManager::GroupState::MessageFifo::iterator
+MessageGroupManager::GroupState::findMsg(const qpid::framing::SequenceNumber &position)
+{
+ MessageState mState(position);
+ MessageFifo::iterator found = std::lower_bound(members.begin(), members.end(), mState);
+ return (found->position == position) ? found : members.end();
+}
+
void MessageGroupManager::unFree( const GroupState& state )
{
- GroupFifo::iterator pos = freeGroups.find( state.members.front() );
+ GroupFifo::iterator pos = freeGroups.find( state.members.front().position );
assert( pos != freeGroups.end() && pos->second == &state );
freeGroups.erase( pos );
}
@@ -60,8 +71,8 @@ void MessageGroupManager::disown( GroupState& state )
{
state.owner.clear();
assert(state.members.size());
- assert(freeGroups.find(state.members.front()) == freeGroups.end());
- freeGroups[state.members.front()] = &state;
+ assert(freeGroups.find(state.members.front().position) == freeGroups.end());
+ freeGroups[state.members.front().position] = &state;
}
MessageGroupManager::GroupState& MessageGroupManager::findGroup( const QueuedMessage& qm )
@@ -106,7 +117,8 @@ void MessageGroupManager::enqueued( const QueuedMessage& qm )
// @todo KAG optimization - store reference to group state in QueuedMessage
// issue: const-ness??
GroupState& state = findGroup(qm);
- state.members.push_back(qm.position);
+ GroupState::MessageState mState(qm.position);
+ state.members.push_back(mState);
uint32_t total = state.members.size();
QPID_LOG( trace, "group queue " << qName <<
": added message to group id=" << state.group << " total=" << total );
@@ -123,7 +135,9 @@ void MessageGroupManager::acquired( const QueuedMessage& qm )
// @todo KAG avoid lookup: retrieve direct reference to group state from QueuedMessage
// issue: const-ness??
GroupState& state = findGroup(qm);
- assert(state.members.size()); // there are msgs present
+ GroupState::MessageFifo::iterator m = state.findMsg(qm.position);
+ assert(m != state.members.end());
+ m->acquired = true;
state.acquired += 1;
QPID_LOG( trace, "group queue " << qName <<
": acquired message in group id=" << state.group << " acquired=" << state.acquired );
@@ -137,6 +151,9 @@ void MessageGroupManager::requeued( const QueuedMessage& qm )
GroupState& state = findGroup(qm);
assert( state.acquired != 0 );
state.acquired -= 1;
+ GroupState::MessageFifo::iterator m = state.findMsg(qm.position);
+ assert(m != state.members.end());
+ m->acquired = false;
if (state.acquired == 0 && state.owned()) {
QPID_LOG( trace, "group queue " << qName <<
": consumer name=" << state.owner << " released group id=" << state.group);
@@ -152,13 +169,17 @@ void MessageGroupManager::dequeued( const QueuedMessage& qm )
// @todo KAG avoid lookup: retrieve direct reference to group state from QueuedMessage
// issue: const-ness??
GroupState& state = findGroup(qm);
- assert( state.members.size() != 0 );
- assert( state.acquired != 0 );
- state.acquired -= 1;
+ GroupState::MessageFifo::iterator m = state.findMsg(qm.position);
+ assert(m != state.members.end());
+ if (m->acquired) {
+ assert( state.acquired != 0 );
+ state.acquired -= 1;
+ }
- // likely to be at or near begin() if dequeued in order
+ // special case if qm is first (oldest) message in the group:
+ // may need to re-insert it back on the freeGroups list, as the index will change
bool reFreeNeeded = false;
- if (state.members.front() == qm.position) {
+ if (m == state.members.begin()) {
if (!state.owned()) {
// will be on the freeGroups list if mgmt is dequeueing rather than a consumer!
// if on freelist, it is indexed by first member, which is about to be removed!
@@ -167,15 +188,7 @@ void MessageGroupManager::dequeued( const QueuedMessage& qm )
}
state.members.pop_front();
} else {
- GroupState::PositionFifo::iterator pos = state.members.begin() + 1;
- GroupState::PositionFifo::iterator end = state.members.end();
- while (pos != end) {
- if (*pos == qm.position) {
- state.members.erase(pos);
- break;
- }
- ++pos;
- }
+ state.members.erase(m);
}
uint32_t total = state.members.size();
@@ -220,11 +233,11 @@ bool MessageGroupManager::nextConsumableMessage( Consumer::shared_ptr& c, Queued
GroupState& group = findGroup(next);
if (!group.owned()) {
//TODO: make acquire more efficient when we already have the message in question
- if (group.members.front() == next.position && messages.acquire(next.position, next)) { // only take from head!
+ if (group.members.front().position == next.position && messages.acquire(next.position, next)) { // only take from head!
return true;
}
QPID_LOG(debug, "Skipping " << next.position << " since group " << group.group
- << "'s head message still pending. pos=" << group.members.front());
+ << "'s head message still pending. pos=" << group.members.front().position);
} else if (group.owner == c->getName() && messages.acquire(next.position, next)) {
return true;
}
@@ -284,7 +297,7 @@ void MessageGroupManager::query(qpid::types::Variant::Map& status) const
info[GROUP_TIMESTAMP] = 0;
if (g->second.members.size() != 0) {
QueuedMessage qm;
- if (messages.find(g->second.members.front(), qm) &&
+ if (messages.find(g->second.members.front().position, qm) &&
qm.payload &&
qm.payload->hasProperties<framing::DeliveryProperties>()) {
info[GROUP_TIMESTAMP] = qm.payload->getProperties<framing::DeliveryProperties>()->getTimestamp();
@@ -353,6 +366,7 @@ namespace {
const std::string GROUP_OWNER("owner");
const std::string GROUP_ACQUIRED_CT("acquired-ct");
const std::string GROUP_POSITIONS("positions");
+ const std::string GROUP_ACQUIRED_MSGS("acquired-msgs");
const std::string GROUP_STATE("group-state");
}
@@ -371,10 +385,14 @@ void MessageGroupManager::getState(qpid::framing::FieldTable& state ) const
group.setString(GROUP_OWNER, g->second.owner);
group.setInt(GROUP_ACQUIRED_CT, g->second.acquired);
framing::Array positions(TYPE_CODE_UINT32);
- for (GroupState::PositionFifo::const_iterator p = g->second.members.begin();
- p != g->second.members.end(); ++p)
- positions.push_back(framing::Array::ValuePtr(new IntegerValue( *p )));
+ framing::Array acquiredMsgs(TYPE_CODE_BOOLEAN);
+ for (GroupState::MessageFifo::const_iterator p = g->second.members.begin();
+ p != g->second.members.end(); ++p) {
+ positions.push_back(framing::Array::ValuePtr(new IntegerValue( p->position )));
+ acquiredMsgs.push_back(framing::Array::ValuePtr(new BoolValue( p->acquired )));
+ }
group.setArray(GROUP_POSITIONS, positions);
+ group.setArray(GROUP_ACQUIRED_MSGS, acquiredMsgs);
groupState.push_back(framing::Array::ValuePtr(new FieldTableValue(group)));
}
state.setArray(GROUP_STATE, groupState);
@@ -425,13 +443,25 @@ void MessageGroupManager::setState(const qpid::framing::FieldTable& state)
qName << "\": position encoding error!");
return;
}
+ framing::Array acquiredMsgs(TYPE_CODE_BOOLEAN);
+ ok = group.getArray(GROUP_ACQUIRED_MSGS, acquiredMsgs);
+ if (!ok || positions.count() != acquiredMsgs.count()) {
+ QPID_LOG(error, "Invalid message group state information for queue \"" <<
+ qName << "\": acquired flag encoding error!");
+ return;
+ }
+
+ Array::const_iterator a = acquiredMsgs.begin();
+ for (Array::const_iterator p = positions.begin(); p != positions.end(); ++p) {
+ GroupState::MessageState mState((*p)->getIntegerValue<uint32_t, 4>());
+ mState.acquired = (*a++)->getIntegerValue<bool>();
+ state.members.push_back(mState);
+ }
- for (Array::const_iterator p = positions.begin(); p != positions.end(); ++p)
- state.members.push_back((*p)->getIntegerValue<uint32_t, 4>());
messageGroups[state.group] = state;
if (!state.owned()) {
assert(state.members.size());
- freeGroups[state.members.front()] = &messageGroups[state.group];
+ freeGroups[state.members.front().position] = &messageGroups[state.group];
}
}
diff --git a/qpid/cpp/src/qpid/broker/MessageGroupManager.h b/qpid/cpp/src/qpid/broker/MessageGroupManager.h
index f4bffc4760..2dd97ea2ff 100644
--- a/qpid/cpp/src/qpid/broker/MessageGroupManager.h
+++ b/qpid/cpp/src/qpid/broker/MessageGroupManager.h
@@ -28,11 +28,14 @@
#include "qpid/broker/MessageDistributor.h"
#include "qpid/sys/unordered_map.h"
+#include <deque>
+
namespace qpid {
namespace broker {
class QueueObserver;
class MessageDistributor;
+class Messages;
class MessageGroupManager : public StatefulQueueObserver, public MessageDistributor
{
@@ -45,19 +48,29 @@ class MessageGroupManager : public StatefulQueueObserver, public MessageDistribu
struct GroupState {
// note: update getState()/setState() when changing this object's state implementation
- typedef std::deque<framing::SequenceNumber> PositionFifo;
+
+ // track which messages are in this group, and if they have been acquired
+ struct MessageState {
+ qpid::framing::SequenceNumber position;
+ bool acquired;
+ MessageState() : acquired(false) {}
+ MessageState(const qpid::framing::SequenceNumber& p) : position(p), acquired(false) {}
+ bool operator<(const MessageState& b) const { return position < b.position; }
+ };
+ typedef std::deque<MessageState> MessageFifo;
std::string group; // group identifier
std::string owner; // consumer with outstanding acquired messages
uint32_t acquired; // count of outstanding acquired messages
- PositionFifo members; // msgs belonging to this group
+ MessageFifo members; // msgs belonging to this group, in enqueue order
GroupState() : acquired(0) {}
bool owned() const {return !owner.empty();}
+ MessageFifo::iterator findMsg(const qpid::framing::SequenceNumber &);
};
typedef sys::unordered_map<std::string, struct GroupState> GroupMap;
- typedef std::map<framing::SequenceNumber, struct GroupState *> GroupFifo;
+ typedef std::map<qpid::framing::SequenceNumber, struct GroupState *> GroupFifo;
GroupMap messageGroups; // index: group name
GroupFifo freeGroups; // ordered by oldest free msg
diff --git a/qpid/cpp/src/qpid/broker/MessageMap.cpp b/qpid/cpp/src/qpid/broker/MessageMap.cpp
index 048df45434..d6702a9336 100644
--- a/qpid/cpp/src/qpid/broker/MessageMap.cpp
+++ b/qpid/cpp/src/qpid/broker/MessageMap.cpp
@@ -20,6 +20,8 @@
*/
#include "qpid/broker/MessageMap.h"
#include "qpid/broker/QueuedMessage.h"
+#include "qpid/log/Statement.h"
+#include <algorithm>
namespace qpid {
namespace broker {
@@ -27,7 +29,16 @@ namespace {
const std::string EMPTY;
}
-bool MessageMap::deleted(const QueuedMessage&) { return true; }
+bool MessageMap::deleted(const QueuedMessage& message)
+{
+ Ordering::iterator i = messages.find(message.position);
+ if (i != messages.end()) {
+ erase(i);
+ return true;
+ } else {
+ return false;
+ }
+}
std::string MessageMap::getKey(const QueuedMessage& message)
{
@@ -38,30 +49,32 @@ std::string MessageMap::getKey(const QueuedMessage& message)
size_t MessageMap::size()
{
- return messages.size();
+ size_t count(0);
+ for (Ordering::iterator i = messages.begin(); i != messages.end(); ++i) {
+ if (i->second.status == QueuedMessage::AVAILABLE) ++count;
+ }
+ return count;
}
bool MessageMap::empty()
{
- return messages.empty();
+ return size() == 0;//TODO: more efficient implementation
}
void MessageMap::release(const QueuedMessage& message)
{
- std::string key = getKey(message);
- Index::iterator i = index.find(key);
- if (i == index.end()) {
- index[key] = message;
- messages[message.position] = message;
- } //else message has already been replaced
+ Ordering::iterator i = messages.find(message.position);
+ if (i != messages.end() && i->second.status == QueuedMessage::ACQUIRED) {
+ i->second.status = QueuedMessage::AVAILABLE;
+ }
}
bool MessageMap::acquire(const framing::SequenceNumber& position, QueuedMessage& message)
{
Ordering::iterator i = messages.find(position);
- if (i != messages.end()) {
+ if (i != messages.end() && i->second.status == QueuedMessage::AVAILABLE) {
+ i->second.status = QueuedMessage::ACQUIRED;
message = i->second;
- erase(i);
return true;
} else {
return false;
@@ -71,7 +84,7 @@ bool MessageMap::acquire(const framing::SequenceNumber& position, QueuedMessage&
bool MessageMap::find(const framing::SequenceNumber& position, QueuedMessage& message)
{
Ordering::iterator i = messages.find(position);
- if (i != messages.end()) {
+ if (i != messages.end() && i->second.status == QueuedMessage::AVAILABLE) {
message = i->second;
return true;
} else {
@@ -79,10 +92,10 @@ bool MessageMap::find(const framing::SequenceNumber& position, QueuedMessage& me
}
}
-bool MessageMap::browse(const framing::SequenceNumber& position, QueuedMessage& message, bool)
+bool MessageMap::browse(const framing::SequenceNumber& position, QueuedMessage& message, bool unacquired)
{
Ordering::iterator i = messages.lower_bound(position+1);
- if (i != messages.end()) {
+ if (i != messages.end() && (i->second.status == QueuedMessage::AVAILABLE || (!unacquired && i->second.status == QueuedMessage::ACQUIRED))) {
message = i->second;
return true;
} else {
@@ -92,14 +105,14 @@ bool MessageMap::browse(const framing::SequenceNumber& position, QueuedMessage&
bool MessageMap::consume(QueuedMessage& message)
{
- Ordering::iterator i = messages.begin();
- if (i != messages.end()) {
- message = i->second;
- erase(i);
- return true;
- } else {
- return false;
+ for (Ordering::iterator i = messages.begin(); i != messages.end(); ++i) {
+ if (i->second.status == QueuedMessage::AVAILABLE) {
+ i->second.status = QueuedMessage::ACQUIRED;
+ message = i->second;
+ return true;
+ }
}
+ return false;
}
const QueuedMessage& MessageMap::replace(const QueuedMessage& original, const QueuedMessage& update)
@@ -115,28 +128,48 @@ bool MessageMap::push(const QueuedMessage& added, QueuedMessage& removed)
if (result.second) {
//there was no previous message for this key; nothing needs to
//be removed, just add the message into its correct position
- messages[added.position] = added;
+ QueuedMessage& a = messages[added.position];
+ a = added;
+ a.status = QueuedMessage::AVAILABLE;
+ QPID_LOG(debug, "Added message " << a);
return false;
} else {
//there is already a message with that key which needs to be replaced
removed = result.first->second;
result.first->second = replace(result.first->second, added);
+ result.first->second.status = QueuedMessage::AVAILABLE;
+ QPID_LOG(debug, "Displaced message " << removed << " with " << result.first->second << ": " << result.first->first);
return true;
}
}
+void MessageMap::setPosition(const framing::SequenceNumber& seq) {
+ // Nothing to do, just assert that the precondition is respected and there
+ // are no undeleted messages after seq.
+ assert(messages.empty() || (--messages.end())->first <= seq);
+}
+
void MessageMap::foreach(Functor f)
{
for (Ordering::iterator i = messages.begin(); i != messages.end(); ++i) {
- f(i->second);
+ if (i->second.status == QueuedMessage::AVAILABLE) f(i->second);
}
}
void MessageMap::removeIf(Predicate p)
{
- for (Ordering::iterator i = messages.begin(); i != messages.end(); i++) {
- if (p(i->second)) {
- erase(i);
+ for (Ordering::iterator i = messages.begin(); i != messages.end();) {
+ if (i->second.status == QueuedMessage::AVAILABLE && p(i->second)) {
+ index.erase(getKey(i->second));
+ //Note: Removing from messages means that the subsequent
+ //call to deleted() for the same message will return
+ //false. At present that is not a problem. If this were
+ //changed to hold onto the message until dequeued
+ //(e.g. with REMOVED state), then the erase() below would
+ //need to take that into account.
+ messages.erase(i++);
+ } else {
+ ++i;
}
}
}
diff --git a/qpid/cpp/src/qpid/broker/MessageMap.h b/qpid/cpp/src/qpid/broker/MessageMap.h
index d1b8217f9b..1f0481cb6b 100644
--- a/qpid/cpp/src/qpid/broker/MessageMap.h
+++ b/qpid/cpp/src/qpid/broker/MessageMap.h
@@ -6,7 +6,7 @@
* 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
+o * 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
@@ -43,13 +43,14 @@ class MessageMap : public Messages
size_t size();
bool empty();
- bool deleted(const QueuedMessage&);
+ virtual bool deleted(const QueuedMessage&);
void release(const QueuedMessage&);
virtual bool acquire(const framing::SequenceNumber&, QueuedMessage&);
bool find(const framing::SequenceNumber&, QueuedMessage&);
virtual bool browse(const framing::SequenceNumber&, QueuedMessage&, bool);
bool consume(QueuedMessage&);
virtual bool push(const QueuedMessage& added, QueuedMessage& removed);
+ void setPosition(const framing::SequenceNumber&);
void foreach(Functor);
virtual void removeIf(Predicate);
diff --git a/qpid/cpp/src/qpid/broker/Messages.h b/qpid/cpp/src/qpid/broker/Messages.h
index 61e9fa110a..45f5e6cd81 100644
--- a/qpid/cpp/src/qpid/broker/Messages.h
+++ b/qpid/cpp/src/qpid/broker/Messages.h
@@ -21,6 +21,7 @@
* under the License.
*
*/
+#include "qpid/framing/SequenceNumber.h"
#include <boost/function.hpp>
namespace qpid {
@@ -101,14 +102,22 @@ class Messages
virtual void updateAcquired(const QueuedMessage&) { }
/**
+ * Set the position of the back of the queue. Next message enqueued will be n+1.
+ *@pre Any messages with seq > n must already be dequeued.
+ */
+ virtual void setPosition(const framing::SequenceNumber& /*n*/) = 0;
+
+ /**
* Apply, the functor to each message held
*/
+
virtual void foreach(Functor) = 0;
/**
* Remove every message held that for which the specified
* predicate returns true
*/
virtual void removeIf(Predicate) = 0;
+
private:
};
}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/NameGenerator.h b/qpid/cpp/src/qpid/broker/NameGenerator.h
index 6ea25c9797..2e9f7febe2 100644
--- a/qpid/cpp/src/qpid/broker/NameGenerator.h
+++ b/qpid/cpp/src/qpid/broker/NameGenerator.h
@@ -32,6 +32,7 @@ namespace qpid {
NameGenerator(const std::string& base);
std::string generate();
};
+ const std::string QPID_NAME_PREFIX("qpid."); // reserved for private names
}
}
diff --git a/qpid/cpp/src/qpid/broker/Observers.h b/qpid/cpp/src/qpid/broker/Observers.h
new file mode 100644
index 0000000000..c62f75d6d0
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/Observers.h
@@ -0,0 +1,69 @@
+#ifndef QPID_BROKER_OBSERVERS_H
+#define QPID_BROKER_OBSERVERS_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/sys/Mutex.h"
+#include <boost/shared_ptr.hpp>
+#include <vector>
+#include <algorithm>
+
+namespace qpid {
+namespace broker {
+
+/**
+ * Base class for collections of observers with thread-safe add/remove and traversal.
+ */
+template <class Observer>
+class Observers
+{
+ public:
+ void add(boost::shared_ptr<Observer> observer) {
+ sys::Mutex::ScopedLock l(lock);
+ observers.push_back(observer);
+ }
+
+ void remove(boost::shared_ptr<Observer> observer) {
+ sys::Mutex::ScopedLock l(lock);
+ typename List::iterator i = std::find(observers.begin(), observers.end(), observer);
+ observers.erase(i);
+ }
+
+ protected:
+ typedef std::vector<boost::shared_ptr<Observer> > List;
+
+ sys::Mutex lock;
+ List observers;
+
+ template <class F> void each(F f) {
+ List copy;
+ {
+ sys::Mutex::ScopedLock l(lock);
+ copy = observers;
+ }
+ std::for_each(copy.begin(), copy.end(), f);
+ }
+};
+
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_OBSERVERS_H*/
diff --git a/qpid/cpp/src/qpid/broker/PriorityQueue.cpp b/qpid/cpp/src/qpid/broker/PriorityQueue.cpp
index d807ef22b1..9a0fead744 100644
--- a/qpid/cpp/src/qpid/broker/PriorityQueue.cpp
+++ b/qpid/cpp/src/qpid/broker/PriorityQueue.cpp
@@ -3,13 +3,13 @@
* 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
+ * regarding copyright ownersip. 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
@@ -22,96 +22,87 @@
#include "qpid/broker/Queue.h"
#include "qpid/broker/QueuedMessage.h"
#include "qpid/framing/reply_exceptions.h"
+#include "qpid/log/Statement.h"
#include <cmath>
namespace qpid {
namespace broker {
-PriorityQueue::PriorityQueue(int l) :
+PriorityQueue::PriorityQueue(int l) :
levels(l),
messages(levels, Deque()),
frontLevel(0), haveFront(false), cached(false) {}
-bool PriorityQueue::deleted(const QueuedMessage&) { return true; }
+bool PriorityQueue::deleted(const QueuedMessage& qm) {
+ bool deleted = fifo.deleted(qm);
+ if (deleted) erase(qm);
+ return deleted;
+}
size_t PriorityQueue::size()
{
- size_t total(0);
- for (int i = 0; i < levels; ++i) {
- total += messages[i].size();
- }
- return total;
+ return fifo.size();
+}
+
+namespace {
+bool before(QueuedMessage* a, QueuedMessage* b) { return *a < *b; }
}
void PriorityQueue::release(const QueuedMessage& message)
{
- uint p = getPriorityLevel(message);
- messages[p].insert(lower_bound(messages[p].begin(), messages[p].end(), message), message);
- clearCache();
+ QueuedMessage* qm = fifo.releasePtr(message);
+ if (qm) {
+ uint p = getPriorityLevel(message);
+ messages[p].insert(
+ lower_bound(messages[p].begin(), messages[p].end(), qm, before), qm);
+ clearCache();
+ }
}
-bool PriorityQueue::find(const framing::SequenceNumber& position, QueuedMessage& message, bool remove)
-{
- QueuedMessage comp;
- comp.position = position;
- for (int i = 0; i < levels; ++i) {
- if (!messages[i].empty()) {
- unsigned long diff = position.getValue() - messages[i].front().position.getValue();
- long maxEnd = diff < messages[i].size() ? diff : messages[i].size();
- Deque::iterator l = lower_bound(messages[i].begin(),messages[i].begin()+maxEnd,comp);
- if (l != messages[i].end() && l->position == position) {
- message = *l;
- if (remove) {
- messages[i].erase(l);
- clearCache();
- }
- return true;
- }
+
+void PriorityQueue::erase(const QueuedMessage& qm) {
+ size_t i = getPriorityLevel(qm);
+ if (!messages[i].empty()) {
+ long diff = qm.position.getValue() - messages[i].front()->position.getValue();
+ if (diff < 0) return;
+ long maxEnd = std::min(size_t(diff), messages[i].size());
+ QueuedMessage mutableQm = qm; // need non-const qm for lower_bound
+ Deque::iterator l =
+ lower_bound(messages[i].begin(),messages[i].begin()+maxEnd, &mutableQm, before);
+ if (l != messages[i].end() && (*l)->position == qm.position) {
+ messages[i].erase(l);
+ clearCache();
+ return;
}
}
- return false;
}
bool PriorityQueue::acquire(const framing::SequenceNumber& position, QueuedMessage& message)
{
- return find(position, message, true);
+ bool acquired = fifo.acquire(position, message);
+ if (acquired) erase(message); // No longer available
+ return acquired;
}
bool PriorityQueue::find(const framing::SequenceNumber& position, QueuedMessage& message)
{
- return find(position, message, false);
+ return fifo.find(position, message);
}
-bool PriorityQueue::browse(const framing::SequenceNumber& position, QueuedMessage& message, bool)
+bool PriorityQueue::browse(
+ const framing::SequenceNumber& position, QueuedMessage& message, bool unacquired)
{
- QueuedMessage match;
- match.position = position+1;
- Deque::iterator lowest;
- bool found = false;
- for (int i = 0; i < levels; ++i) {
- Deque::iterator m = lower_bound(messages[i].begin(), messages[i].end(), match);
- if (m != messages[i].end()) {
- if (m->position == match.position) {
- message = *m;
- return true;
- } else if (!found || m->position < lowest->position) {
- lowest = m;
- found = true;
- }
- }
- }
- if (found) {
- message = *lowest;
- }
- return found;
+ return fifo.browse(position, message, unacquired);
}
bool PriorityQueue::consume(QueuedMessage& message)
{
if (checkFront()) {
- message = messages[frontLevel].front();
+ QueuedMessage* pm = messages[frontLevel].front();
messages[frontLevel].pop_front();
clearCache();
+ pm->status = QueuedMessage::ACQUIRED; // Updates FIFO index
+ message = *pm;
return true;
} else {
return false;
@@ -120,23 +111,31 @@ bool PriorityQueue::consume(QueuedMessage& message)
bool PriorityQueue::push(const QueuedMessage& added, QueuedMessage& /*not needed*/)
{
- messages[getPriorityLevel(added)].push_back(added);
+ QueuedMessage* qmp = fifo.pushPtr(added);
+ messages[getPriorityLevel(added)].push_back(qmp);
clearCache();
- return false;//adding a message never causes one to be removed for deque
+ return false; // Adding a message never causes one to be removed for deque
+}
+
+void PriorityQueue::updateAcquired(const QueuedMessage& acquired) {
+ fifo.updateAcquired(acquired);
+}
+
+void PriorityQueue::setPosition(const framing::SequenceNumber& n) {
+ fifo.setPosition(n);
}
void PriorityQueue::foreach(Functor f)
{
- for (int i = 0; i < levels; ++i) {
- std::for_each(messages[i].begin(), messages[i].end(), f);
- }
+ fifo.foreach(f);
}
void PriorityQueue::removeIf(Predicate p)
{
for (int priority = 0; priority < levels; ++priority) {
for (Deque::iterator i = messages[priority].begin(); i != messages[priority].end();) {
- if (p(*i)) {
+ if (p(**i)) {
+ (*i)->status = QueuedMessage::DELETED; // Updates fifo index
i = messages[priority].erase(i);
clearCache();
} else {
@@ -144,6 +143,7 @@ void PriorityQueue::removeIf(Predicate p)
}
}
}
+ fifo.clean();
}
uint PriorityQueue::getPriorityLevel(const QueuedMessage& m) const
diff --git a/qpid/cpp/src/qpid/broker/PriorityQueue.h b/qpid/cpp/src/qpid/broker/PriorityQueue.h
index 67c31468d2..301367358b 100644
--- a/qpid/cpp/src/qpid/broker/PriorityQueue.h
+++ b/qpid/cpp/src/qpid/broker/PriorityQueue.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -21,7 +21,7 @@
* under the License.
*
*/
-#include "qpid/broker/Messages.h"
+#include "qpid/broker/MessageDeque.h"
#include "qpid/sys/IntegerTypes.h"
#include <deque>
#include <vector>
@@ -32,7 +32,10 @@ namespace broker {
/**
* Basic priority queue with a configurable number of recognised
* priority levels. This is implemented as a separate deque per
- * priority level. Browsing is FIFO not priority order.
+ * priority level.
+ *
+ * Browsing is FIFO not priority order. There is a MessageDeque
+ * for fast browsing.
*/
class PriorityQueue : public Messages
{
@@ -48,23 +51,32 @@ class PriorityQueue : public Messages
bool browse(const framing::SequenceNumber&, QueuedMessage&, bool);
bool consume(QueuedMessage&);
bool push(const QueuedMessage& added, QueuedMessage& removed);
-
+ void updateAcquired(const QueuedMessage& acquired);
+ void setPosition(const framing::SequenceNumber&);
void foreach(Functor);
void removeIf(Predicate);
+
static uint getPriority(const QueuedMessage&);
+
protected:
- typedef std::deque<QueuedMessage> Deque;
+ typedef std::deque<QueuedMessage*> Deque;
typedef std::vector<Deque> PriorityLevels;
virtual bool findFrontLevel(uint& p, PriorityLevels&);
const int levels;
+
private:
+ /** Available messages separated by priority and sorted in priority order.
+ * Holds pointers to the QueuedMessages in fifo
+ */
PriorityLevels messages;
+ /** FIFO index of all messsagse (including acquired messages) for fast browsing and indexing */
+ MessageDeque fifo;
uint frontLevel;
bool haveFront;
bool cached;
-
- bool find(const framing::SequenceNumber&, QueuedMessage&, bool remove);
+
+ void erase(const QueuedMessage&);
uint getPriorityLevel(const QueuedMessage&) const;
void clearCache();
bool checkFront();
diff --git a/qpid/cpp/src/qpid/broker/Queue.cpp b/qpid/cpp/src/qpid/broker/Queue.cpp
index 01ce017289..3202a2676f 100644
--- a/qpid/cpp/src/qpid/broker/Queue.cpp
+++ b/qpid/cpp/src/qpid/broker/Queue.cpp
@@ -19,8 +19,9 @@
*
*/
-#include "qpid/broker/Broker.h"
#include "qpid/broker/Queue.h"
+
+#include "qpid/broker/Broker.h"
#include "qpid/broker/QueueEvents.h"
#include "qpid/broker/Exchange.h"
#include "qpid/broker/Fairshare.h"
@@ -41,12 +42,14 @@
#include "qpid/management/ManagementAgent.h"
#include "qpid/framing/reply_exceptions.h"
#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/FieldValue.h"
#include "qpid/sys/ClusterSafe.h"
#include "qpid/sys/Monitor.h"
#include "qpid/sys/Time.h"
#include "qpid/types/Variant.h"
#include "qmf/org/apache/qpid/broker/ArgsQueuePurge.h"
#include "qmf/org/apache/qpid/broker/ArgsQueueReroute.h"
+#include "qmf/org/apache/qpid/broker/EventQueueDelete.h"
#include <iostream>
#include <algorithm>
@@ -56,13 +59,16 @@
#include <boost/intrusive_ptr.hpp>
-using namespace qpid::broker;
+namespace qpid {
+namespace broker {
+
using namespace qpid::sys;
using namespace qpid::framing;
using qpid::management::ManagementAgent;
using qpid::management::ManagementObject;
using qpid::management::Manageable;
using qpid::management::Args;
+using std::string;
using std::for_each;
using std::mem_fun;
namespace _qmf = qmf::org::apache::qpid::broker;
@@ -150,6 +156,7 @@ Queue::Queue(const string& _name, bool _autodelete,
store(_store),
owner(_owner),
consumerCount(0),
+ browserCount(0),
exclusive(0),
noLocal(false),
persistLastNode(false),
@@ -232,11 +239,16 @@ void Queue::deliver(boost::intrusive_ptr<Message> msg){
void Queue::recoverPrepared(boost::intrusive_ptr<Message>& msg)
{
+ Mutex::ScopedLock locker(messageLock);
if (policy.get()) policy->recoverEnqueued(msg);
}
-void Queue::recover(boost::intrusive_ptr<Message>& msg){
- if (policy.get()) policy->recoverEnqueued(msg);
+void Queue::recover(boost::intrusive_ptr<Message>& msg)
+{
+ {
+ Mutex::ScopedLock locker(messageLock);
+ if (policy.get()) policy->recoverEnqueued(msg);
+ }
push(msg, true);
if (store){
@@ -276,7 +288,6 @@ void Queue::requeue(const QueuedMessage& msg){
assertClusterSafe();
QueueListeners::NotificationSet copy;
{
- Mutex::ScopedLock locker(messageLock);
if (!isEnqueued(msg)) return;
if (deleted) {
//
@@ -294,8 +305,18 @@ void Queue::requeue(const QueuedMessage& msg){
}
mgntDeqStats(msg.payload, mgmtObject, brokerMgmtObject);
} else {
- messages->release(msg);
- listeners.populate(copy);
+ {
+ Mutex::ScopedLock locker(messageLock);
+ messages->release(msg);
+ observeRequeue(msg, locker);
+ listeners.populate(copy);
+ }
+
+ if (mgmtObject) {
+ mgmtObject->inc_releases();
+ if (brokerMgmtObject)
+ brokerMgmtObject->inc_releases();
+ }
// for persistLastNode - don't force a message twice to disk, but force it if no force before
if(inLastNodeFailure && persistLastNode && !msg.payload->isStoredOnQueue(shared_from_this())) {
@@ -305,7 +326,6 @@ void Queue::requeue(const QueuedMessage& msg){
enqueue(0, payload);
}
}
- observeRequeue(msg, locker);
}
}
copy.notify();
@@ -313,10 +333,9 @@ void Queue::requeue(const QueuedMessage& msg){
bool Queue::acquireMessageAt(const SequenceNumber& position, QueuedMessage& message)
{
- Mutex::ScopedLock locker(messageLock);
assertClusterSafe();
QPID_LOG(debug, "Attempting to acquire message at " << position);
- if (acquire(position, message, locker)) {
+ if (acquire(position, message)) {
QPID_LOG(debug, "Acquired message at " << position << " from " << name);
return true;
} else {
@@ -327,17 +346,20 @@ bool Queue::acquireMessageAt(const SequenceNumber& position, QueuedMessage& mess
bool Queue::acquire(const QueuedMessage& msg, const std::string& consumer)
{
- Mutex::ScopedLock locker(messageLock);
assertClusterSafe();
QPID_LOG(debug, consumer << " attempting to acquire message at " << msg.position);
-
- if (!allocator->allocate( consumer, msg )) {
+ bool ok;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ ok = allocator->allocate( consumer, msg );
+ }
+ if (!ok) {
QPID_LOG(debug, "Not permitted to acquire msg at " << msg.position << " from '" << name);
return false;
}
QueuedMessage copy(msg);
- if (acquire( msg.position, copy, locker)) {
+ if (acquire( msg.position, copy)) {
QPID_LOG(debug, "Acquired message at " << msg.position << " from " << name);
return true;
}
@@ -379,59 +401,73 @@ bool Queue::getNextMessage(QueuedMessage& m, Consumer::shared_ptr& c)
Queue::ConsumeCode Queue::consumeNextMessage(QueuedMessage& m, Consumer::shared_ptr& c)
{
while (true) {
- Mutex::ScopedLock locker(messageLock);
QueuedMessage msg;
- if (allocator->nextConsumableMessage(c, msg)) {
- if (msg.payload->hasExpired()) {
- QPID_LOG(debug, "Message expired from queue '" << name << "'");
- c->setPosition(msg.position);
- dequeue(0, msg);
- if (mgmtObject) {
- mgmtObject->inc_discardsTtl();
- if (brokerMgmtObject)
- brokerMgmtObject->inc_discardsTtl();
- }
+ bool found;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ found = allocator->nextConsumableMessage(c, msg);
+ if (!found) listeners.addListener(c);
+ }
+ if (!found) {
+ QPID_LOG(debug, "No messages to dispatch on queue '" << name << "'");
+ return NO_MESSAGES;
+ }
- continue;
+ if (msg.payload->hasExpired()) {
+ QPID_LOG(debug, "Message expired from queue '" << name << "'");
+ c->setPosition(msg.position);
+ dequeue(0, msg);
+ if (mgmtObject) {
+ mgmtObject->inc_discardsTtl();
+ if (brokerMgmtObject)
+ brokerMgmtObject->inc_discardsTtl();
}
+ continue;
+ }
- if (c->filter(msg.payload)) {
- if (c->accept(msg.payload)) {
+ if (c->filter(msg.payload)) {
+ if (c->accept(msg.payload)) {
+ {
+ Mutex::ScopedLock locker(messageLock);
bool ok = allocator->allocate( c->getName(), msg ); // inform allocator
(void) ok; assert(ok);
observeAcquire(msg, locker);
- m = msg;
- return CONSUMED;
- } else {
- //message(s) are available but consumer hasn't got enough credit
- QPID_LOG(debug, "Consumer can't currently accept message from '" << name << "'");
- messages->release(msg);
- return CANT_CONSUME;
}
+ if (mgmtObject) {
+ mgmtObject->inc_acquires();
+ if (brokerMgmtObject)
+ brokerMgmtObject->inc_acquires();
+ }
+ m = msg;
+ return CONSUMED;
} else {
- //consumer will never want this message
- QPID_LOG(debug, "Consumer doesn't want message from '" << name << "'");
- messages->release(msg);
- return CANT_CONSUME;
+ //message(s) are available but consumer hasn't got enough credit
+ QPID_LOG(debug, "Consumer can't currently accept message from '" << name << "'");
}
} else {
- QPID_LOG(debug, "No messages to dispatch on queue '" << name << "'");
- listeners.addListener(c);
- return NO_MESSAGES;
+ //consumer will never want this message
+ QPID_LOG(debug, "Consumer doesn't want message from '" << name << "'");
}
+
+ Mutex::ScopedLock locker(messageLock);
+ messages->release(msg);
+ return CANT_CONSUME;
}
}
bool Queue::browseNextMessage(QueuedMessage& m, Consumer::shared_ptr& c)
{
while (true) {
- Mutex::ScopedLock locker(messageLock);
QueuedMessage msg;
-
- if (!allocator->nextBrowsableMessage(c, msg)) { // no next available
+ bool found;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ found = allocator->nextBrowsableMessage(c, msg);
+ if (!found) listeners.addListener(c);
+ }
+ if (!found) { // no next available
QPID_LOG(debug, "No browsable messages available for consumer " <<
c->getName() << " on queue '" << name << "'");
- listeners.addListener(c);
return false;
}
@@ -489,78 +525,115 @@ bool Queue::find(SequenceNumber pos, QueuedMessage& msg) const {
void Queue::consume(Consumer::shared_ptr c, bool requestExclusive){
assertClusterSafe();
{
- Mutex::ScopedLock locker(consumerLock);
- if(exclusive) {
- throw ResourceLockedException(
- QPID_MSG("Queue " << getName() << " has an exclusive consumer. No more consumers allowed."));
- } else if(requestExclusive) {
- if(consumerCount) {
+ Mutex::ScopedLock locker(messageLock);
+ // NOTE: consumerCount is actually a count of all
+ // subscriptions, both acquiring and non-acquiring (browsers).
+ // Check for exclusivity of acquiring consumers.
+ size_t acquiringConsumers = consumerCount - browserCount;
+ if (c->preAcquires()) {
+ if(exclusive) {
throw ResourceLockedException(
- QPID_MSG("Queue " << getName() << " already has consumers. Exclusive access denied."));
- } else {
- exclusive = c->getSession();
+ QPID_MSG("Queue " << getName()
+ << " has an exclusive consumer. No more consumers allowed."));
+ } else if(requestExclusive) {
+ if(acquiringConsumers) {
+ throw ResourceLockedException(
+ QPID_MSG("Queue " << getName()
+ << " already has consumers. Exclusive access denied."));
+ } else {
+ exclusive = c->getSession();
+ }
}
}
+ else
+ browserCount++;
consumerCount++;
- if (mgmtObject != 0)
- mgmtObject->inc_consumerCount ();
//reset auto deletion timer if necessary
if (autoDeleteTimeout && autoDeleteTask) {
autoDeleteTask->cancel();
}
+ observeConsumerAdd(*c, locker);
}
- Mutex::ScopedLock locker(messageLock);
- for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) {
- try{
- (*i)->consumerAdded(*c);
- } catch (const std::exception& e) {
- QPID_LOG(warning, "Exception on notification of new consumer for queue " << getName() << ": " << e.what());
- }
- }
+ if (mgmtObject != 0)
+ mgmtObject->inc_consumerCount ();
}
void Queue::cancel(Consumer::shared_ptr c){
removeListener(c);
{
- Mutex::ScopedLock locker(consumerLock);
+ Mutex::ScopedLock locker(messageLock);
consumerCount--;
+ if (!c->preAcquires()) browserCount--;
if(exclusive) exclusive = 0;
- if (mgmtObject != 0)
- mgmtObject->dec_consumerCount ();
- }
- Mutex::ScopedLock locker(messageLock);
- for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) {
- try{
- (*i)->consumerRemoved(*c);
- } catch (const std::exception& e) {
- QPID_LOG(warning, "Exception on notification of removed consumer for queue " << getName() << ": " << e.what());
- }
+ observeConsumerRemove(*c, locker);
}
+ if (mgmtObject != 0)
+ mgmtObject->dec_consumerCount ();
}
QueuedMessage Queue::get(){
- Mutex::ScopedLock locker(messageLock);
QueuedMessage msg(this);
- if (messages->consume(msg))
- observeAcquire(msg, locker);
+ bool ok;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ ok = messages->consume(msg);
+ if (ok) observeAcquire(msg, locker);
+ }
+
+ if (ok && mgmtObject) {
+ mgmtObject->inc_acquires();
+ if (brokerMgmtObject)
+ brokerMgmtObject->inc_acquires();
+ }
+
return msg;
}
-bool collect_if_expired(std::deque<QueuedMessage>& expired, QueuedMessage& message)
+namespace {
+bool collectIf(QueuedMessage& qm, Messages::Predicate predicate,
+ std::deque<QueuedMessage>& collection)
{
- if (message.payload->hasExpired()) {
- expired.push_back(message);
+ if (predicate(qm)) {
+ collection.push_back(qm);
return true;
} else {
return false;
}
}
+bool isExpired(const QueuedMessage& qm) { return qm.payload->hasExpired(); }
+} // namespace
+
+void Queue::dequeueIf(Messages::Predicate predicate,
+ std::deque<QueuedMessage>& dequeued)
+{
+ {
+ Mutex::ScopedLock locker(messageLock);
+ messages->removeIf(boost::bind(&collectIf, _1, predicate, boost::ref(dequeued)));
+ }
+ if (!dequeued.empty()) {
+ if (mgmtObject) {
+ mgmtObject->inc_acquires(dequeued.size());
+ if (brokerMgmtObject)
+ brokerMgmtObject->inc_acquires(dequeued.size());
+ }
+ for (std::deque<QueuedMessage>::const_iterator i = dequeued.begin();
+ i != dequeued.end(); ++i) {
+ {
+ // KAG: should be safe to retake lock after the removeIf, since
+ // no other thread can touch these messages after the removeIf() call
+ Mutex::ScopedLock locker(messageLock);
+ observeAcquire(*i, locker);
+ }
+ dequeue( 0, *i );
+ }
+ }
+}
+
/**
*@param lapse: time since the last purgeExpired
*/
-void Queue::purgeExpired(qpid::sys::Duration lapse)
-{
+void Queue::purgeExpired(sys::Duration lapse) {
//As expired messages are discarded during dequeue also, only
//bother explicitly expiring if the rate of dequeues since last
//attempt is less than one per second.
@@ -568,33 +641,18 @@ void Queue::purgeExpired(qpid::sys::Duration lapse)
dequeueSincePurge -= count;
int seconds = int64_t(lapse)/qpid::sys::TIME_SEC;
if (seconds == 0 || count / seconds < 1) {
- std::deque<QueuedMessage> expired;
- {
- Mutex::ScopedLock locker(messageLock);
- messages->removeIf(boost::bind(&collect_if_expired, boost::ref(expired), _1));
- }
-
- //
- // Report the count of discarded-by-ttl messages
- //
- if (mgmtObject && !expired.empty()) {
- mgmtObject->inc_discardsTtl(expired.size());
- if (brokerMgmtObject)
- brokerMgmtObject->inc_discardsTtl(expired.size());
- }
-
- for (std::deque<QueuedMessage>::const_iterator i = expired.begin();
- i != expired.end(); ++i) {
- {
- Mutex::ScopedLock locker(messageLock);
- observeAcquire(*i, locker);
+ std::deque<QueuedMessage> dequeued;
+ dequeueIf(boost::bind(&isExpired, _1), dequeued);
+ if (dequeued.size()) {
+ if (mgmtObject) {
+ mgmtObject->inc_discardsTtl(dequeued.size());
+ if (brokerMgmtObject)
+ brokerMgmtObject->inc_discardsTtl(dequeued.size());
}
- dequeue( 0, *i );
}
}
}
-
namespace {
// for use with purge/move below - collect messages that match a given filter
//
@@ -715,32 +773,47 @@ uint32_t Queue::purge(const uint32_t purge_request, boost::shared_ptr<Exchange>
std::auto_ptr<MessageFilter> mf(MessageFilter::create(filter));
Collector c(*mf.get(), purge_request);
- Mutex::ScopedLock locker(messageLock);
- messages->removeIf( boost::bind<bool>(boost::ref(c), _1) );
+ {
+ Mutex::ScopedLock locker(messageLock);
+ messages->removeIf( boost::bind<bool>(boost::ref(c), _1) );
+ }
- if (mgmtObject && !c.matches.empty()) {
- if (dest.get()) {
- mgmtObject->inc_reroutes(c.matches.size());
- if (brokerMgmtObject)
- brokerMgmtObject->inc_reroutes(c.matches.size());
- } else {
- mgmtObject->inc_discardsPurge(c.matches.size());
- if (brokerMgmtObject)
- brokerMgmtObject->inc_discardsPurge(c.matches.size());
+ if (!c.matches.empty()) {
+ if (mgmtObject) {
+ mgmtObject->inc_acquires(c.matches.size());
+ if (dest.get()) {
+ mgmtObject->inc_reroutes(c.matches.size());
+ if (brokerMgmtObject) {
+ brokerMgmtObject->inc_acquires(c.matches.size());
+ brokerMgmtObject->inc_reroutes(c.matches.size());
+ }
+ } else {
+ mgmtObject->inc_discardsPurge(c.matches.size());
+ if (brokerMgmtObject) {
+ brokerMgmtObject->inc_acquires(c.matches.size());
+ brokerMgmtObject->inc_discardsPurge(c.matches.size());
+ }
+ }
}
- }
- for (std::deque<QueuedMessage>::iterator qmsg = c.matches.begin();
- qmsg != c.matches.end(); ++qmsg) {
- // Update observers and message state:
- observeAcquire(*qmsg, locker);
- dequeue(0, *qmsg);
- QPID_LOG(debug, "Purged message at " << qmsg->position << " from " << getName());
- // now reroute if necessary
- if (dest.get()) {
- assert(qmsg->payload);
- DeliverableMessage dmsg(qmsg->payload);
- dest->routeWithAlternate(dmsg);
+ for (std::deque<QueuedMessage>::iterator qmsg = c.matches.begin();
+ qmsg != c.matches.end(); ++qmsg) {
+
+ {
+ // KAG: should be safe to retake lock after the removeIf, since
+ // no other thread can touch these messages after the removeIf call
+ Mutex::ScopedLock locker(messageLock);
+ observeAcquire(*qmsg, locker);
+ }
+ dequeue(0, *qmsg);
+ QPID_LOG(debug, "Purged message at " << qmsg->position << " from " << getName());
+ // now reroute if necessary
+ if (dest.get()) {
+ assert(qmsg->payload);
+ qmsg->payload->clearTrace();
+ DeliverableMessage dmsg(qmsg->payload);
+ dest->routeWithAlternate(dmsg);
+ }
}
}
return c.matches.size();
@@ -752,27 +825,51 @@ uint32_t Queue::move(const Queue::shared_ptr destq, uint32_t qty,
std::auto_ptr<MessageFilter> mf(MessageFilter::create(filter));
Collector c(*mf.get(), qty);
- Mutex::ScopedLock locker(messageLock);
- messages->removeIf( boost::bind<bool>(boost::ref(c), _1) );
+ {
+ Mutex::ScopedLock locker(messageLock);
+ messages->removeIf( boost::bind<bool>(boost::ref(c), _1) );
+ }
+
- for (std::deque<QueuedMessage>::iterator qmsg = c.matches.begin();
- qmsg != c.matches.end(); ++qmsg) {
+ if (!c.matches.empty()) {
// Update observers and message state:
- observeAcquire(*qmsg, locker);
- dequeue(0, *qmsg);
- // and move to destination Queue.
- assert(qmsg->payload);
- destq->deliver(qmsg->payload);
+
+ if (mgmtObject) {
+ mgmtObject->inc_acquires(c.matches.size());
+ if (brokerMgmtObject)
+ brokerMgmtObject->inc_acquires(c.matches.size());
+ }
+
+ for (std::deque<QueuedMessage>::iterator qmsg = c.matches.begin();
+ qmsg != c.matches.end(); ++qmsg) {
+ {
+ Mutex::ScopedLock locker(messageLock);
+ observeAcquire(*qmsg, locker);
+ }
+ dequeue(0, *qmsg);
+ // and move to destination Queue.
+ assert(qmsg->payload);
+ destq->deliver(qmsg->payload);
+ }
}
return c.matches.size();
}
/** Acquire the message at the given position, return true and msg if acquire succeeds */
-bool Queue::acquire(const qpid::framing::SequenceNumber& position, QueuedMessage& msg,
- const Mutex::ScopedLock& locker)
+bool Queue::acquire(const qpid::framing::SequenceNumber& position, QueuedMessage& msg)
{
- if (messages->acquire(position, msg)) {
- observeAcquire(msg, locker);
+ bool ok;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ ok = messages->acquire(position, msg);
+ if (ok) observeAcquire(msg, locker);
+ }
+ if (ok) {
+ if (mgmtObject) {
+ mgmtObject->inc_acquires();
+ if (brokerMgmtObject)
+ brokerMgmtObject->inc_acquires();
+ }
++dequeueSincePurge;
return true;
}
@@ -782,35 +879,44 @@ bool Queue::acquire(const qpid::framing::SequenceNumber& position, QueuedMessage
void Queue::push(boost::intrusive_ptr<Message>& msg, bool isRecovery){
assertClusterSafe();
QueueListeners::NotificationSet copy;
- QueuedMessage removed;
+ QueuedMessage removed, qm(this, msg);
bool dequeueRequired = false;
{
Mutex::ScopedLock locker(messageLock);
- QueuedMessage qm(this, msg, ++sequence);
- if (insertSeqNo) msg->insertCustomProperty(seqNoKey, sequence);
-
- dequeueRequired = messages->push(qm, removed);
- if (dequeueRequired) {
+ qm.position = ++sequence;
+ if (messages->push(qm, removed)) {
+ dequeueRequired = true;
observeAcquire(removed, locker);
- if (mgmtObject) {
- mgmtObject->inc_discardsLvq();
- if (brokerMgmtObject)
- brokerMgmtObject->inc_discardsLvq();
- }
}
- listeners.populate(copy);
observeEnqueue(qm, locker);
+ if (policy.get()) {
+ policy->enqueued(qm);
+ }
+ listeners.populate(copy);
}
- copy.notify();
+ if (insertSeqNo) msg->insertCustomProperty(seqNoKey, qm.position);
+
+ mgntEnqStats(msg, mgmtObject, brokerMgmtObject);
+
if (dequeueRequired) {
+ if (mgmtObject) {
+ mgmtObject->inc_acquires();
+ mgmtObject->inc_discardsLvq();
+ if (brokerMgmtObject) {
+ brokerMgmtObject->inc_acquires();
+ brokerMgmtObject->inc_discardsLvq();
+ }
+ }
if (isRecovery) {
//can't issue new requests for the store until
//recovery is complete
+ Mutex::ScopedLock locker(messageLock);
pendingDequeues.push_back(removed);
} else {
dequeue(0, removed);
}
}
+ copy.notify();
}
void isEnqueueComplete(uint32_t* result, const QueuedMessage& message)
@@ -821,8 +927,8 @@ void isEnqueueComplete(uint32_t* result, const QueuedMessage& message)
/** function only provided for unit tests, or code not in critical message path */
uint32_t Queue::getEnqueueCompleteMessageCount() const
{
- Mutex::ScopedLock locker(messageLock);
uint32_t count = 0;
+ Mutex::ScopedLock locker(messageLock);
messages->foreach(boost::bind(&isEnqueueComplete, &count, _1));
return count;
}
@@ -835,13 +941,13 @@ uint32_t Queue::getMessageCount() const
uint32_t Queue::getConsumerCount() const
{
- Mutex::ScopedLock locker(consumerLock);
+ Mutex::ScopedLock locker(messageLock);
return consumerCount;
}
bool Queue::canAutoDelete() const
{
- Mutex::ScopedLock locker(consumerLock);
+ Mutex::ScopedLock locker(messageLock);
return autodelete && !consumerCount && !owner;
}
@@ -948,14 +1054,20 @@ bool Queue::dequeue(TransactionContext* ctxt, const QueuedMessage& msg)
{
ScopedUse u(barrier);
if (!u.acquired) return false;
-
{
Mutex::ScopedLock locker(messageLock);
if (!isEnqueued(msg)) return false;
if (!ctxt) {
+ if (policy.get()) policy->dequeued(msg);
+ messages->deleted(msg);
observeDequeue(msg, locker);
}
}
+
+ if (!ctxt) {
+ mgntDeqStats(msg.payload, mgmtObject, brokerMgmtObject);
+ }
+
// This check prevents messages which have been forced persistent on one queue from dequeuing
// from another on which no forcing has taken place and thus causing a store error.
bool fp = msg.payload->isForcedPersistent();
@@ -972,8 +1084,13 @@ bool Queue::dequeue(TransactionContext* ctxt, const QueuedMessage& msg)
void Queue::dequeueCommitted(const QueuedMessage& msg)
{
- Mutex::ScopedLock locker(messageLock);
- observeDequeue(msg, locker);
+ {
+ Mutex::ScopedLock locker(messageLock);
+ if (policy.get()) policy->dequeued(msg);
+ messages->deleted(msg);
+ observeDequeue(msg, locker);
+ }
+ mgntDeqStats(msg.payload, mgmtObject, brokerMgmtObject);
if (mgmtObject != 0) {
_qmf::Queue::PerThreadStats *qStats = mgmtObject->getStatistics();
const uint64_t contentSize = msg.payload->contentSize();
@@ -993,10 +1110,20 @@ void Queue::dequeueCommitted(const QueuedMessage& msg)
* Removes the first (oldest) message from the in-memory delivery queue as well dequeing
* it from the logical (and persistent if applicable) queue
*/
-bool Queue::popAndDequeue(QueuedMessage& msg, const Mutex::ScopedLock& locker)
+bool Queue::popAndDequeue(QueuedMessage& msg)
{
- if (messages->consume(msg)) {
- observeAcquire(msg, locker);
+ bool popped;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ popped = messages->consume(msg);
+ if (popped) observeAcquire(msg, locker);
+ }
+ if (popped) {
+ if (mgmtObject) {
+ mgmtObject->inc_acquires();
+ if (brokerMgmtObject)
+ brokerMgmtObject->inc_acquires();
+ }
dequeue(0, msg);
return true;
} else {
@@ -1006,13 +1133,10 @@ bool Queue::popAndDequeue(QueuedMessage& msg, const Mutex::ScopedLock& locker)
/**
* Updates policy and management when a message has been dequeued,
- * expects messageLock to be held
+ * Requires messageLock be held by caller.
*/
-void Queue::observeDequeue(const QueuedMessage& msg, const Mutex::ScopedLock&)
+void Queue::observeDequeue(const QueuedMessage& msg, const qpid::sys::Mutex::ScopedLock&)
{
- mgntDeqStats(msg.payload, mgmtObject, brokerMgmtObject);
- if (policy.get()) policy->dequeued(msg);
- messages->deleted(msg);
for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) {
try{
(*i)->dequeued(msg);
@@ -1022,17 +1146,11 @@ void Queue::observeDequeue(const QueuedMessage& msg, const Mutex::ScopedLock&)
}
}
-/** updates queue observers when a message has become unavailable for transfer,
- * expects messageLock to be held
+/** updates queue observers when a message has become unavailable for transfer.
+ * Requires messageLock be held by caller.
*/
-void Queue::observeAcquire(const QueuedMessage& msg, const Mutex::ScopedLock&)
+void Queue::observeAcquire(const QueuedMessage& msg, const qpid::sys::Mutex::ScopedLock&)
{
- if (mgmtObject) {
- mgmtObject->inc_acquires();
- if (brokerMgmtObject)
- brokerMgmtObject->inc_acquires();
- }
-
for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) {
try{
(*i)->acquired(msg);
@@ -1042,17 +1160,11 @@ void Queue::observeAcquire(const QueuedMessage& msg, const Mutex::ScopedLock&)
}
}
-/** updates queue observers when a message has become re-available for transfer,
- * expects messageLock to be held
+/** updates queue observers when a message has become re-available for transfer
+ * Requires messageLock be held by caller.
*/
-void Queue::observeRequeue(const QueuedMessage& msg, const Mutex::ScopedLock&)
+void Queue::observeRequeue(const QueuedMessage& msg, const qpid::sys::Mutex::ScopedLock&)
{
- if (mgmtObject) {
- mgmtObject->inc_releases();
- if (brokerMgmtObject)
- brokerMgmtObject->inc_releases();
- }
-
for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) {
try{
(*i)->requeued(msg);
@@ -1062,6 +1174,33 @@ void Queue::observeRequeue(const QueuedMessage& msg, const Mutex::ScopedLock&)
}
}
+/** updates queue observers when a new consumer has subscribed to this queue.
+ */
+void Queue::observeConsumerAdd( const Consumer& c, const qpid::sys::Mutex::ScopedLock&)
+{
+ for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) {
+ try{
+ (*i)->consumerAdded(c);
+ } catch (const std::exception& e) {
+ QPID_LOG(warning, "Exception on notification of new consumer for queue " << getName() << ": " << e.what());
+ }
+ }
+}
+
+/** updates queue observers when a consumer has unsubscribed from this queue.
+ */
+void Queue::observeConsumerRemove( const Consumer& c, const qpid::sys::Mutex::ScopedLock&)
+{
+ for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) {
+ try{
+ (*i)->consumerRemoved(c);
+ } catch (const std::exception& e) {
+ QPID_LOG(warning, "Exception on notification of removed consumer for queue " << getName() << ": " << e.what());
+ }
+ }
+}
+
+
void Queue::create(const FieldTable& _settings)
{
settings = _settings;
@@ -1209,23 +1348,21 @@ void Queue::configureImpl(const FieldTable& _settings)
void Queue::destroyed()
{
unbind(broker->getExchanges());
- {
- Mutex::ScopedLock locker(messageLock);
- QueuedMessage m;
- while(popAndDequeue(m, locker)) {
- DeliverableMessage msg(m.payload);
- if (alternateExchange.get()) {
- if (brokerMgmtObject)
- brokerMgmtObject->inc_abandonedViaAlt();
- alternateExchange->routeWithAlternate(msg);
- } else {
- if (brokerMgmtObject)
- brokerMgmtObject->inc_abandoned();
- }
+
+ QueuedMessage m;
+ while(popAndDequeue(m)) {
+ DeliverableMessage msg(m.payload);
+ if (alternateExchange.get()) {
+ if (brokerMgmtObject)
+ brokerMgmtObject->inc_abandonedViaAlt();
+ alternateExchange->routeWithAlternate(msg);
+ } else {
+ if (brokerMgmtObject)
+ brokerMgmtObject->inc_abandoned();
}
- if (alternateExchange.get())
- alternateExchange->decAlternateUsers();
}
+ if (alternateExchange.get())
+ alternateExchange->decAlternateUsers();
if (store) {
barrier.destroy();
@@ -1236,7 +1373,7 @@ void Queue::destroyed()
if (autoDeleteTask) autoDeleteTask = boost::intrusive_ptr<TimerTask>();
notifyDeleted();
{
- Mutex::ScopedLock locker(messageLock);
+ Mutex::ScopedLock lock(messageLock);
observers.clear();
}
}
@@ -1246,8 +1383,8 @@ void Queue::notifyDeleted()
QueueListeners::ListenerSet set;
{
Mutex::ScopedLock locker(messageLock);
- listeners.snapshot(set);
deleted = true;
+ listeners.snapshot(set);
}
set.notifyAll();
}
@@ -1265,6 +1402,7 @@ void Queue::unbind(ExchangeRegistry& exchanges)
void Queue::setPolicy(std::auto_ptr<QueuePolicy> _policy)
{
+ Mutex::ScopedLock locker(messageLock);
policy = _policy;
if (policy.get())
policy->setQueue(this);
@@ -1272,6 +1410,7 @@ void Queue::setPolicy(std::auto_ptr<QueuePolicy> _policy)
const QueuePolicy* Queue::getPolicy()
{
+ Mutex::ScopedLock locker(messageLock);
return policy.get();
}
@@ -1346,12 +1485,15 @@ boost::shared_ptr<Exchange> Queue::getAlternateExchange()
return alternateExchange;
}
-void tryAutoDeleteImpl(Broker& broker, Queue::shared_ptr queue)
+void tryAutoDeleteImpl(Broker& broker, Queue::shared_ptr queue, const std::string& connectionId, const std::string& userId)
{
if (broker.getQueues().destroyIf(queue->getName(),
boost::bind(boost::mem_fn(&Queue::canAutoDelete), queue))) {
QPID_LOG(debug, "Auto-deleting " << queue->getName());
queue->destroyed();
+
+ if (broker.getManagementAgent())
+ broker.getManagementAgent()->raiseEvent(_qmf::EventQueueDelete(connectionId, userId, queue->getName()));
}
}
@@ -1359,9 +1501,11 @@ struct AutoDeleteTask : qpid::sys::TimerTask
{
Broker& broker;
Queue::shared_ptr queue;
+ std::string connectionId;
+ std::string userId;
- AutoDeleteTask(Broker& b, Queue::shared_ptr q, AbsTime fireTime)
- : qpid::sys::TimerTask(fireTime, "DelayedAutoDeletion"), broker(b), queue(q) {}
+ AutoDeleteTask(Broker& b, Queue::shared_ptr q, const std::string& cId, const std::string& uId, AbsTime fireTime)
+ : qpid::sys::TimerTask(fireTime, "DelayedAutoDeletion:"+q->getName()), broker(b), queue(q), connectionId(cId), userId(uId) {}
void fire()
{
@@ -1369,19 +1513,19 @@ struct AutoDeleteTask : qpid::sys::TimerTask
//created, but then became unused again before the task fired;
//in this case ignore this request as there will have already
//been a later task added
- tryAutoDeleteImpl(broker, queue);
+ tryAutoDeleteImpl(broker, queue, connectionId, userId);
}
};
-void Queue::tryAutoDelete(Broker& broker, Queue::shared_ptr queue)
+void Queue::tryAutoDelete(Broker& broker, Queue::shared_ptr queue, const std::string& connectionId, const std::string& userId)
{
if (queue->autoDeleteTimeout && queue->canAutoDelete()) {
AbsTime time(now(), Duration(queue->autoDeleteTimeout * TIME_SEC));
- queue->autoDeleteTask = boost::intrusive_ptr<qpid::sys::TimerTask>(new AutoDeleteTask(broker, queue, time));
+ queue->autoDeleteTask = boost::intrusive_ptr<qpid::sys::TimerTask>(new AutoDeleteTask(broker, queue, connectionId, userId, time));
broker.getClusterTimer().add(queue->autoDeleteTask);
QPID_LOG(debug, "Timed auto-delete for " << queue->getName() << " initiated");
} else {
- tryAutoDeleteImpl(broker, queue);
+ tryAutoDeleteImpl(broker, queue, connectionId, userId);
}
}
@@ -1501,9 +1645,14 @@ Manageable::status_t Queue::ManagementMethod (uint32_t methodId, Args& args, str
{
_qmf::ArgsQueueReroute& rerouteArgs = (_qmf::ArgsQueueReroute&) args;
boost::shared_ptr<Exchange> dest;
- if (rerouteArgs.i_useAltExchange)
+ if (rerouteArgs.i_useAltExchange) {
+ if (!alternateExchange) {
+ status = Manageable::STATUS_PARAMETER_INVALID;
+ etext = "No alternate-exchange defined";
+ break;
+ }
dest = alternateExchange;
- else {
+ } else {
try {
dest = broker->getExchanges().get(rerouteArgs.i_exchange);
} catch(const std::exception&) {
@@ -1530,13 +1679,28 @@ void Queue::query(qpid::types::Variant::Map& results) const
if (allocator) allocator->query(results);
}
+namespace {
+struct After {
+ framing::SequenceNumber seq;
+ After(framing::SequenceNumber s) : seq(s) {}
+ bool operator()(const QueuedMessage& qm) { return qm.position > seq; }
+};
+} // namespace
+
+
void Queue::setPosition(SequenceNumber n) {
Mutex::ScopedLock locker(messageLock);
+ if (n < sequence) {
+ std::deque<QueuedMessage> dequeued;
+ dequeueIf(After(n), dequeued);
+ messages->setPosition(n);
+ }
sequence = n;
QPID_LOG(trace, "Set position to " << sequence << " on " << getName());
}
SequenceNumber Queue::getPosition() {
+ Mutex::ScopedLock locker(messageLock);
return sequence;
}
@@ -1553,8 +1717,12 @@ void Queue::recoveryComplete(ExchangeRegistry& exchanges)
<< "\": exchange does not exist.");
}
//process any pending dequeues
- for_each(pendingDequeues.begin(), pendingDequeues.end(), boost::bind(&Queue::dequeue, this, (TransactionContext*) 0, _1));
- pendingDequeues.clear();
+ std::deque<QueuedMessage> pd;
+ {
+ Mutex::ScopedLock locker(messageLock);
+ pendingDequeues.swap(pd);
+ }
+ for_each(pd.begin(), pd.end(), boost::bind(&Queue::dequeue, this, (TransactionContext*) 0, _1));
}
void Queue::insertSequenceNumbers(const std::string& key)
@@ -1564,10 +1732,10 @@ void Queue::insertSequenceNumbers(const std::string& key)
QPID_LOG(debug, "Inserting sequence numbers as " << key);
}
-/** updates queue observers and state when a message has become available for transfer,
- * expects messageLock to be held
+/** updates queue observers and state when a message has become available for transfer
+ * Requires messageLock be held by caller.
*/
-void Queue::observeEnqueue(const QueuedMessage& m, const Mutex::ScopedLock&)
+void Queue::observeEnqueue(const QueuedMessage& m, const qpid::sys::Mutex::ScopedLock&)
{
for (Observers::iterator i = observers.begin(); i != observers.end(); ++i) {
try {
@@ -1576,10 +1744,6 @@ void Queue::observeEnqueue(const QueuedMessage& m, const Mutex::ScopedLock&)
QPID_LOG(warning, "Exception on notification of enqueue for queue " << getName() << ": " << e.what());
}
}
- if (policy.get()) {
- policy->enqueued(m);
- }
- mgntEnqStats(m.payload, mgmtObject, brokerMgmtObject);
}
void Queue::updateEnqueued(const QueuedMessage& m)
@@ -1587,12 +1751,16 @@ void Queue::updateEnqueued(const QueuedMessage& m)
if (m.payload) {
boost::intrusive_ptr<Message> payload = m.payload;
enqueue(0, payload, true);
- messages->updateAcquired(m);
- if (policy.get()) {
- policy->recoverEnqueued(payload);
+ {
+ Mutex::ScopedLock locker(messageLock);
+ messages->updateAcquired(m);
+ observeEnqueue(m, locker);
+ if (policy.get()) {
+ policy->recoverEnqueued(payload);
+ policy->enqueued(m);
+ }
}
- Mutex::ScopedLock locker(messageLock);
- observeEnqueue(m, locker);
+ mgntEnqStats(m.payload, mgmtObject, brokerMgmtObject);
} else {
QPID_LOG(warning, "Queue informed of enqueued message that has no payload");
}
@@ -1600,10 +1768,16 @@ void Queue::updateEnqueued(const QueuedMessage& m)
bool Queue::isEnqueued(const QueuedMessage& msg)
{
+ Mutex::ScopedLock locker(messageLock);
return !policy.get() || policy->isEnqueued(msg);
}
+// Note: accessing listeners outside of lock is dangerous. Caller must ensure the queue's
+// state is not changed while listeners is referenced.
QueueListeners& Queue::getListeners() { return listeners; }
+
+// Note: accessing messages outside of lock is dangerous. Caller must ensure the queue's
+// state is not changed while messages is referenced.
Messages& Queue::getMessages() { return *messages; }
const Messages& Queue::getMessages() const { return *messages; }
@@ -1616,13 +1790,13 @@ void Queue::checkNotDeleted(const Consumer::shared_ptr& c)
void Queue::addObserver(boost::shared_ptr<QueueObserver> observer)
{
- Mutex::ScopedLock locker(messageLock);
+ Mutex::ScopedLock lock(messageLock);
observers.insert(observer);
}
void Queue::removeObserver(boost::shared_ptr<QueueObserver> observer)
{
- Mutex::ScopedLock locker(messageLock);
+ Mutex::ScopedLock lock(messageLock);
observers.erase(observer);
}
@@ -1685,7 +1859,7 @@ Queue::UsageBarrier::UsageBarrier(Queue& q) : parent(q), count(0) {}
bool Queue::UsageBarrier::acquire()
{
- Monitor::ScopedLock l(parent.messageLock);
+ Monitor::ScopedLock l(parent.messageLock); /** @todo: use a dedicated lock instead of messageLock */
if (parent.deleted) {
return false;
} else {
@@ -1706,3 +1880,6 @@ void Queue::UsageBarrier::destroy()
parent.deleted = true;
while (count) parent.messageLock.wait();
}
+
+}}
+
diff --git a/qpid/cpp/src/qpid/broker/Queue.h b/qpid/cpp/src/qpid/broker/Queue.h
index 282eb691b9..a31e0002ea 100644
--- a/qpid/cpp/src/qpid/broker/Queue.h
+++ b/qpid/cpp/src/qpid/broker/Queue.h
@@ -97,7 +97,8 @@ class Queue : public boost::enable_shared_from_this<Queue>,
const bool autodelete;
MessageStore* store;
const OwnershipToken* owner;
- uint32_t consumerCount;
+ uint32_t consumerCount; // Actually a count of all subscriptions, acquiring or not.
+ uint32_t browserCount; // Count of non-acquiring subscriptions.
OwnershipToken* exclusive;
bool noLocal;
bool persistLastNode;
@@ -107,7 +108,22 @@ class Queue : public boost::enable_shared_from_this<Queue>,
QueueListeners listeners;
std::auto_ptr<Messages> messages;
std::deque<QueuedMessage> pendingDequeues;//used to avoid dequeuing during recovery
- mutable qpid::sys::Mutex consumerLock;
+ /** messageLock is used to keep the Queue's state consistent while processing message
+ * events, such as message dispatch, enqueue, acquire, and dequeue. It must be held
+ * while updating certain members in order to keep these members consistent with
+ * each other:
+ * o messages
+ * o sequence
+ * o policy
+ * o listeners
+ * o allocator
+ * o observeXXX() methods
+ * o observers
+ * o pendingDequeues (TBD: move under separate lock)
+ * o exclusive OwnershipToken (TBD: move under separate lock)
+ * o consumerCount (TBD: move under separate lock)
+ * o Queue::UsageBarrier (TBD: move under separate lock)
+ */
mutable qpid::sys::Monitor messageLock;
mutable qpid::sys::Mutex ownershipLock;
mutable uint64_t persistenceId;
@@ -143,22 +159,23 @@ class Queue : public boost::enable_shared_from_this<Queue>,
bool isExcluded(boost::intrusive_ptr<Message>& msg);
- /** update queue observers, stats, policy, etc when the messages' state changes. Lock
- * must be held by caller */
+ /** update queue observers, stats, policy, etc when the messages' state changes.
+ * messageLock is held by caller */
void observeEnqueue(const QueuedMessage& msg, const sys::Mutex::ScopedLock& lock);
void observeAcquire(const QueuedMessage& msg, const sys::Mutex::ScopedLock& lock);
void observeRequeue(const QueuedMessage& msg, const sys::Mutex::ScopedLock& lock);
void observeDequeue(const QueuedMessage& msg, const sys::Mutex::ScopedLock& lock);
- bool popAndDequeue(QueuedMessage&, const sys::Mutex::ScopedLock& lock);
- // acquire message @ position, return true and set msg if acquire succeeds
- bool acquire(const qpid::framing::SequenceNumber& position, QueuedMessage& msg,
- const sys::Mutex::ScopedLock& held);
+ void observeConsumerAdd( const Consumer&, const sys::Mutex::ScopedLock& lock);
+ void observeConsumerRemove( const Consumer&, const sys::Mutex::ScopedLock& lock);
+ bool popAndDequeue(QueuedMessage&);
+ bool acquire(const qpid::framing::SequenceNumber& position, QueuedMessage& msg);
void forcePersistent(QueuedMessage& msg);
int getEventMode();
void configureImpl(const qpid::framing::FieldTable& settings);
void checkNotDeleted(const Consumer::shared_ptr& c);
void notifyDeleted();
+ void dequeueIf(Messages::Predicate predicate, std::deque<QueuedMessage>& dequeued);
public:
@@ -327,7 +344,7 @@ class Queue : public boost::enable_shared_from_this<Queue>,
* exclusive owner
*/
static Queue::shared_ptr restore(QueueRegistry& queues, framing::Buffer& buffer);
- static void tryAutoDelete(Broker& broker, Queue::shared_ptr);
+ static void tryAutoDelete(Broker& broker, Queue::shared_ptr, const std::string& connectionId, const std::string& userId);
virtual void setExternalQueueStore(ExternalQueueStore* inst);
@@ -355,15 +372,25 @@ class Queue : public boost::enable_shared_from_this<Queue>,
/** Apply f to each Observer on the queue */
template <class F> void eachObserver(F f) {
+ sys::Mutex::ScopedLock l(messageLock);
std::for_each<Observers::iterator, F>(observers.begin(), observers.end(), f);
}
- /** Set the position sequence number for the next message on the queue.
- * Must be >= the current sequence number.
- * Used by cluster to replicate queues.
+ /**
+ * Set the sequence number for the back of the queue, the
+ * next message enqueued will be pos+1.
+ * If pos > getPosition() this creates a gap in the sequence numbers.
+ * if pos < getPosition() the back of the queue is reset to pos,
+ *
+ * The _caller_ must ensure that any messages after pos have been dequeued.
+ *
+ * Used by HA/cluster code for queue replication.
*/
QPID_BROKER_EXTERN void setPosition(framing::SequenceNumber pos);
- /** return current position sequence number for the next message on the queue.
+
+ /**
+ *@return sequence number for the back of the queue. The next message pushed
+ * will be at getPosition+1
*/
QPID_BROKER_EXTERN framing::SequenceNumber getPosition();
QPID_BROKER_EXTERN void addObserver(boost::shared_ptr<QueueObserver>);
diff --git a/qpid/cpp/src/qpid/broker/QueueFlowLimit.cpp b/qpid/cpp/src/qpid/broker/QueueFlowLimit.cpp
index f15bb45c01..14fe5f4022 100644
--- a/qpid/cpp/src/qpid/broker/QueueFlowLimit.cpp
+++ b/qpid/cpp/src/qpid/broker/QueueFlowLimit.cpp
@@ -75,8 +75,8 @@ namespace {
result = v->get<int64_t>();
QPID_LOG(debug, "Got integer value for " << key << ": " << result);
if (result >= 0) return result;
- } else if (v->convertsTo<string>()) {
- string s(v->get<string>());
+ } else if (v->convertsTo<std::string>()) {
+ std::string s(v->get<std::string>());
QPID_LOG(debug, "Got string value for " << key << ": " << s);
std::istringstream convert(s);
if (convert >> result && result >= 0) return result;
diff --git a/qpid/cpp/src/qpid/broker/QueueListeners.cpp b/qpid/cpp/src/qpid/broker/QueueListeners.cpp
index 32c208b073..0338a674cf 100644
--- a/qpid/cpp/src/qpid/broker/QueueListeners.cpp
+++ b/qpid/cpp/src/qpid/broker/QueueListeners.cpp
@@ -79,10 +79,6 @@ void QueueListeners::NotificationSet::notify()
std::for_each(browsers.begin(), browsers.end(), boost::mem_fn(&Consumer::notify));
}
-bool QueueListeners::contains(Consumer::shared_ptr c) const {
- return c->inListeners;
-}
-
void QueueListeners::ListenerSet::notifyAll()
{
std::for_each(listeners.begin(), listeners.end(), boost::mem_fn(&Consumer::notify));
diff --git a/qpid/cpp/src/qpid/broker/QueueListeners.h b/qpid/cpp/src/qpid/broker/QueueListeners.h
index 0659499253..ca844fd47e 100644
--- a/qpid/cpp/src/qpid/broker/QueueListeners.h
+++ b/qpid/cpp/src/qpid/broker/QueueListeners.h
@@ -30,7 +30,7 @@ namespace broker {
/**
* Track and notify components that wish to be notified of messages
* that become available on a queue.
- *
+ *
* None of the methods defined here are protected by locking. However
* the populate method allows a 'snapshot' to be taken of the
* listeners to be notified. NotificationSet::notify() may then be
@@ -61,11 +61,10 @@ class QueueListeners
friend class QueueListeners;
};
- void addListener(Consumer::shared_ptr);
- void removeListener(Consumer::shared_ptr);
+ void addListener(Consumer::shared_ptr);
+ void removeListener(Consumer::shared_ptr);
void populate(NotificationSet&);
void snapshot(ListenerSet&);
- bool contains(Consumer::shared_ptr c) const;
void notifyAll();
template <class F> void eachListener(F f) {
diff --git a/qpid/cpp/src/qpid/broker/QueuePolicy.cpp b/qpid/cpp/src/qpid/broker/QueuePolicy.cpp
index d5b4c1ae86..3978420f4e 100644
--- a/qpid/cpp/src/qpid/broker/QueuePolicy.cpp
+++ b/qpid/cpp/src/qpid/broker/QueuePolicy.cpp
@@ -133,8 +133,8 @@ T getCapacity(const FieldTable& settings, const std::string& key, T defaultValue
result = v->get<T>();
QPID_LOG(debug, "Got integer value for " << key << ": " << result);
if (result >= 0) return result;
- } else if (v->convertsTo<string>()) {
- string s(v->get<string>());
+ } else if (v->convertsTo<std::string>()) {
+ std::string s(v->get<std::string>());
QPID_LOG(debug, "Got string value for " << key << ": " << s);
std::istringstream convert(s);
if (convert >> result && result >= 0 && convert.eof()) return result;
diff --git a/qpid/cpp/src/qpid/broker/QueueRegistry.cpp b/qpid/cpp/src/qpid/broker/QueueRegistry.cpp
index 236d5ae34c..2916d7bb93 100644
--- a/qpid/cpp/src/qpid/broker/QueueRegistry.cpp
+++ b/qpid/cpp/src/qpid/broker/QueueRegistry.cpp
@@ -18,6 +18,7 @@
* under the License.
*
*/
+#include "qpid/broker/Broker.h"
#include "qpid/broker/Queue.h"
#include "qpid/broker/QueueRegistry.h"
#include "qpid/broker/QueueEvents.h"
@@ -46,35 +47,45 @@ QueueRegistry::declare(const string& declareName, bool durable,
definition from persistente
record*/)
{
- RWlock::ScopedWlock locker(lock);
- string name = declareName.empty() ? generateName() : declareName;
- assert(!name.empty());
- QueueMap::iterator i = queues.find(name);
+ Queue::shared_ptr queue;
+ std::pair<Queue::shared_ptr, bool> result;
+ {
+ RWlock::ScopedWlock locker(lock);
+ string name = declareName.empty() ? generateName() : declareName;
+ assert(!name.empty());
+ QueueMap::iterator i = queues.find(name);
- if (i == queues.end()) {
- Queue::shared_ptr queue(new Queue(name, autoDelete, durable ? store : 0, owner, parent, broker));
- if (alternate) {
- queue->setAlternateExchange(alternate);//need to do this *before* create
- alternate->incAlternateUsers();
- }
- if (!recovering) {
- //apply settings & create persistent record if required
- queue->create(arguments);
+ if (i == queues.end()) {
+ queue.reset(new Queue(name, autoDelete, durable ? store : 0, owner, parent, broker));
+ if (alternate) {
+ queue->setAlternateExchange(alternate);//need to do this *before* create
+ alternate->incAlternateUsers();
+ }
+ if (!recovering) {
+ //apply settings & create persistent record if required
+ queue->create(arguments);
+ } else {
+ //i.e. recovering a queue for which we already have a persistent record
+ queue->configure(arguments);
+ }
+ queues[name] = queue;
+ if (lastNode) queue->setLastNodeFailure();
+ result = std::pair<Queue::shared_ptr, bool>(queue, true);
} else {
- //i.e. recovering a queue for which we already have a persistent record
- queue->configure(arguments);
+ result = std::pair<Queue::shared_ptr, bool>(i->second, false);
}
- queues[name] = queue;
- if (lastNode) queue->setLastNodeFailure();
-
- return std::pair<Queue::shared_ptr, bool>(queue, true);
- } else {
- return std::pair<Queue::shared_ptr, bool>(i->second, false);
}
+ if (broker && queue) broker->getConfigurationObservers().queueCreate(queue);
+ return result;
}
-void QueueRegistry::destroyLH (const string& name){
- queues.erase(name);
+void QueueRegistry::destroyLH (const string& name) {
+ QueueMap::iterator i = queues.find(name);
+ if (i != queues.end()) {
+ Queue::shared_ptr q = i->second;
+ queues.erase(i);
+ if (broker) broker->getConfigurationObservers().queueDestroy(q);
+ }
}
void QueueRegistry::destroy (const string& name){
diff --git a/qpid/cpp/src/qpid/ha/ConnectionExcluder.cpp b/qpid/cpp/src/qpid/broker/QueuedMessage.cpp
index 67ad7202d6..d40cc901ff 100644
--- a/qpid/cpp/src/qpid/ha/ConnectionExcluder.cpp
+++ b/qpid/cpp/src/qpid/broker/QueuedMessage.cpp
@@ -18,23 +18,17 @@
* under the License.
*
*/
-
-#include "ConnectionExcluder.h"
-#include "qpid/broker/Connection.h"
-#include <boost/function.hpp>
-#include <sstream>
+#include "QueuedMessage.h"
+#include "Queue.h"
+#include <iostream>
namespace qpid {
-namespace ha {
-
-ConnectionExcluder::ConnectionExcluder() {}
+namespace broker {
-void ConnectionExcluder::opened(broker::Connection& connection) {
- if (!connection.isLink() && !connection.getClientProperties().isSet(ADMIN_TAG))
- throw Exception(
- QPID_MSG("HA: Backup broker rejected connection " << connection.getMgmtId()));
+std::ostream& operator<<(std::ostream& o, const QueuedMessage& qm) {
+ o << (qm.queue ? qm.queue->getName() : std::string()) << "[" << qm.position <<"]";
+ return o;
}
-const std::string ConnectionExcluder::ADMIN_TAG="qpid.ha-admin";
-}} // namespace qpid::ha
+}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/QueuedMessage.h b/qpid/cpp/src/qpid/broker/QueuedMessage.h
index 806da8e720..9d008193a0 100644
--- a/qpid/cpp/src/qpid/broker/QueuedMessage.h
+++ b/qpid/cpp/src/qpid/broker/QueuedMessage.h
@@ -22,6 +22,8 @@
#define _QueuedMessage_
#include "qpid/broker/Message.h"
+#include "BrokerImportExport.h"
+#include <iosfwd>
namespace qpid {
namespace broker {
@@ -47,6 +49,7 @@ inline bool operator<(const QueuedMessage& a, const QueuedMessage& b) {
return a.position < b.position;
}
+QPID_BROKER_EXTERN std::ostream& operator<<(std::ostream&, const QueuedMessage&);
}}
diff --git a/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp b/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp
index d08409695e..858535637a 100644
--- a/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp
+++ b/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp
@@ -144,11 +144,13 @@ RecoverableTransaction::shared_ptr RecoveryManagerImpl::recoverTransaction(const
RecoverableConfig::shared_ptr RecoveryManagerImpl::recoverConfig(framing::Buffer& buffer)
{
string kind;
-
+ uint32_t p = buffer.getPosition();
buffer.getShortString (kind);
- if (kind == "link")
+ buffer.setPosition(p);
+
+ if (Link::isEncodedLink(kind))
return RecoverableConfig::shared_ptr(new RecoverableConfigImpl(Link::decode (links, buffer)));
- else if (kind == "bridge")
+ else if (Bridge::isEncodedBridge(kind))
return RecoverableConfig::shared_ptr(new RecoverableConfigImpl(Bridge::decode (links, buffer)));
return RecoverableConfig::shared_ptr(); // TODO: raise an exception instead
diff --git a/qpid/cpp/src/qpid/broker/SaslAuthenticator.cpp b/qpid/cpp/src/qpid/broker/SaslAuthenticator.cpp
index d7adbd68ab..2d7c820b63 100644
--- a/qpid/cpp/src/qpid/broker/SaslAuthenticator.cpp
+++ b/qpid/cpp/src/qpid/broker/SaslAuthenticator.cpp
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -23,9 +23,11 @@
# include "config.h"
#endif
+#include "qpid/broker/AclModule.h"
#include "qpid/broker/Connection.h"
#include "qpid/log/Statement.h"
#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/FieldValue.h"
#include "qpid/sys/SecuritySettings.h"
#include <boost/format.hpp>
@@ -36,6 +38,7 @@
using qpid::sys::cyrus::CyrusSecurityLayer;
#endif
+using std::string;
using namespace qpid::framing;
using qpid::sys::SecurityLayer;
using qpid::sys::SecuritySettings;
@@ -163,13 +166,17 @@ void SaslAuthenticator::fini(void)
#endif
-std::auto_ptr<SaslAuthenticator> SaslAuthenticator::createAuthenticator(Connection& c, bool isShadow )
+std::auto_ptr<SaslAuthenticator> SaslAuthenticator::createAuthenticator(Connection& c )
{
if (c.getBroker().getOptions().auth) {
- if ( isShadow )
- return std::auto_ptr<SaslAuthenticator>(new NullAuthenticator(c, c.getBroker().getOptions().requireEncrypted));
- else
- return std::auto_ptr<SaslAuthenticator>(new CyrusAuthenticator(c, c.getBroker().getOptions().requireEncrypted));
+ // The cluster creates non-authenticated connections for internal shadow connections
+ // that are never connected to an external client.
+ if ( !c.isAuthenticated() )
+ return std::auto_ptr<SaslAuthenticator>(
+ new NullAuthenticator(c, c.getBroker().getOptions().requireEncrypted));
+ else
+ return std::auto_ptr<SaslAuthenticator>(
+ new CyrusAuthenticator(c, c.getBroker().getOptions().requireEncrypted));
} else {
QPID_LOG(debug, "SASL: No Authentication Performed");
return std::auto_ptr<SaslAuthenticator>(new NullAuthenticator(c, c.getBroker().getOptions().requireEncrypted));
@@ -177,7 +184,7 @@ std::auto_ptr<SaslAuthenticator> SaslAuthenticator::createAuthenticator(Connecti
}
-NullAuthenticator::NullAuthenticator(Connection& c, bool e) : connection(c), client(c.getOutput()),
+NullAuthenticator::NullAuthenticator(Connection& c, bool e) : connection(c), client(c.getOutput()),
realm(c.getBroker().getOptions().realm), encrypt(e) {}
NullAuthenticator::~NullAuthenticator() {}
@@ -213,7 +220,7 @@ void NullAuthenticator::start(const string& mechanism, const string* response)
} else if (i != string::npos) {
//authorization id is first null delimited field
uid = response->substr(0, i);
- }//else not a valid SASL PLAIN response, throw error?
+ }//else not a valid SASL PLAIN response, throw error?
if (!uid.empty()) {
//append realm if it has not already been added
i = uid.find(realm);
@@ -225,7 +232,12 @@ void NullAuthenticator::start(const string& mechanism, const string* response)
}
} else {
connection.setUserId("anonymous");
- }
+ }
+ AclModule* acl = connection.getBroker().getAcl();
+ if (acl && !acl->approveConnection(connection))
+ {
+ throw ConnectionForcedException("User connection denied by configured limit");
+ }
client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, connection.getHeartbeatMax());
}
@@ -239,7 +251,7 @@ std::auto_ptr<SecurityLayer> NullAuthenticator::getSecurityLayer(uint16_t)
#if HAVE_SASL
-CyrusAuthenticator::CyrusAuthenticator(Connection& c, bool _encrypt) :
+CyrusAuthenticator::CyrusAuthenticator(Connection& c, bool _encrypt) :
sasl_conn(0), connection(c), client(c.getOutput()), encrypt(_encrypt)
{
init();
@@ -270,17 +282,17 @@ void CyrusAuthenticator::init()
NULL, /* Callbacks */
0, /* Connection flags */
&sasl_conn);
-
+
if (SASL_OK != code) {
QPID_LOG(error, "SASL: Connection creation failed: [" << code << "] " << sasl_errdetail(sasl_conn));
-
+
// TODO: Change this to an exception signaling
// server error, when one is available
throw ConnectionForcedException("Unable to perform authentication");
}
sasl_security_properties_t secprops;
-
+
//TODO: should the actual SSF values be configurable here?
secprops.min_ssf = encrypt ? 10: 0;
secprops.max_ssf = 256;
@@ -318,14 +330,14 @@ void CyrusAuthenticator::init()
secprops.property_values = 0;
secprops.security_flags = 0; /* or SASL_SEC_NOANONYMOUS etc as appropriate */
/*
- * The nodict flag restricts SASL authentication mechanisms
- * to those that are not susceptible to dictionary attacks.
- * They are:
+ * The nodict flag restricts SASL authentication mechanisms
+ * to those that are not susceptible to dictionary attacks.
+ * They are:
* SRP
* PASSDSS-3DES-1
* EXTERNAL
*/
- if (external.nodict) secprops.security_flags |= SASL_SEC_NODICTIONARY;
+ if (external.nodict) secprops.security_flags |= SASL_SEC_NODICTIONARY;
int result = sasl_setprop(sasl_conn, SASL_SEC_PROPS, &secprops);
if (result != SASL_OK) {
throw framing::InternalErrorException(QPID_MSG("SASL error: " << result));
@@ -370,10 +382,10 @@ void CyrusAuthenticator::getMechanisms(Array& mechanisms)
"", separator, "",
&list, &list_len,
&count);
-
+
if (SASL_OK != code) {
QPID_LOG(info, "SASL: Mechanism listing failed: " << sasl_errdetail(sasl_conn));
-
+
// TODO: Change this to an exception signaling
// server error, when one is available
throw ConnectionForcedException("Mechanism listing failed");
@@ -381,17 +393,17 @@ void CyrusAuthenticator::getMechanisms(Array& mechanisms)
string mechanism;
unsigned int start;
unsigned int end;
-
+
QPID_LOG(info, "SASL: Mechanism list: " << list);
-
+
end = 0;
do {
start = end;
-
+
// Seek to end of next mechanism
while (end < list_len && separator[0] != list[end])
end++;
-
+
// Record the mechanism
mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value(string(list, start, end - start))));
end++;
@@ -403,20 +415,20 @@ void CyrusAuthenticator::start(const string& mechanism, const string* response)
{
const char *challenge;
unsigned int challenge_len;
-
+
// This should be at same debug level as mech list in getMechanisms().
QPID_LOG(info, "SASL: Starting authentication with mechanism: " << mechanism);
int code = sasl_server_start(sasl_conn,
mechanism.c_str(),
(response ? response->c_str() : 0), (response ? response->size() : 0),
&challenge, &challenge_len);
-
+
processAuthenticationStep(code, challenge, challenge_len);
qmf::org::apache::qpid::broker::Connection* cnxMgmt = connection.getMgmtObject();
- if ( cnxMgmt )
+ if ( cnxMgmt )
cnxMgmt->set_saslMechanism(mechanism);
}
-
+
void CyrusAuthenticator::step(const string& response)
{
const char *challenge;
@@ -438,10 +450,17 @@ void CyrusAuthenticator::processAuthenticationStep(int code, const char *challen
// authentication failure, when one is available
throw ConnectionForcedException("Authenticated username unavailable");
}
- QPID_LOG(info, connection.getMgmtId() << " SASL: Authentication succeeded for: " << uid);
connection.setUserId(uid);
+ AclModule* acl = connection.getBroker().getAcl();
+ if (acl && !acl->approveConnection(connection))
+ {
+ throw ConnectionForcedException("User connection denied by configured limit");
+ }
+
+ QPID_LOG(info, connection.getMgmtId() << " SASL: Authentication succeeded for: " << uid);
+
client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, connection.getHeartbeatMax());
} else if (SASL_CONTINUE == code) {
string challenge_str(challenge, challenge_len);
@@ -489,7 +508,7 @@ std::auto_ptr<SecurityLayer> CyrusAuthenticator::getSecurityLayer(uint16_t maxFr
securityLayer = std::auto_ptr<SecurityLayer>(new CyrusSecurityLayer(sasl_conn, maxFrameSize));
}
qmf::org::apache::qpid::broker::Connection* cnxMgmt = connection.getMgmtObject();
- if ( cnxMgmt )
+ if ( cnxMgmt )
cnxMgmt->set_saslSsf(ssf);
return securityLayer;
}
diff --git a/qpid/cpp/src/qpid/broker/SaslAuthenticator.h b/qpid/cpp/src/qpid/broker/SaslAuthenticator.h
index 4e5d43214c..e5ecc9f6ec 100644
--- a/qpid/cpp/src/qpid/broker/SaslAuthenticator.h
+++ b/qpid/cpp/src/qpid/broker/SaslAuthenticator.h
@@ -54,7 +54,7 @@ public:
static void init(const std::string& saslName, std::string const & saslConfigPath );
static void fini(void);
- static std::auto_ptr<SaslAuthenticator> createAuthenticator(Connection& connection, bool isShadow);
+ static std::auto_ptr<SaslAuthenticator> createAuthenticator(Connection& connection);
virtual void callUserIdCallbacks() { }
};
diff --git a/qpid/cpp/src/qpid/broker/SecureConnectionFactory.cpp b/qpid/cpp/src/qpid/broker/SecureConnectionFactory.cpp
index 754b443c22..757f6efc59 100644
--- a/qpid/cpp/src/qpid/broker/SecureConnectionFactory.cpp
+++ b/qpid/cpp/src/qpid/broker/SecureConnectionFactory.cpp
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -41,11 +41,6 @@ SecureConnectionFactory::SecureConnectionFactory(Broker& b) : broker(b) {}
sys::ConnectionCodec*
SecureConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, const std::string& id,
const SecuritySettings& external) {
- if (broker.getConnectionCounter().allowConnection())
- {
- QPID_LOG(error, "Client max connection count limit exceeded: " << broker.getOptions().maxConnections << " connection refused");
- return 0;
- }
if (v == ProtocolVersion(0, 10)) {
SecureConnectionPtr sc(new SecureConnection());
CodecPtr c(new amqp_0_10::Connection(out, id, false));
@@ -71,5 +66,5 @@ SecureConnectionFactory::create(sys::OutputControl& out, const std::string& id,
return sc.release();
}
-
+
}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/SemanticState.cpp b/qpid/cpp/src/qpid/broker/SemanticState.cpp
index 64924bdd4c..9a84db547c 100644
--- a/qpid/cpp/src/qpid/broker/SemanticState.cpp
+++ b/qpid/cpp/src/qpid/broker/SemanticState.cpp
@@ -72,7 +72,8 @@ SemanticState::SemanticState(DeliveryAdapter& da, SessionContext& ss)
dtxSelected(false),
authMsg(getSession().getBroker().getOptions().auth && !getSession().getConnection().isUserProxyAuth()),
userID(getSession().getConnection().getUserId()),
- closeComplete(false)
+ closeComplete(false),
+ connectionId(getSession().getConnection().getUrl())
{}
SemanticState::~SemanticState() {
@@ -142,6 +143,7 @@ bool SemanticState::cancel(const string& tag)
DeliveryRecords::iterator removed =
remove_if(unacked.begin(), unacked.end(), bind(&DeliveryRecord::isRedundant, _1));
unacked.erase(removed, unacked.end());
+ getSession().setUnackedCount(unacked.size());
return true;
} else {
return false;
@@ -270,6 +272,7 @@ void SemanticState::checkDtxTimeout()
void SemanticState::record(const DeliveryRecord& delivery)
{
unacked.push_back(delivery);
+ getSession().setUnackedCount(unacked.size());
}
const std::string QPID_SYNC_FREQUENCY("qpid.sync_frequency");
@@ -426,7 +429,7 @@ void SemanticState::cancel(ConsumerImpl::shared_ptr c)
if(queue) {
queue->cancel(c);
if (queue->canAutoDelete() && !queue->hasExclusiveOwner()) {
- Queue::tryAutoDelete(session.getBroker(), queue);
+ Queue::tryAutoDelete(session.getBroker(), queue, connectionId, userID);
}
}
c->cancel();
@@ -555,6 +558,7 @@ void SemanticState::recover(bool requeue)
//w.r.t id is lost
sort(unacked.begin(), unacked.end());
}
+ getSession().setUnackedCount(unacked.size());
}
void SemanticState::deliver(DeliveryRecord& msg, bool sync)
@@ -712,6 +716,7 @@ void SemanticState::release(DeliveryId first, DeliveryId last, bool setRedeliver
DeliveryRecords::iterator removed =
remove_if(range.start, range.end, bind(&DeliveryRecord::isRedundant, _1));
unacked.erase(removed, range.end);
+ getSession().setUnackedCount(unacked.size());
}
void SemanticState::reject(DeliveryId first, DeliveryId last)
@@ -723,6 +728,7 @@ void SemanticState::reject(DeliveryId first, DeliveryId last)
if (i->isRedundant()) i = unacked.erase(i);
else i++;
}
+ getSession().setUnackedCount(unacked.size());
}
bool SemanticState::ConsumerImpl::doOutput()
@@ -810,6 +816,7 @@ void SemanticState::accepted(const SequenceSet& commands) {
(TransactionContext*) 0)));
unacked.erase(removed, unacked.end());
}
+ getSession().setUnackedCount(unacked.size());
}
void SemanticState::completed(const SequenceSet& commands) {
@@ -819,6 +826,7 @@ void SemanticState::completed(const SequenceSet& commands) {
bind(&SemanticState::complete, this, _1)));
unacked.erase(removed, unacked.end());
requestDispatch();
+ getSession().setUnackedCount(unacked.size());
}
void SemanticState::attached()
diff --git a/qpid/cpp/src/qpid/broker/SemanticState.h b/qpid/cpp/src/qpid/broker/SemanticState.h
index e5e1d2da16..15928ce599 100644
--- a/qpid/cpp/src/qpid/broker/SemanticState.h
+++ b/qpid/cpp/src/qpid/broker/SemanticState.h
@@ -146,6 +146,8 @@ class SemanticState : private boost::noncopyable {
std::string getResumeId() const { return resumeId; };
const std::string& getTag() const { return tag; }
uint64_t getResumeTtl() const { return resumeTtl; }
+ uint32_t getDeliveryCount() const { return deliveryCount; }
+ void setDeliveryCount(uint32_t _deliveryCount) { deliveryCount = _deliveryCount; }
const framing::FieldTable& getArguments() const { return arguments; }
SemanticState& getParent() { return *parent; }
@@ -180,6 +182,8 @@ class SemanticState : private boost::noncopyable {
const bool authMsg;
const std::string userID;
bool closeComplete;
+ //needed for queue delete events in auto-delete:
+ const std::string connectionId;
void route(boost::intrusive_ptr<Message> msg, Deliverable& strategy);
void checkDtxTimeout();
diff --git a/qpid/cpp/src/qpid/broker/SessionAdapter.cpp b/qpid/cpp/src/qpid/broker/SessionAdapter.cpp
index 4aad46f782..7469fb3af3 100644
--- a/qpid/cpp/src/qpid/broker/SessionAdapter.cpp
+++ b/qpid/cpp/src/qpid/broker/SessionAdapter.cpp
@@ -21,8 +21,9 @@
#include "qpid/Exception.h"
#include "qpid/framing/reply_exceptions.h"
#include "qpid/framing/enum.h"
-#include "qpid/log/Statement.h"
+#include "qpid/framing/FieldValue.h"
#include "qpid/framing/SequenceSet.h"
+#include "qpid/log/Statement.h"
#include "qpid/management/ManagementAgent.h"
#include "qpid/broker/SessionState.h"
#include "qmf/org/apache/qpid/broker/EventExchangeDeclare.h"
@@ -40,6 +41,8 @@
namespace qpid {
namespace broker {
+using std::string;
+
using namespace qpid;
using namespace qpid::framing;
using namespace qpid::framing::dtx;
@@ -73,18 +76,12 @@ void SessionAdapter::ExchangeHandlerImpl::declare(const string& exchange, const
if(passive){
AclModule* acl = getBroker().getAcl();
if (acl) {
- //TODO: why does a passive declare require create
- //permission? The purpose of the passive flag is to state
- //that the exchange should *not* created. For
- //authorisation a passive declare is similar to
- //exchange-query.
std::map<acl::Property, std::string> params;
params.insert(make_pair(acl::PROP_TYPE, type));
params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange));
- params.insert(make_pair(acl::PROP_PASSIVE, _TRUE));
params.insert(make_pair(acl::PROP_DURABLE, durable ? _TRUE : _FALSE));
- if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_EXCHANGE,exchange,&params) )
- throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange create request from " << getConnection().getUserId()));
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_EXCHANGE,exchange,&params) )
+ throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange access request from " << getConnection().getUserId()));
}
Exchange::shared_ptr actual(getBroker().getExchanges().get(exchange));
checkType(actual, type);
@@ -209,7 +206,10 @@ ExchangeBoundResult SessionAdapter::ExchangeHandlerImpl::bound(const std::string
}
}
-SessionAdapter::QueueHandlerImpl::QueueHandlerImpl(SemanticState& session) : HandlerHelper(session), broker(getBroker())
+SessionAdapter::QueueHandlerImpl::QueueHandlerImpl(SemanticState& session)
+ : HandlerHelper(session), broker(getBroker()),
+ //record connection id and userid for deleting exclsuive queues after session has ended:
+ connectionId(getConnection().getUrl()), userId(getConnection().getUserId())
{}
@@ -228,7 +228,7 @@ void SessionAdapter::QueueHandlerImpl::destroyExclusiveQueues()
Queue::shared_ptr q(exclusiveQueues.front());
q->releaseExclusiveOwnership();
if (q->canAutoDelete()) {
- Queue::tryAutoDelete(broker, q);
+ Queue::tryAutoDelete(broker, q, connectionId, userId);
}
exclusiveQueues.erase(exclusiveQueues.begin());
}
@@ -274,22 +274,16 @@ void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string&
if (passive && !name.empty()) {
AclModule* acl = getBroker().getAcl();
if (acl) {
- //TODO: why does a passive declare require create
- //permission? The purpose of the passive flag is to state
- //that the queue should *not* created. For
- //authorisation a passive declare is similar to
- //queue-query (or indeed a qmf query).
std::map<acl::Property, std::string> params;
params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange));
- params.insert(make_pair(acl::PROP_PASSIVE, _TRUE));
params.insert(make_pair(acl::PROP_DURABLE, std::string(durable ? _TRUE : _FALSE)));
params.insert(make_pair(acl::PROP_EXCLUSIVE, std::string(exclusive ? _TRUE : _FALSE)));
params.insert(make_pair(acl::PROP_AUTODELETE, std::string(autoDelete ? _TRUE : _FALSE)));
params.insert(make_pair(acl::PROP_POLICYTYPE, arguments.getAsString("qpid.policy_type")));
params.insert(make_pair(acl::PROP_MAXQUEUECOUNT, boost::lexical_cast<string>(arguments.getAsInt("qpid.max_count"))));
params.insert(make_pair(acl::PROP_MAXQUEUESIZE, boost::lexical_cast<string>(arguments.getAsInt64("qpid.max_size"))));
- if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_QUEUE,name,&params) )
- throw UnauthorizedAccessException(QPID_MSG("ACL denied queue create request from " << getConnection().getUserId()));
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_ACCESS,acl::OBJ_QUEUE,name,&params) )
+ throw UnauthorizedAccessException(QPID_MSG("ACL denied queue access request from " << getConnection().getUserId()));
}
queue = getQueue(name);
//TODO: check alternate-exchange is as expected
@@ -409,6 +403,7 @@ SessionAdapter::MessageHandlerImpl::subscribe(const string& queueName,
if(!destination.empty() && state.exists(destination))
throw NotAllowedException(QPID_MSG("Consumer tags must be unique"));
+ // We allow browsing (acquireMode == 1) of exclusive queues, this is required by HA.
if (queue->hasExclusiveOwner() && !queue->isExclusiveOwner(&session) && acquireMode == 0)
throw ResourceLockedException(QPID_MSG("Cannot subscribe to exclusive queue "
<< queue->getName()));
@@ -548,13 +543,6 @@ void SessionAdapter::TxHandlerImpl::rollback()
state.rollback();
}
-std::string SessionAdapter::DtxHandlerImpl::convert(const framing::Xid& xid)
-{
- std::string encoded;
- encode(xid, encoded);
- return encoded;
-}
-
void SessionAdapter::DtxHandlerImpl::select()
{
state.selectDtx();
@@ -566,7 +554,7 @@ XaResult SessionAdapter::DtxHandlerImpl::end(const Xid& xid,
{
try {
if (fail) {
- state.endDtx(convert(xid), true);
+ state.endDtx(DtxManager::convert(xid), true);
if (suspend) {
throw CommandInvalidException(QPID_MSG("End and suspend cannot both be set."));
} else {
@@ -574,9 +562,9 @@ XaResult SessionAdapter::DtxHandlerImpl::end(const Xid& xid,
}
} else {
if (suspend) {
- state.suspendDtx(convert(xid));
+ state.suspendDtx(DtxManager::convert(xid));
} else {
- state.endDtx(convert(xid), false);
+ state.endDtx(DtxManager::convert(xid), false);
}
return XaResult(XA_STATUS_XA_OK);
}
@@ -594,9 +582,9 @@ XaResult SessionAdapter::DtxHandlerImpl::start(const Xid& xid,
}
try {
if (resume) {
- state.resumeDtx(convert(xid));
+ state.resumeDtx(DtxManager::convert(xid));
} else {
- state.startDtx(convert(xid), getBroker().getDtxManager(), join);
+ state.startDtx(DtxManager::convert(xid), getBroker().getDtxManager(), join);
}
return XaResult(XA_STATUS_XA_OK);
} catch (const DtxTimeoutException& /*e*/) {
@@ -607,7 +595,7 @@ XaResult SessionAdapter::DtxHandlerImpl::start(const Xid& xid,
XaResult SessionAdapter::DtxHandlerImpl::prepare(const Xid& xid)
{
try {
- bool ok = getBroker().getDtxManager().prepare(convert(xid));
+ bool ok = getBroker().getDtxManager().prepare(DtxManager::convert(xid));
return XaResult(ok ? XA_STATUS_XA_OK : XA_STATUS_XA_RBROLLBACK);
} catch (const DtxTimeoutException& /*e*/) {
return XaResult(XA_STATUS_XA_RBTIMEOUT);
@@ -618,7 +606,7 @@ XaResult SessionAdapter::DtxHandlerImpl::commit(const Xid& xid,
bool onePhase)
{
try {
- bool ok = getBroker().getDtxManager().commit(convert(xid), onePhase);
+ bool ok = getBroker().getDtxManager().commit(DtxManager::convert(xid), onePhase);
return XaResult(ok ? XA_STATUS_XA_OK : XA_STATUS_XA_RBROLLBACK);
} catch (const DtxTimeoutException& /*e*/) {
return XaResult(XA_STATUS_XA_RBTIMEOUT);
@@ -629,7 +617,7 @@ XaResult SessionAdapter::DtxHandlerImpl::commit(const Xid& xid,
XaResult SessionAdapter::DtxHandlerImpl::rollback(const Xid& xid)
{
try {
- getBroker().getDtxManager().rollback(convert(xid));
+ getBroker().getDtxManager().rollback(DtxManager::convert(xid));
return XaResult(XA_STATUS_XA_OK);
} catch (const DtxTimeoutException& /*e*/) {
return XaResult(XA_STATUS_XA_RBTIMEOUT);
@@ -659,7 +647,7 @@ void SessionAdapter::DtxHandlerImpl::forget(const Xid& xid)
DtxGetTimeoutResult SessionAdapter::DtxHandlerImpl::getTimeout(const Xid& xid)
{
- uint32_t timeout = getBroker().getDtxManager().getTimeout(convert(xid));
+ uint32_t timeout = getBroker().getDtxManager().getTimeout(DtxManager::convert(xid));
return DtxGetTimeoutResult(timeout);
}
@@ -667,7 +655,7 @@ DtxGetTimeoutResult SessionAdapter::DtxHandlerImpl::getTimeout(const Xid& xid)
void SessionAdapter::DtxHandlerImpl::setTimeout(const Xid& xid,
uint32_t timeout)
{
- getBroker().getDtxManager().setTimeout(convert(xid), timeout);
+ getBroker().getDtxManager().setTimeout(DtxManager::convert(xid), timeout);
}
diff --git a/qpid/cpp/src/qpid/broker/SessionAdapter.h b/qpid/cpp/src/qpid/broker/SessionAdapter.h
index 8987c4812f..3cc745f96c 100644
--- a/qpid/cpp/src/qpid/broker/SessionAdapter.h
+++ b/qpid/cpp/src/qpid/broker/SessionAdapter.h
@@ -121,6 +121,9 @@ class Queue;
{
Broker& broker;
std::vector< boost::shared_ptr<Queue> > exclusiveQueues;
+ //connectionId and userId are needed for queue-delete events for auto deleted, exclusive queues
+ std::string connectionId;
+ std::string userId;
public:
QueueHandlerImpl(SemanticState& session);
@@ -226,10 +229,8 @@ class Queue;
void rollback();
};
- class DtxHandlerImpl : public DtxHandler, public HandlerHelper, private framing::StructHelper
+ class DtxHandlerImpl : public DtxHandler, public HandlerHelper
{
- std::string convert(const framing::Xid& xid);
-
public:
DtxHandlerImpl(SemanticState& session) : HandlerHelper(session) {}
diff --git a/qpid/cpp/src/qpid/broker/SessionContext.h b/qpid/cpp/src/qpid/broker/SessionContext.h
index 253ce8dcf2..ee98da1878 100644
--- a/qpid/cpp/src/qpid/broker/SessionContext.h
+++ b/qpid/cpp/src/qpid/broker/SessionContext.h
@@ -47,6 +47,7 @@ class SessionContext : public OwnershipToken, public sys::OutputControl
virtual uint16_t getChannel() const = 0;
virtual const SessionId& getSessionId() const = 0;
virtual void addPendingExecutionSync() = 0;
+ virtual void setUnackedCount(uint64_t) {}
};
}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/SessionHandler.cpp b/qpid/cpp/src/qpid/broker/SessionHandler.cpp
index 752fa55535..23fa2ee0ca 100644
--- a/qpid/cpp/src/qpid/broker/SessionHandler.cpp
+++ b/qpid/cpp/src/qpid/broker/SessionHandler.cpp
@@ -35,23 +35,39 @@ SessionHandler::SessionHandler(Connection& c, ChannelId ch)
: amqp_0_10::SessionHandler(&c.getOutput(), ch),
connection(c),
proxy(out),
- clusterOrderProxy(c.getClusterOrderOutput() ? new SetChannelProxy(ch, c.getClusterOrderOutput()) : 0)
+ clusterOrderProxy(c.getClusterOrderOutput() ?
+ new SetChannelProxy(ch, c.getClusterOrderOutput()) : 0)
{}
SessionHandler::~SessionHandler() {}
-void SessionHandler::connectionException(framing::connection::CloseCode code, const std::string& msg) {
+void SessionHandler::connectionException(
+ framing::connection::CloseCode code, const std::string& msg)
+{
// NOTE: must tell the error listener _before_ calling connection.close()
- if (connection.getErrorListener()) connection.getErrorListener()->connectionError(msg);
+ if (connection.getErrorListener())
+ connection.getErrorListener()->connectionError(msg);
+ if (errorListener)
+ errorListener->connectionException(code, msg);
connection.close(code, msg);
}
-void SessionHandler::channelException(framing::session::DetachCode, const std::string& msg) {
- if (connection.getErrorListener()) connection.getErrorListener()->sessionError(getChannel(), msg);
+void SessionHandler::channelException(
+ framing::session::DetachCode code, const std::string& msg)
+{
+ if (connection.getErrorListener())
+ connection.getErrorListener()->sessionError(getChannel(), msg);
+ if (errorListener)
+ errorListener->channelException(code, msg);
}
-void SessionHandler::executionException(framing::execution::ErrorCode, const std::string& msg) {
- if (connection.getErrorListener()) connection.getErrorListener()->sessionError(getChannel(), msg);
+void SessionHandler::executionException(
+ framing::execution::ErrorCode code, const std::string& msg)
+{
+ if (connection.getErrorListener())
+ connection.getErrorListener()->sessionError(getChannel(), msg);
+ if (errorListener)
+ errorListener->executionException(code, msg);
}
ConnectionState& SessionHandler::getConnection() { return connection; }
@@ -64,6 +80,7 @@ void SessionHandler::handleDetach() {
if (session.get())
connection.getBroker().getSessionManager().detach(session);
assert(!session.get());
+ if (errorListener) errorListener->detach();
connection.closeChannel(channel.get());
}
diff --git a/qpid/cpp/src/qpid/broker/SessionHandler.h b/qpid/cpp/src/qpid/broker/SessionHandler.h
index 8cd5072574..ab87cf41a4 100644
--- a/qpid/cpp/src/qpid/broker/SessionHandler.h
+++ b/qpid/cpp/src/qpid/broker/SessionHandler.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -25,6 +25,7 @@
#include "qpid/amqp_0_10/SessionHandler.h"
#include "qpid/broker/SessionHandler.h"
#include "qpid/framing/AMQP_ClientProxy.h"
+#include <boost/shared_ptr.hpp>
namespace qpid {
class SessionState;
@@ -42,6 +43,21 @@ class SessionState;
*/
class SessionHandler : public amqp_0_10::SessionHandler {
public:
+ class ErrorListener {
+ public:
+ virtual ~ErrorListener() {}
+ virtual void connectionException(
+ framing::connection::CloseCode code, const std::string& msg) = 0;
+ virtual void channelException(
+ framing::session::DetachCode, const std::string& msg) = 0;
+ virtual void executionException(
+ framing::execution::ErrorCode, const std::string& msg) = 0;
+ /** Called when it is safe to delete the ErrorListener. */
+ virtual void detach() = 0;
+ };
+
+ /**
+ *@param e must not be deleted until ErrorListener::detach has been called */
SessionHandler(Connection&, framing::ChannelId);
~SessionHandler();
@@ -61,7 +77,7 @@ class SessionHandler : public amqp_0_10::SessionHandler {
* This proxy is for sending such commands. In a clustered broker it will take steps
* to synchronize command order across the cluster. In a stand-alone broker
* it is just a synonym for getProxy()
- */
+ */
framing::AMQP_ClientProxy& getClusterOrderProxy() {
return clusterOrderProxy.get() ? *clusterOrderProxy : proxy;
}
@@ -70,6 +86,8 @@ class SessionHandler : public amqp_0_10::SessionHandler {
void attached(const std::string& name);//used by 'pushing' inter-broker bridges
void attachAs(const std::string& name);//used by 'pulling' inter-broker bridges
+ void setErrorListener(boost::shared_ptr<ErrorListener> e) { errorListener = e; }
+
protected:
virtual void setState(const std::string& sessionName, bool force);
virtual qpid::SessionState* getState();
@@ -91,6 +109,7 @@ class SessionHandler : public amqp_0_10::SessionHandler {
framing::AMQP_ClientProxy proxy;
std::auto_ptr<SessionState> session;
std::auto_ptr<SetChannelProxy> clusterOrderProxy;
+ boost::shared_ptr<ErrorListener> errorListener;
};
}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/SessionState.cpp b/qpid/cpp/src/qpid/broker/SessionState.cpp
index 99407bc3a6..cc02d9ec94 100644
--- a/qpid/cpp/src/qpid/broker/SessionState.cpp
+++ b/qpid/cpp/src/qpid/broker/SessionState.cpp
@@ -156,7 +156,7 @@ ManagementObject* SessionState::GetManagementObject (void) const
Manageable::status_t SessionState::ManagementMethod (uint32_t methodId,
Args& /*args*/,
- string& /*text*/)
+ std::string& /*text*/)
{
Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
diff --git a/qpid/cpp/src/qpid/broker/SessionState.h b/qpid/cpp/src/qpid/broker/SessionState.h
index 8db232a2d6..a8ff7feff9 100644
--- a/qpid/cpp/src/qpid/broker/SessionState.h
+++ b/qpid/cpp/src/qpid/broker/SessionState.h
@@ -126,6 +126,11 @@ class SessionState : public qpid::SessionState,
// the SessionState of a received Execution.Sync command.
void addPendingExecutionSync();
+ void setUnackedCount(uint64_t count) {
+ if (mgmtObject)
+ mgmtObject->set_unackedMessages(count);
+ }
+
// Used to delay creation of management object for sessions
// belonging to inter-broker bridges
void addManagementObject();
diff --git a/qpid/cpp/src/qpid/broker/System.cpp b/qpid/cpp/src/qpid/broker/System.cpp
index 8cd2edda76..fa8df6406b 100644
--- a/qpid/cpp/src/qpid/broker/System.cpp
+++ b/qpid/cpp/src/qpid/broker/System.cpp
@@ -37,7 +37,6 @@ System::System (string _dataDir, Broker* broker) : mgmtObject(0)
if (agent != 0)
{
- framing::Uuid systemId;
if (_dataDir.empty ())
{
@@ -66,14 +65,13 @@ System::System (string _dataDir, Broker* broker) : mgmtObject(0)
}
mgmtObject = new _qmf::System(agent, this, types::Uuid(systemId.c_array()));
- std::string sysname, nodename, release, version, machine;
- qpid::sys::SystemInfo::getSystemId (sysname,
- nodename,
+ qpid::sys::SystemInfo::getSystemId (osName,
+ nodeName,
release,
version,
machine);
- mgmtObject->set_osName (sysname);
- mgmtObject->set_nodeName (nodename);
+ mgmtObject->set_osName (osName);
+ mgmtObject->set_nodeName (nodeName);
mgmtObject->set_release (release);
mgmtObject->set_version (version);
mgmtObject->set_machine (machine);
diff --git a/qpid/cpp/src/qpid/broker/System.h b/qpid/cpp/src/qpid/broker/System.h
index 0fc2c2bd88..6847c662ae 100644
--- a/qpid/cpp/src/qpid/broker/System.h
+++ b/qpid/cpp/src/qpid/broker/System.h
@@ -21,6 +21,7 @@
//
#include "qpid/management/Manageable.h"
+#include "qpid/framing/Uuid.h"
#include "qmf/org/apache/qpid/broker/System.h"
#include <boost/shared_ptr.hpp>
#include <string>
@@ -35,6 +36,8 @@ class System : public management::Manageable
private:
qmf::org::apache::qpid::broker::System* mgmtObject;
+ framing::Uuid systemId;
+ std::string osName, nodeName, release, version, machine;
public:
@@ -44,6 +47,20 @@ class System : public management::Manageable
management::ManagementObject* GetManagementObject (void) const
{ return mgmtObject; }
+
+
+ /** Persistent UUID assigned by the management system to this broker. */
+ framing::Uuid getSystemId() const { return systemId; }
+ /** Returns the OS name; e.g., GNU/Linux or Windows */
+ std::string getOsName() const { return osName; }
+ /** Returns the node name. Usually the same as the host name. */
+ std::string getNodeName() const { return nodeName; }
+ /** Returns the OS release identifier. */
+ std::string getRelease() const { return release; }
+ /** Returns the OS release version (kernel, build, sp, etc.) */
+ std::string getVersion() const { return version; }
+ /** Returns the hardware type. */
+ std::string getMachine() const { return machine; }
};
}}
diff --git a/qpid/cpp/src/qpid/broker/TopicExchange.cpp b/qpid/cpp/src/qpid/broker/TopicExchange.cpp
index dd3ec13019..c11389bb17 100644
--- a/qpid/cpp/src/qpid/broker/TopicExchange.cpp
+++ b/qpid/cpp/src/qpid/broker/TopicExchange.cpp
@@ -32,20 +32,8 @@ using namespace qpid::sys;
using namespace std;
namespace _qmf = qmf::org::apache::qpid::broker;
-
-// TODO aconway 2006-09-20: More efficient matching algorithm.
-// Areas for improvement:
-// - excessive string copying: should be 0 copy, match from original buffer.
-// - match/lookup: use descision tree or other more efficient structure.
-
-namespace
-{
-const std::string STAR("*");
-const std::string HASH("#");
-}
-
// iterator for federation ReOrigin bind operation
-class TopicExchange::ReOriginIter : public TopicExchange::BindingNode::TreeIterator {
+class TopicExchange::ReOriginIter : public BindingNode::TreeIterator {
public:
ReOriginIter() {};
~ReOriginIter() {};
@@ -61,7 +49,7 @@ public:
// match iterator used by route(): builds BindingList of all unique queues
// that match the routing key.
-class TopicExchange::BindingsFinderIter : public TopicExchange::BindingNode::TreeIterator {
+class TopicExchange::BindingsFinderIter : public BindingNode::TreeIterator {
public:
BindingsFinderIter(BindingList &bl) : b(bl) {};
~BindingsFinderIter() {};
@@ -85,7 +73,7 @@ public:
// Iterator to visit all bindings until a given queue is found
-class TopicExchange::QueueFinderIter : public TopicExchange::BindingNode::TreeIterator {
+class TopicExchange::QueueFinderIter : public BindingNode::TreeIterator {
public:
QueueFinderIter(Queue::shared_ptr queue) : queue(queue), found(false) {};
~QueueFinderIter() {};
@@ -107,58 +95,7 @@ public:
};
-// Iterate over a string of '.'-separated tokens.
-struct TopicExchange::TokenIterator {
- typedef pair<const char*,const char*> Token;
-
- TokenIterator(const char* b, const char* e) : end(e), token(make_pair(b, find(b,e,'.'))) {}
-
- TokenIterator(const string& key) : end(&key[0]+key.size()), token(make_pair(&key[0], find(&key[0],end,'.'))) {}
-
- bool finished() const { return !token.first; }
-
- void next() {
- if (token.second == end)
- token.first = token.second = 0;
- else {
- token.first=token.second+1;
- token.second=(find(token.first, end, '.'));
- }
- }
-
- void pop(string &top) {
- ptrdiff_t l = len();
- if (l) {
- top.assign(token.first, l);
- } else top.clear();
- next();
- }
-
- bool match1(char c) const {
- return token.second==token.first+1 && *token.first == c;
- }
-
- bool match(const Token& token2) const {
- ptrdiff_t l=len();
- return l == token2.second-token2.first &&
- strncmp(token.first, token2.first, l) == 0;
- }
-
- bool match(const string& str) const {
- ptrdiff_t l=len();
- return l == ptrdiff_t(str.size()) &&
- str.compare(0, l, token.first, l) == 0;
- }
-
- ptrdiff_t len() const { return token.second - token.first; }
-
-
- const char* end;
- Token token;
-};
-
-
-class TopicExchange::Normalizer : public TopicExchange::TokenIterator {
+class TopicExchange::Normalizer : public TokenIterator {
public:
Normalizer(string& p)
: TokenIterator(&p[0], &p[0]+p.size()), pattern(p)
@@ -230,7 +167,7 @@ bool TopicExchange::bind(Queue::shared_ptr queue, const string& routingKey, cons
if (args == 0 || fedOp.empty() || fedOp == fedOpBind) {
RWlock::ScopedWlock l(lock);
- BindingKey *bk = bindingTree.addBindingKey(routingPattern);
+ BindingKey *bk = bindingTree.add(routingPattern);
if (bk) {
Binding::vector& qv(bk->bindingVector);
Binding::vector::iterator q;
@@ -324,7 +261,7 @@ bool TopicExchange::deleteBinding(Queue::shared_ptr queue,
nBindings--;
if(qv.empty()) {
- bindingTree.removeBindingKey(routingKey);
+ bindingTree.remove(routingKey);
}
if (mgmtExchange != 0) {
mgmtExchange->dec_bindingCount();
@@ -340,7 +277,7 @@ bool TopicExchange::deleteBinding(Queue::shared_ptr queue,
TopicExchange::BindingKey *TopicExchange::getQueueBinding(Queue::shared_ptr queue, const string& pattern)
{
// Note well: lock held by caller....
- BindingKey *bk = bindingTree.getBindingKey(pattern); // Exact match against binding pattern
+ BindingKey *bk = bindingTree.get(pattern); // Exact match against binding pattern
if (!bk) return 0;
Binding::vector& qv(bk->bindingVector);
Binding::vector::iterator q;
@@ -385,7 +322,7 @@ bool TopicExchange::isBound(Queue::shared_ptr queue, const string* const routing
} else if (!routingKey && !queue) {
return nBindings > 0;
} else if (routingKey) {
- if (bindingTree.getBindingKey(*routingKey)) {
+ if (bindingTree.get(*routingKey)) {
return true;
}
} else {
@@ -400,294 +337,4 @@ TopicExchange::~TopicExchange() {}
const std::string TopicExchange::typeName("topic");
-//
-// class BindingNode
-//
-
-TopicExchange::BindingNode::~BindingNode()
-{
- childTokens.clear();
-}
-
-
-// Add a binding pattern to the tree. Return a pointer to the binding key
-// of the node that matches the binding pattern.
-TopicExchange::BindingKey*
-TopicExchange::BindingNode::addBindingKey(const std::string& normalizedRoute)
-{
- TokenIterator bKey(normalizedRoute);
- return addBindingKey(bKey, normalizedRoute);
-}
-
-
-// Return a pointer to the binding key of the leaf node that matches the binding pattern.
-TopicExchange::BindingKey*
-TopicExchange::BindingNode::getBindingKey(const std::string& normalizedRoute)
-{
- TokenIterator bKey(normalizedRoute);
- return getBindingKey(bKey);
-}
-
-
-// Delete the binding associated with the given route.
-void TopicExchange::BindingNode::removeBindingKey(const std::string& normalizedRoute)
-{
- TokenIterator bKey2(normalizedRoute);
- removeBindingKey(bKey2, normalizedRoute);
-}
-
-// visit each node in the tree. Note: all nodes are visited,
-// even non-leaf nodes (i.e. nodes without any bindings)
-bool TopicExchange::BindingNode::iterateAll(TopicExchange::BindingNode::TreeIterator& iter)
-{
- if (!iter.visit(*this)) return false;
-
- if (starChild && !starChild->iterateAll(iter)) return false;
-
- if (hashChild && !hashChild->iterateAll(iter)) return false;
-
- for (ChildMap::iterator ptr = childTokens.begin();
- ptr != childTokens.end(); ptr++) {
-
- if (!ptr->second->iterateAll(iter)) return false;
- }
-
- return true;
-}
-
-// applies iter against only matching nodes until iter returns false
-// Note Well: the iter may match against the same node more than once
-// if # wildcards are present!
-bool TopicExchange::BindingNode::iterateMatch(const std::string& routingKey, TreeIterator& iter)
-{
- TopicExchange::TokenIterator rKey(routingKey);
- return iterateMatch( rKey, iter );
-}
-
-
-// recurse over binding using token iterator.
-// Note well: bkey is modified!
-TopicExchange::BindingKey*
-TopicExchange::BindingNode::addBindingKey(TokenIterator &bKey,
- const string& fullPattern)
-{
- if (bKey.finished()) {
- // this node's binding
- if (routePattern.empty()) {
- routePattern = fullPattern;
- } else assert(routePattern == fullPattern);
-
- return &bindings;
-
- } else {
- // pop the topmost token & recurse...
-
- if (bKey.match(STAR)) {
- if (!starChild) {
- starChild.reset(new StarNode());
- }
- bKey.next();
- return starChild->addBindingKey(bKey, fullPattern);
-
- } else if (bKey.match(HASH)) {
- if (!hashChild) {
- hashChild.reset(new HashNode());
- }
- bKey.next();
- return hashChild->addBindingKey(bKey, fullPattern);
-
- } else {
- ChildMap::iterator ptr;
- std::string next_token;
- bKey.pop(next_token);
- ptr = childTokens.find(next_token);
- if (ptr != childTokens.end()) {
- return ptr->second->addBindingKey(bKey, fullPattern);
- } else {
- BindingNode::shared_ptr child(new BindingNode(next_token));
- childTokens[next_token] = child;
- return child->addBindingKey(bKey, fullPattern);
- }
- }
- }
-}
-
-
-// Remove a binding pattern from the tree. Return true if the current
-// node becomes a leaf without any bindings (therefore can be deleted).
-// Note Well: modifies parameter bKey's value!
-bool
-TopicExchange::BindingNode::removeBindingKey(TokenIterator &bKey,
- const string& fullPattern)
-{
- bool remove;
-
- if (!bKey.finished()) {
-
- if (bKey.match(STAR)) {
- bKey.next();
- if (starChild) {
- remove = starChild->removeBindingKey(bKey, fullPattern);
- if (remove) {
- starChild.reset();
- }
- }
- } else if (bKey.match(HASH)) {
- bKey.next();
- if (hashChild) {
- remove = hashChild->removeBindingKey(bKey, fullPattern);
- if (remove) {
- hashChild.reset();
- }
- }
- } else {
- ChildMap::iterator ptr;
- std::string next_token;
- bKey.pop(next_token);
- ptr = childTokens.find(next_token);
- if (ptr != childTokens.end()) {
- remove = ptr->second->removeBindingKey(bKey, fullPattern);
- if (remove) {
- childTokens.erase(ptr);
- }
- }
- }
- }
-
- // no bindings and no children == parent can delete this node.
- return getChildCount() == 0 && bindings.bindingVector.empty();
-}
-
-
-// find the binding key that matches the given binding pattern.
-// Note Well: modifies key parameter!
-TopicExchange::BindingKey*
-TopicExchange::BindingNode::getBindingKey(TokenIterator &key)
-{
- if (key.finished()) {
- return &bindings;
- }
-
- string next_token;
-
- key.pop(next_token);
-
- if (next_token == STAR) {
- if (starChild)
- return starChild->getBindingKey(key);
- } else if (next_token == HASH) {
- if (hashChild)
- return hashChild->getBindingKey(key);
- } else {
- ChildMap::iterator ptr;
- ptr = childTokens.find(next_token);
- if (ptr != childTokens.end()) {
- return ptr->second->getBindingKey(key);
- }
- }
-
- return 0;
-}
-
-
-
-// iterate over all nodes that match the given key. Note well: the set of nodes
-// that are visited includes matching non-leaf nodes.
-// Note well: parameter key is modified!
-bool TopicExchange::BindingNode::iterateMatch(TokenIterator& key, TreeIterator& iter)
-{
- // invariant: key has matched all previous tokens up to this node.
- if (key.finished()) {
- // exact match this node: visit if bound
- if (!bindings.bindingVector.empty())
- if (!iter.visit(*this)) return false;
- }
-
- // check remaining key against children, even if empty.
- return iterateMatchChildren(key, iter);
-}
-
-
-TopicExchange::StarNode::StarNode()
- : BindingNode(STAR) {}
-
-
-// See iterateMatch() above.
-// Special case: this node must verify a token is available (match exactly one).
-bool TopicExchange::StarNode::iterateMatch(TokenIterator& key, TreeIterator& iter)
-{
- // must match one token:
- if (key.finished())
- return true; // match failed, but continue iteration on siblings
-
- // pop the topmost token
- key.next();
-
- if (key.finished()) {
- // exact match this node: visit if bound
- if (!bindings.bindingVector.empty())
- if (!iter.visit(*this)) return false;
- }
-
- return iterateMatchChildren(key, iter);
-}
-
-
-TopicExchange::HashNode::HashNode()
- : BindingNode(HASH) {}
-
-
-// See iterateMatch() above.
-// Special case: can match zero or more tokens at the head of the key.
-bool TopicExchange::HashNode::iterateMatch(TokenIterator& key, TreeIterator& iter)
-{
- // consume each token and look for a match on the
- // remaining key.
- while (!key.finished()) {
- if (!iterateMatchChildren(key, iter)) return false;
- key.next();
- }
-
- if (!bindings.bindingVector.empty())
- return iter.visit(*this);
-
- return true;
-}
-
-
-// helper: iterate over current node's matching children
-bool
-TopicExchange::BindingNode::iterateMatchChildren(const TopicExchange::TokenIterator& key,
- TopicExchange::BindingNode::TreeIterator& iter)
-{
- // always try glob - it can match empty keys
- if (hashChild) {
- TokenIterator tmp(key);
- if (!hashChild->iterateMatch(tmp, iter))
- return false;
- }
-
- if (!key.finished()) {
-
- if (starChild) {
- TokenIterator tmp(key);
- if (!starChild->iterateMatch(tmp, iter))
- return false;
- }
-
- if (!childTokens.empty()) {
- TokenIterator newKey(key);
- std::string next_token;
- newKey.pop(next_token);
-
- ChildMap::iterator ptr = childTokens.find(next_token);
- if (ptr != childTokens.end()) {
- return ptr->second->iterateMatch(newKey, iter);
- }
- }
- }
-
- return true;
-}
-
}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/TopicExchange.h b/qpid/cpp/src/qpid/broker/TopicExchange.h
index cc24e1411e..46871a1c6b 100644
--- a/qpid/cpp/src/qpid/broker/TopicExchange.h
+++ b/qpid/cpp/src/qpid/broker/TopicExchange.h
@@ -28,6 +28,7 @@
#include "qpid/framing/FieldTable.h"
#include "qpid/sys/Monitor.h"
#include "qpid/broker/Queue.h"
+#include "qpid/broker/TopicKeyNode.h"
namespace qpid {
@@ -35,7 +36,6 @@ namespace broker {
class TopicExchange : public virtual Exchange {
- struct TokenIterator;
class Normalizer;
struct BindingKey { // binding for this node
@@ -43,129 +43,44 @@ class TopicExchange : public virtual Exchange {
FedBinding fedBinding;
};
- // Binding database:
- // The dotted form of a binding key is broken up and stored in a directed tree graph.
- // Common binding prefix are merged. This allows the route match alogrithm to quickly
- // isolate those sub-trees that match a given routingKey.
- // For example, given the routes:
- // a.b.c.<...>
- // a.b.d.<...>
- // a.x.y.<...>
- // The resulting tree would be:
- // a-->b-->c-->...
- // | +-->d-->...
- // +-->x-->y-->...
- //
- class QPID_BROKER_CLASS_EXTERN BindingNode {
- public:
-
- typedef boost::shared_ptr<BindingNode> shared_ptr;
-
- // for database transversal (visit a node).
- class TreeIterator {
- public:
- TreeIterator() {};
- virtual ~TreeIterator() {};
- virtual bool visit(BindingNode& node) = 0;
- };
-
- BindingNode() {};
- BindingNode(const std::string& token) : token(token) {};
- QPID_BROKER_EXTERN virtual ~BindingNode();
-
- // add normalizedRoute to tree, return associated BindingKey
- QPID_BROKER_EXTERN BindingKey* addBindingKey(const std::string& normalizedRoute);
-
- // return BindingKey associated with normalizedRoute
- QPID_BROKER_EXTERN BindingKey* getBindingKey(const std::string& normalizedRoute);
-
- // remove BindingKey associated with normalizedRoute
- QPID_BROKER_EXTERN void removeBindingKey(const std::string& normalizedRoute);
-
- // applies iter against each node in tree until iter returns false
- QPID_BROKER_EXTERN bool iterateAll(TreeIterator& iter);
-
- // applies iter against only matching nodes until iter returns false
- QPID_BROKER_EXTERN bool iterateMatch(const std::string& routingKey, TreeIterator& iter);
-
- std::string routePattern; // normalized binding that matches this node
- BindingKey bindings; // for matches against this node
-
- protected:
-
- std::string token; // portion of pattern represented by this node
-
- // children
- typedef std::map<const std::string, BindingNode::shared_ptr> ChildMap;
- ChildMap childTokens;
- BindingNode::shared_ptr starChild; // "*" subtree
- BindingNode::shared_ptr hashChild; // "#" subtree
-
- unsigned int getChildCount() { return childTokens.size() +
- (starChild ? 1 : 0) + (hashChild ? 1 : 0); }
- BindingKey* addBindingKey(TokenIterator& bKey,
- const std::string& fullPattern);
- bool removeBindingKey(TokenIterator& bKey,
- const std::string& fullPattern);
- BindingKey* getBindingKey(TokenIterator& bKey);
- QPID_BROKER_EXTERN virtual bool iterateMatch(TokenIterator& rKey, TreeIterator& iter);
- bool iterateMatchChildren(const TokenIterator& key, TreeIterator& iter);
- };
-
- // Special case: ("*" token) Node in the tree for a match exactly one wildcard
- class StarNode : public BindingNode {
- public:
- StarNode();
- ~StarNode() {};
-
- protected:
- virtual bool iterateMatch(TokenIterator& key, TreeIterator& iter);
- };
+ typedef TopicKeyNode<BindingKey> BindingNode;
- // Special case: ("#" token) Node in the tree for a match zero or more
- class HashNode : public BindingNode {
- public:
- HashNode();
- ~HashNode() {};
+ BindingKey *getQueueBinding(Queue::shared_ptr queue, const std::string& pattern);
+ bool deleteBinding(Queue::shared_ptr queue,
+ const std::string& routingKey,
+ BindingKey *bk);
- protected:
- virtual bool iterateMatch(TokenIterator& key, TreeIterator& iter);
- };
+ class ReOriginIter;
+ class BindingsFinderIter;
+ class QueueFinderIter;
BindingNode bindingTree;
unsigned long nBindings;
qpid::sys::RWlock lock; // protects bindingTree and nBindings
qpid::sys::RWlock cacheLock; // protects cache
std::map<std::string, BindingList> bindingCache; // cache of matched routes.
+
class ClearCache {
private:
qpid::sys::RWlock* cacheLock;
std::map<std::string, BindingList>* bindingCache;
- bool cleared;
+ bool cleared;
public:
- ClearCache(qpid::sys::RWlock* l, std::map<std::string, BindingList>* bc): cacheLock(l),
- bindingCache(bc),cleared(false) {};
+ ClearCache(qpid::sys::RWlock* l, std::map<std::string, BindingList>* bc) :
+ cacheLock(l), bindingCache(bc),cleared(false) {};
void clearCache() {
- qpid::sys::RWlock::ScopedWlock l(*cacheLock);
- if (!cleared) {
- bindingCache->clear();
- cleared =true;
- }
+ qpid::sys::RWlock::ScopedWlock l(*cacheLock);
+ if (!cleared) {
+ bindingCache->clear();
+ cleared =true;
+ }
};
~ClearCache(){
- clearCache();
+ clearCache();
};
};
- BindingKey *getQueueBinding(Queue::shared_ptr queue, const std::string& pattern);
- bool deleteBinding(Queue::shared_ptr queue,
- const std::string& routingKey,
- BindingKey *bk);
- class ReOriginIter;
- class BindingsFinderIter;
- class QueueFinderIter;
-
- public:
+public:
static const std::string typeName;
static QPID_BROKER_EXTERN std::string normalize(const std::string& pattern);
@@ -199,7 +114,6 @@ class TopicExchange : public virtual Exchange {
};
-
}
}
diff --git a/qpid/cpp/src/qpid/broker/TopicKeyNode.h b/qpid/cpp/src/qpid/broker/TopicKeyNode.h
new file mode 100644
index 0000000000..7671ed069d
--- /dev/null
+++ b/qpid/cpp/src/qpid/broker/TopicKeyNode.h
@@ -0,0 +1,371 @@
+/*
+ *
+ * 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.
+ *
+ */
+#ifndef _QPID_BROKER_TOPIC_KEY_NODE_
+#define _QPID_BROKER_TOPIC_KEY_NODE_
+
+#include "qpid/broker/BrokerImportExport.h"
+#include <boost/shared_ptr.hpp>
+#include <map>
+#include <string>
+#include <string.h>
+
+
+namespace qpid {
+namespace broker {
+
+static const std::string STAR("*");
+static const std::string HASH("#");
+
+
+// Iterate over a string of '.'-separated tokens.
+struct TokenIterator {
+ typedef std::pair<const char*,const char*> Token;
+
+ TokenIterator(const char* b, const char* e) : end(e), token(std::make_pair(b, std::find(b,e,'.'))) {}
+
+ TokenIterator(const std::string& key) : end(&key[0]+key.size()), token(std::make_pair(&key[0], std::find(&key[0],end,'.'))) {}
+
+ bool finished() const { return !token.first; }
+
+ void next() {
+ if (token.second == end)
+ token.first = token.second = 0;
+ else {
+ token.first=token.second+1;
+ token.second=(std::find(token.first, end, '.'));
+ }
+ }
+
+ void pop(std::string &top) {
+ ptrdiff_t l = len();
+ if (l) {
+ top.assign(token.first, l);
+ } else top.clear();
+ next();
+ }
+
+ bool match1(char c) const {
+ return token.second==token.first+1 && *token.first == c;
+ }
+
+ bool match(const Token& token2) const {
+ ptrdiff_t l=len();
+ return l == token2.second-token2.first &&
+ strncmp(token.first, token2.first, l) == 0;
+ }
+
+ bool match(const std::string& str) const {
+ ptrdiff_t l=len();
+ return l == ptrdiff_t(str.size()) &&
+ str.compare(0, l, token.first, l) == 0;
+ }
+
+ ptrdiff_t len() const { return token.second - token.first; }
+
+
+ const char* end;
+ Token token;
+};
+
+
+// Binding database:
+// The dotted form of a binding key is broken up and stored in a directed tree graph.
+// Common binding prefix are merged. This allows the route match alogrithm to quickly
+// isolate those sub-trees that match a given routingKey.
+// For example, given the routes:
+// a.b.c.<...>
+// a.b.d.<...>
+// a.x.y.<...>
+// The resulting tree would be:
+// a-->b-->c-->...
+// | +-->d-->...
+// +-->x-->y-->...
+//
+template <class T>
+class QPID_BROKER_CLASS_EXTERN TopicKeyNode {
+
+ public:
+
+ typedef boost::shared_ptr<TopicKeyNode> shared_ptr;
+
+ // for database transversal (visit a node).
+ class TreeIterator {
+ public:
+ TreeIterator() {};
+ virtual ~TreeIterator() {};
+ virtual bool visit(TopicKeyNode& node) = 0;
+ };
+
+ TopicKeyNode() : isStar(false), isHash(false) {}
+ TopicKeyNode(const std::string& _t) : token(_t), isStar(_t == STAR), isHash(_t == HASH) {}
+ QPID_BROKER_EXTERN virtual ~TopicKeyNode() {
+ childTokens.clear();
+ }
+
+ // add normalizedRoute to tree, return associated T
+ QPID_BROKER_EXTERN T* add(const std::string& normalizedRoute) {
+ TokenIterator bKey(normalizedRoute);
+ return add(bKey, normalizedRoute);
+ }
+
+ // return T associated with normalizedRoute
+ QPID_BROKER_EXTERN T* get(const std::string& normalizedRoute) {
+ TokenIterator bKey(normalizedRoute);
+ return get(bKey);
+ }
+
+ // remove T associated with normalizedRoute
+ QPID_BROKER_EXTERN void remove(const std::string& normalizedRoute) {
+ TokenIterator bKey2(normalizedRoute);
+ remove(bKey2, normalizedRoute);
+ }
+
+ // applies iter against each node in tree until iter returns false
+ QPID_BROKER_EXTERN bool iterateAll(TreeIterator& iter) {
+ if (!iter.visit(*this)) return false;
+ if (starChild && !starChild->iterateAll(iter)) return false;
+ if (hashChild && !hashChild->iterateAll(iter)) return false;
+ for (typename ChildMap::iterator ptr = childTokens.begin();
+ ptr != childTokens.end(); ptr++) {
+ if (!ptr->second->iterateAll(iter)) return false;
+ }
+ return true;
+ }
+
+ // applies iter against only matching nodes until iter returns false
+ QPID_BROKER_EXTERN bool iterateMatch(const std::string& routingKey, TreeIterator& iter) {
+ TokenIterator rKey(routingKey);
+ return iterateMatch( rKey, iter );
+ }
+
+ std::string routePattern; // normalized binding that matches this node
+ T bindings; // for matches against this node
+
+ private:
+
+ std::string token; // portion of pattern represented by this node
+ bool isStar;
+ bool isHash;
+
+ // children
+ typedef std::map<const std::string, typename TopicKeyNode::shared_ptr> ChildMap;
+ ChildMap childTokens;
+ typename TopicKeyNode::shared_ptr starChild; // "*" subtree
+ typename TopicKeyNode::shared_ptr hashChild; // "#" subtree
+
+ unsigned int getChildCount() { return childTokens.size() +
+ (starChild ? 1 : 0) + (hashChild ? 1 : 0); }
+
+ T* add(TokenIterator& bKey, const std::string& fullPattern){
+ if (bKey.finished()) {
+ // this node's binding
+ if (routePattern.empty()) {
+ routePattern = fullPattern;
+ } else assert(routePattern == fullPattern);
+
+ return &bindings;
+
+ } else {
+ // pop the topmost token & recurse...
+
+ if (bKey.match(STAR)) {
+ if (!starChild) {
+ starChild.reset(new TopicKeyNode<T>(STAR));
+ }
+ bKey.next();
+ return starChild->add(bKey, fullPattern);
+
+ } else if (bKey.match(HASH)) {
+ if (!hashChild) {
+ hashChild.reset(new TopicKeyNode<T>(HASH));
+ }
+ bKey.next();
+ return hashChild->add(bKey, fullPattern);
+
+ } else {
+ typename ChildMap::iterator ptr;
+ std::string next_token;
+ bKey.pop(next_token);
+ ptr = childTokens.find(next_token);
+ if (ptr != childTokens.end()) {
+ return ptr->second->add(bKey, fullPattern);
+ } else {
+ typename TopicKeyNode::shared_ptr child(new TopicKeyNode<T>(next_token));
+ childTokens[next_token] = child;
+ return child->add(bKey, fullPattern);
+ }
+ }
+ }
+ }
+
+
+ bool remove(TokenIterator& bKey, const std::string& fullPattern) {
+ bool remove;
+ if (!bKey.finished()) {
+ if (bKey.match(STAR)) {
+ bKey.next();
+ if (starChild) {
+ remove = starChild->remove(bKey, fullPattern);
+ if (remove) {
+ starChild.reset();
+ }
+ }
+ } else if (bKey.match(HASH)) {
+ bKey.next();
+ if (hashChild) {
+ remove = hashChild->remove(bKey, fullPattern);
+ if (remove) {
+ hashChild.reset();
+ }
+ }
+ } else {
+ typename ChildMap::iterator ptr;
+ std::string next_token;
+ bKey.pop(next_token);
+ ptr = childTokens.find(next_token);
+ if (ptr != childTokens.end()) {
+ remove = ptr->second->remove(bKey, fullPattern);
+ if (remove) {
+ childTokens.erase(ptr);
+ }
+ }
+ }
+ }
+
+ // no bindings and no children == parent can delete this node.
+ return getChildCount() == 0 && bindings.bindingVector.empty();
+ }
+
+
+ T* get(TokenIterator& bKey) {
+ if (bKey.finished()) {
+ return &bindings;
+ }
+
+ std::string next_token;
+ bKey.pop(next_token);
+
+ if (next_token == STAR) {
+ if (starChild)
+ return starChild->get(bKey);
+ } else if (next_token == HASH) {
+ if (hashChild)
+ return hashChild->get(bKey);
+ } else {
+ typename ChildMap::iterator ptr;
+ ptr = childTokens.find(next_token);
+ if (ptr != childTokens.end()) {
+ return ptr->second->get(bKey);
+ }
+ }
+
+ return 0;
+ }
+
+
+ bool iterateMatch(TokenIterator& rKey, TreeIterator& iter) {
+ if (isStar) return iterateMatchStar(rKey, iter);
+ if (isHash) return iterateMatchHash(rKey, iter);
+ return iterateMatchString(rKey, iter);
+ }
+
+
+ bool iterateMatchString(TokenIterator& rKey, TreeIterator& iter){
+ // invariant: key has matched all previous tokens up to this node.
+ if (rKey.finished()) {
+ // exact match this node: visit if bound
+ if (!bindings.bindingVector.empty())
+ if (!iter.visit(*this)) return false;
+ }
+
+ // check remaining key against children, even if empty.
+ return iterateMatchChildren(rKey, iter);
+ }
+
+
+ bool iterateMatchStar(TokenIterator& rKey, TreeIterator& iter) {
+ // must match one token:
+ if (rKey.finished())
+ return true; // match failed, but continue iteration on siblings
+
+ // pop the topmost token
+ rKey.next();
+
+ if (rKey.finished()) {
+ // exact match this node: visit if bound
+ if (!bindings.bindingVector.empty())
+ if (!iter.visit(*this)) return false;
+ }
+
+ return iterateMatchChildren(rKey, iter);
+ }
+
+
+ bool iterateMatchHash(TokenIterator& rKey, TreeIterator& iter) {
+ // consume each token and look for a match on the
+ // remaining key.
+ while (!rKey.finished()) {
+ if (!iterateMatchChildren(rKey, iter)) return false;
+ rKey.next();
+ }
+
+ if (!bindings.bindingVector.empty())
+ return iter.visit(*this);
+
+ return true;
+ }
+
+
+ bool iterateMatchChildren(const TokenIterator& key, TreeIterator& iter) {
+ // always try glob - it can match empty keys
+ if (hashChild) {
+ TokenIterator tmp(key);
+ if (!hashChild->iterateMatch(tmp, iter))
+ return false;
+ }
+
+ if (!key.finished()) {
+ if (starChild) {
+ TokenIterator tmp(key);
+ if (!starChild->iterateMatch(tmp, iter))
+ return false;
+ }
+
+ if (!childTokens.empty()) {
+ TokenIterator newKey(key);
+ std::string next_token;
+ newKey.pop(next_token);
+
+ typename ChildMap::iterator ptr = childTokens.find(next_token);
+ if (ptr != childTokens.end()) {
+ return ptr->second->iterateMatch(newKey, iter);
+ }
+ }
+ }
+
+ return true;
+ }
+};
+
+}
+}
+
+#endif
diff --git a/qpid/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp b/qpid/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp
index 2acc09cded..1d026f730e 100644
--- a/qpid/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp
+++ b/qpid/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp
@@ -25,12 +25,15 @@
#include "qpid/broker/Connection.h"
#include "qpid/log/Statement.h"
#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/FieldValue.h"
#include <windows.h>
using namespace qpid::framing;
using qpid::sys::SecurityLayer;
+using std::string;
+
namespace qpid {
namespace broker {
diff --git a/qpid/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp b/qpid/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp
index 1dff1ddc8f..fb59d058f8 100644
--- a/qpid/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp
+++ b/qpid/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp
@@ -44,26 +44,34 @@
namespace qpid {
namespace sys {
+
+class Timer;
+
namespace windows {
struct SslServerOptions : qpid::Options
{
std::string certStore;
+ std::string certStoreLocation;
std::string certName;
uint16_t port;
bool clientAuth;
SslServerOptions() : qpid::Options("SSL Options"),
- certStore("My"), port(5671), clientAuth(false)
+ certStore("My"),
+ certStoreLocation("CurrentUser"),
+ certName("localhost"),
+ port(5671),
+ clientAuth(false)
{
qpid::Address me;
if (qpid::sys::SystemInfo::getLocalHostname(me))
certName = me.host;
- else
- certName = "localhost";
addOptions()
("ssl-cert-store", optValue(certStore, "NAME"), "Local store name from which to obtain certificate")
+ ("ssl-cert-store-location", optValue(certStoreLocation, "NAME"),
+ "Local store name location for certificates ( CurrentUser | LocalMachine | CurrentService )")
("ssl-cert-name", optValue(certName, "NAME"), "Name of the certificate to use")
("ssl-port", optValue(port, "PORT"), "Port on which to listen for SSL connections")
("ssl-require-client-authentication", optValue(clientAuth),
@@ -72,10 +80,12 @@ struct SslServerOptions : qpid::Options
};
class SslProtocolFactory : public qpid::sys::ProtocolFactory {
- const bool tcpNoDelay;
boost::ptr_vector<Socket> listeners;
boost::ptr_vector<AsynchAcceptor> acceptors;
+ Timer& brokerTimer;
+ uint32_t maxNegotiateTime;
uint16_t listeningPort;
+ const bool tcpNoDelay;
std::string brokerHost;
const bool clientAuthSelected;
std::auto_ptr<qpid::sys::AsynchAcceptor> acceptor;
@@ -83,7 +93,9 @@ class SslProtocolFactory : public qpid::sys::ProtocolFactory {
CredHandle credHandle;
public:
- SslProtocolFactory(const SslServerOptions&, const std::string& host, const std::string& port, int backlog, bool nodelay);
+ SslProtocolFactory(const SslServerOptions&, const std::string& host, const std::string& port,
+ int backlog, bool nodelay,
+ Timer& timer, uint32_t maxTime);
~SslProtocolFactory();
void accept(sys::Poller::shared_ptr, sys::ConnectionCodec::Factory*);
void connect(sys::Poller::shared_ptr, const std::string& host, const std::string& port,
@@ -120,8 +132,8 @@ static struct SslPlugin : public Plugin {
const broker::Broker::Options& opts = broker->getOptions();
ProtocolFactory::shared_ptr protocol(new SslProtocolFactory(options,
"", boost::lexical_cast<std::string>(options.port),
- opts.connectionBacklog,
- opts.tcpNoDelay));
+ opts.connectionBacklog, opts.tcpNoDelay,
+ broker->getTimer(), opts.maxNegotiateTime));
QPID_LOG(notice, "Listening for SSL connections on TCP port " << protocol->getPort());
broker->registerProtocolFactory("ssl", protocol);
} catch (const std::exception& e) {
@@ -132,9 +144,12 @@ static struct SslPlugin : public Plugin {
} sslPlugin;
SslProtocolFactory::SslProtocolFactory(const SslServerOptions& options,
- const std::string& host, const std::string& port, int backlog,
- bool nodelay)
- : tcpNoDelay(nodelay),
+ const std::string& host, const std::string& port,
+ int backlog, bool nodelay,
+ Timer& timer, uint32_t maxTime)
+ : brokerTimer(timer),
+ maxNegotiateTime(maxTime),
+ tcpNoDelay(nodelay),
clientAuthSelected(options.clientAuth) {
// Make sure that certificate store is good before listening to sockets
@@ -142,11 +157,25 @@ SslProtocolFactory::SslProtocolFactory(const SslServerOptions& options,
SecInvalidateHandle(&credHandle);
// Get the certificate for this server.
+ DWORD flags = 0;
+ std::string certStoreLocation = options.certStoreLocation;
+ std::transform(certStoreLocation.begin(), certStoreLocation.end(), certStoreLocation.begin(), ::tolower);
+ if (certStoreLocation == "currentuser") {
+ flags = CERT_SYSTEM_STORE_CURRENT_USER;
+ } else if (certStoreLocation == "localmachine") {
+ flags = CERT_SYSTEM_STORE_LOCAL_MACHINE;
+ } else if (certStoreLocation == "currentservice") {
+ flags = CERT_SYSTEM_STORE_CURRENT_SERVICE;
+ } else {
+ QPID_LOG(error, "Unrecognised SSL certificate store location: " << options.certStoreLocation
+ << " - Using default location");
+ }
HCERTSTORE certStoreHandle;
certStoreHandle = ::CertOpenStore(CERT_STORE_PROV_SYSTEM_A,
X509_ASN_ENCODING,
0,
- CERT_SYSTEM_STORE_LOCAL_MACHINE,
+ flags |
+ CERT_STORE_READONLY_FLAG,
options.certStore.c_str());
if (!certStoreHandle)
throw qpid::Exception(QPID_MSG("Opening store " << options.certStore << " " << qpid::sys::strError(GetLastError())));
@@ -252,7 +281,7 @@ void SslProtocolFactory::established(sys::Poller::shared_ptr poller,
boost::bind(&AsynchIOHandler::idle, async, _1));
}
- async->init(aio, 4);
+ async->init(aio, brokerTimer, maxNegotiateTime, 4);
aio->start(poller);
}
diff --git a/qpid/cpp/src/qpid/client/ConnectionHandler.cpp b/qpid/cpp/src/qpid/client/ConnectionHandler.cpp
index ab0d8e0700..91838d8e8b 100644
--- a/qpid/cpp/src/qpid/client/ConnectionHandler.cpp
+++ b/qpid/cpp/src/qpid/client/ConnectionHandler.cpp
@@ -28,10 +28,13 @@
#include "qpid/framing/all_method_bodies.h"
#include "qpid/framing/ClientInvoker.h"
#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/FieldValue.h"
#include "qpid/log/Helpers.h"
#include "qpid/log/Statement.h"
#include "qpid/sys/SystemInfo.h"
+#include <algorithm>
+
using namespace qpid::client;
using namespace qpid::framing;
using namespace qpid::framing::connection;
@@ -238,15 +241,16 @@ void ConnectionHandler::start(const FieldTable& /*serverProps*/, const Array& me
);
std::vector<std::string> mechlist;
+ mechlist.reserve(mechanisms.size());
if (mechanism.empty()) {
//mechlist is simply what the server offers
- mechanisms.collect(mechlist);
+ std::transform(mechanisms.begin(), mechanisms.end(), std::back_inserter(mechlist), Array::get<std::string, Array::ValuePtr>);
} else {
//mechlist is the intersection of those indicated by user and
//those supported by server, in the order listed by user
std::vector<std::string> allowed = split(mechanism, " ");
- std::vector<std::string> supported;
- mechanisms.collect(supported);
+ std::vector<std::string> supported(mechanisms.size());
+ std::transform(mechanisms.begin(), mechanisms.end(), std::back_inserter(supported), Array::get<std::string, Array::ValuePtr>);
intersection(allowed, supported, mechlist);
if (mechlist.empty()) {
throw Exception(QPID_MSG("Desired mechanism(s) not valid: " << mechanism << " (supported: " << join(supported) << ")"));
@@ -254,7 +258,7 @@ void ConnectionHandler::start(const FieldTable& /*serverProps*/, const Array& me
}
if (sasl.get()) {
- string response;
+ std::string response;
if (sasl->start(join(mechlist), response, getSecuritySettings ? getSecuritySettings() : 0)) {
proxy.startOk(properties, sasl->getMechanism(), response, locale);
} else {
@@ -268,7 +272,7 @@ void ConnectionHandler::start(const FieldTable& /*serverProps*/, const Array& me
}
} else {
//TODO: verify that desired mechanism and locale are supported
- string response = ((char)0) + username + ((char)0) + password;
+ std::string response = ((char)0) + username + ((char)0) + password;
proxy.startOk(properties, mechanism, response, locale);
}
}
@@ -276,7 +280,7 @@ void ConnectionHandler::start(const FieldTable& /*serverProps*/, const Array& me
void ConnectionHandler::secure(const std::string& challenge)
{
if (sasl.get()) {
- string response = sasl->step(challenge);
+ std::string response = sasl->step(challenge);
proxy.secureOk(response);
} else {
throw NotImplementedException("Challenge-response cycle not yet implemented in client");
diff --git a/qpid/cpp/src/qpid/client/ConnectionImpl.cpp b/qpid/cpp/src/qpid/client/ConnectionImpl.cpp
index db97f1e0f4..85b0e8303e 100644
--- a/qpid/cpp/src/qpid/client/ConnectionImpl.cpp
+++ b/qpid/cpp/src/qpid/client/ConnectionImpl.cpp
@@ -115,8 +115,10 @@ public:
ioThreads(0),
connections(0)
{
+ CommonOptions common("", "", QPIDC_CONF_FILE);
IOThreadOptions options(c);
- options.parse(0, 0, QPIDC_CONF_FILE, true);
+ common.parse(0, 0, common.clientConfig, true);
+ options.parse(0, 0, common.clientConfig, true);
maxIOThreads = (options.maxIOThreads != -1) ?
options.maxIOThreads : 1;
}
diff --git a/qpid/cpp/src/qpid/client/LoadPlugins.cpp b/qpid/cpp/src/qpid/client/LoadPlugins.cpp
index 246eb60c67..d76e1d458e 100644
--- a/qpid/cpp/src/qpid/client/LoadPlugins.cpp
+++ b/qpid/cpp/src/qpid/client/LoadPlugins.cpp
@@ -39,10 +39,12 @@ namespace {
struct LoadtimeInitialise {
LoadtimeInitialise() {
+ CommonOptions common("", "", QPIDC_CONF_FILE);
qpid::ModuleOptions moduleOptions(QPIDC_MODULE_DIR);
string defaultPath (moduleOptions.loadDir);
- moduleOptions.parse (0, 0, QPIDC_CONF_FILE, true);
-
+ common.parse(0, 0, common.clientConfig, true);
+ moduleOptions.parse (0, 0, common.clientConfig, true);
+
for (vector<string>::iterator iter = moduleOptions.load.begin();
iter != moduleOptions.load.end();
iter++)
diff --git a/qpid/cpp/src/qpid/client/PrivateImplRef.h b/qpid/cpp/src/qpid/client/PrivateImplRef.h
index 503a383c31..fa89b1bfa0 100644
--- a/qpid/cpp/src/qpid/client/PrivateImplRef.h
+++ b/qpid/cpp/src/qpid/client/PrivateImplRef.h
@@ -77,15 +77,15 @@ template <class T> class PrivateImplRef {
static void set(T& t, const intrusive_ptr& p) {
if (t.impl == p) return;
- if (t.impl) boost::intrusive_ptr_release(t.impl);
+ if (t.impl) intrusive_ptr_release(t.impl);
t.impl = p.get();
- if (t.impl) boost::intrusive_ptr_add_ref(t.impl);
+ if (t.impl) intrusive_ptr_add_ref(t.impl);
}
// Helper functions to implement the ctor, dtor, copy, assign
- static void ctor(T& t, Impl* p) { t.impl = p; if (p) boost::intrusive_ptr_add_ref(p); }
+ static void ctor(T& t, Impl* p) { t.impl = p; if (p) intrusive_ptr_add_ref(p); }
static void copy(T& t, const T& x) { if (&t == &x) return; t.impl = 0; assign(t, x); }
- static void dtor(T& t) { if(t.impl) boost::intrusive_ptr_release(t.impl); }
+ static void dtor(T& t) { if(t.impl) intrusive_ptr_release(t.impl); }
static T& assign(T& t, const T& x) { set(t, get(x)); return t;}
};
diff --git a/qpid/cpp/src/qpid/client/SessionImpl.cpp b/qpid/cpp/src/qpid/client/SessionImpl.cpp
index 3f3ad617f4..91e728d5ae 100644
--- a/qpid/cpp/src/qpid/client/SessionImpl.cpp
+++ b/qpid/cpp/src/qpid/client/SessionImpl.cpp
@@ -426,7 +426,7 @@ void SessionImpl::sendContent(const MethodContent& content)
uint32_t remaining = data_length - offset;
while (remaining > 0) {
uint32_t length = remaining > frag_size ? frag_size : remaining;
- string frag(content.getData().substr(offset, length));
+ std::string frag(content.getData().substr(offset, length));
AMQFrame frame((AMQContentBody(frag)));
frame.setFirstSegment(false);
frame.setLastSegment(true);
diff --git a/qpid/cpp/src/qpid/client/SslConnector.cpp b/qpid/cpp/src/qpid/client/SslConnector.cpp
index 6b6bf884ec..4c6fadd28a 100644
--- a/qpid/cpp/src/qpid/client/SslConnector.cpp
+++ b/qpid/cpp/src/qpid/client/SslConnector.cpp
@@ -94,8 +94,6 @@ class SslConnector : public Connector
sys::ShutdownHandler* shutdownHandler;
framing::InputHandler* input;
- framing::InitiationHandler* initialiser;
- framing::OutputHandler* output;
Writer writer;
@@ -148,8 +146,10 @@ namespace {
struct StaticInit {
StaticInit() {
try {
+ CommonOptions common("", "", QPIDC_CONF_FILE);
SslOptions options;
- options.parse (0, 0, QPIDC_CONF_FILE, true);
+ common.parse(0, 0, common.clientConfig, true);
+ options.parse (0, 0, common.clientConfig, true);
if (options.certDbPath.empty()) {
QPID_LOG(info, "SSL connector not enabled, you must set QPID_SSL_CERT_DB to enable it.");
} else {
@@ -174,6 +174,7 @@ SslConnector::SslConnector(Poller::shared_ptr p,
initiated(false),
closed(true),
shutdownHandler(0),
+ input(0),
writer(maxFrameSize, cimpl),
aio(0),
poller(p)
diff --git a/qpid/cpp/src/qpid/client/TCPConnector.cpp b/qpid/cpp/src/qpid/client/TCPConnector.cpp
index 4660a41c07..7b2ef648be 100644
--- a/qpid/cpp/src/qpid/client/TCPConnector.cpp
+++ b/qpid/cpp/src/qpid/client/TCPConnector.cpp
@@ -76,6 +76,7 @@ TCPConnector::TCPConnector(Poller::shared_ptr p,
initiated(false),
closed(true),
shutdownHandler(0),
+ input(0),
connector(0),
aio(0),
poller(p)
diff --git a/qpid/cpp/src/qpid/client/TCPConnector.h b/qpid/cpp/src/qpid/client/TCPConnector.h
index eb3f696013..cad59043d9 100644
--- a/qpid/cpp/src/qpid/client/TCPConnector.h
+++ b/qpid/cpp/src/qpid/client/TCPConnector.h
@@ -66,8 +66,6 @@ class TCPConnector : public Connector, public sys::Codec
sys::ShutdownHandler* shutdownHandler;
framing::InputHandler* input;
- framing::InitiationHandler* initialiser;
- framing::OutputHandler* output;
sys::Socket socket;
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp
index 5924e30dd8..a8f4fb5237 100644
--- a/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp
+++ b/qpid/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp
@@ -32,6 +32,7 @@
#include "qpid/framing/ExchangeBoundResult.h"
#include "qpid/framing/ExchangeQueryResult.h"
#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/FieldValue.h"
#include "qpid/framing/QueueQueryResult.h"
#include "qpid/framing/ReplyTo.h"
#include "qpid/framing/reply_exceptions.h"
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp
index 2ea4dc0c61..aaebec0720 100644
--- a/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp
+++ b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp
@@ -78,7 +78,7 @@ bool expired(const sys::AbsTime& start, double timeout)
if (timeout == 0) return true;
if (timeout == FOREVER) return false;
sys::Duration used(start, sys::now());
- sys::Duration allowed(int64_t(timeout*sys::TIME_SEC));
+ sys::Duration allowed((int64_t)(timeout*sys::TIME_SEC));
return allowed < used;
}
diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h
index 5693b7b71f..76da4f31a9 100644
--- a/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h
+++ b/qpid/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h
@@ -78,7 +78,6 @@ class ReceiverImpl : public qpid::messaging::ReceiverImpl
std::auto_ptr<MessageSource> source;
uint32_t capacity;
qpid::client::AsyncSession session;
- qpid::messaging::MessageListener* listener;
uint32_t window;
void startFlow(const sys::Mutex::ScopedLock&); // Dummy param, call with lock held
diff --git a/qpid/cpp/src/qpid/cluster/Cluster.cpp b/qpid/cpp/src/qpid/cluster/Cluster.cpp
index 3c1d23c842..34aaf3d341 100644
--- a/qpid/cpp/src/qpid/cluster/Cluster.cpp
+++ b/qpid/cpp/src/qpid/cluster/Cluster.cpp
@@ -131,6 +131,7 @@
#include "qpid/cluster/UpdateExchange.h"
#include "qpid/cluster/ClusterTimer.h"
#include "qpid/cluster/CredentialsExchange.h"
+#include "qpid/cluster/UpdateClient.h"
#include "qpid/assert.h"
#include "qmf/org/apache/qpid/cluster/ArgsClusterStopClusterNode.h"
@@ -202,7 +203,7 @@ namespace arg=client::arg;
* Currently use SVN revision to avoid clashes with versions from
* different branches.
*/
-const uint32_t Cluster::CLUSTER_VERSION = 1207877;
+const uint32_t Cluster::CLUSTER_VERSION = 1332342;
struct ClusterDispatcher : public framing::AMQP_AllOperations::ClusterHandler {
qpid::cluster::Cluster& cluster;
@@ -269,7 +270,6 @@ Cluster::Cluster(const ClusterSettings& set, broker::Broker& b) :
"Error delivering frames",
poller),
failoverExchange(new FailoverExchange(broker.GetVhostObject(), &broker)),
- updateDataExchange(new UpdateDataExchange(*this)),
credentialsExchange(new CredentialsExchange(*this)),
quorum(boost::bind(&Cluster::leave, this)),
decoder(boost::bind(&Cluster::deliverFrame, this, _1)),
@@ -295,15 +295,6 @@ Cluster::Cluster(const ClusterSettings& set, broker::Broker& b) :
// Failover exchange provides membership updates to clients.
broker.getExchanges().registerExchange(failoverExchange);
- // Update exchange is used during updates to replicate messages
- // without modifying delivery-properties.exchange.
- broker.getExchanges().registerExchange(
- boost::shared_ptr<broker::Exchange>(new UpdateExchange(this)));
-
- // Update-data exchange is used for passing data that may be too large
- // for single control frame.
- broker.getExchanges().registerExchange(updateDataExchange);
-
// CredentialsExchange is used to authenticate new cluster members
broker.getExchanges().registerExchange(credentialsExchange);
@@ -680,6 +671,17 @@ void Cluster::initMapCompleted(Lock& l) {
authenticate();
broker.setRecovery(false); // Ditch my current store.
broker.setClusterUpdatee(true);
+
+ // Update exchange is used during updates to replicate messages
+ // without modifying delivery-properties.exchange.
+ broker.getExchanges().registerExchange(
+ boost::shared_ptr<broker::Exchange>(new UpdateExchange(this)));
+
+ // Update-data exchange is used during update for passing data that
+ // may be too large for single control frame.
+ updateDataExchange.reset(new UpdateDataExchange(*this));
+ broker.getExchanges().registerExchange(updateDataExchange);
+
if (mAgent) mAgent->suppress(true); // Suppress mgmt output during update.
state = JOINER;
mcast.mcastControl(ClusterUpdateRequestBody(ProtocolVersion(), myUrl.str()), self);
@@ -999,6 +1001,10 @@ void Cluster::checkUpdateIn(Lock& l) {
boost::ref(broker.getExchanges())));
enableClusterSafe(); // Enable cluster-safe assertions
deliverEventQueue.start();
+ // FIXME aconway 2012-04-04: unregister/delete Update[Data]Exchange
+ updateDataExchange.reset();
+ broker.getExchanges().destroy(UpdateDataExchange::EXCHANGE_NAME);
+ broker.getExchanges().destroy(UpdateClient::UPDATE);
}
else if (updateRetracted) { // Update was retracted, request another update
updateRetracted = false;
diff --git a/qpid/cpp/src/qpid/cluster/ClusterMap.cpp b/qpid/cpp/src/qpid/cluster/ClusterMap.cpp
index a8389095c9..d9817db35f 100644
--- a/qpid/cpp/src/qpid/cluster/ClusterMap.cpp
+++ b/qpid/cpp/src/qpid/cluster/ClusterMap.cpp
@@ -21,6 +21,7 @@
#include "qpid/cluster/ClusterMap.h"
#include "qpid/Url.h"
#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/FieldValue.h"
#include "qpid/log/Statement.h"
#include <boost/bind.hpp>
#include <algorithm>
@@ -29,7 +30,8 @@
#include <ostream>
using namespace std;
-using namespace boost;
+using boost::ref;
+using boost::optional;
namespace qpid {
using namespace framing;
diff --git a/qpid/cpp/src/qpid/cluster/ClusterTimer.cpp b/qpid/cpp/src/qpid/cluster/ClusterTimer.cpp
index b4f7d00f38..90e4fa9d4d 100644
--- a/qpid/cpp/src/qpid/cluster/ClusterTimer.cpp
+++ b/qpid/cpp/src/qpid/cluster/ClusterTimer.cpp
@@ -24,6 +24,7 @@
#include "qpid/log/Statement.h"
#include "qpid/framing/ClusterTimerWakeupBody.h"
#include "qpid/framing/ClusterTimerDropBody.h"
+#include "qpid/sys/ClusterSafe.h"
namespace qpid {
namespace cluster {
@@ -107,6 +108,7 @@ void ClusterTimer::drop(intrusive_ptr<TimerTask> t) {
// Deliver thread
void ClusterTimer::deliverWakeup(const std::string& name) {
QPID_LOG(trace, "Cluster timer wakeup delivered for " << name);
+ qpid::sys::assertClusterSafe();
Map::iterator i = map.find(name);
if (i == map.end())
throw Exception(QPID_MSG("Cluster timer wakeup non-existent task " << name));
diff --git a/qpid/cpp/src/qpid/cluster/Connection.cpp b/qpid/cpp/src/qpid/cluster/Connection.cpp
index fc6ada096f..de9e5b3ba0 100644
--- a/qpid/cpp/src/qpid/cluster/Connection.cpp
+++ b/qpid/cpp/src/qpid/cluster/Connection.cpp
@@ -47,6 +47,7 @@
#include "qpid/framing/ClusterConnectionAnnounceBody.h"
#include "qpid/framing/ConnectionCloseBody.h"
#include "qpid/framing/ConnectionCloseOkBody.h"
+#include "qpid/framing/FieldValue.h"
#include "qpid/log/Statement.h"
#include "qpid/sys/ClusterSafe.h"
#include "qpid/types/Variant.h"
@@ -57,6 +58,8 @@
namespace qpid {
namespace cluster {
+using std::string;
+
using namespace framing;
using namespace framing::cluster;
using amqp_0_10::ListCodec;
@@ -82,7 +85,9 @@ Connection::Connection(Cluster& c, sys::ConnectionOutputHandler& out,
const std::string& mgmtId,
const ConnectionId& id, const qpid::sys::SecuritySettings& external)
: cluster(c), self(id), catchUp(false), announced(false), output(*this, out),
- connectionCtor(&output, cluster.getBroker(), mgmtId, external, false, 0, true),
+ connectionCtor(&output, cluster.getBroker(), mgmtId, external,
+ false/*isLink*/, 0/*objectId*/, true/*shadow*/, false/*delayManagement*/,
+ false/*authenticated*/),
expectProtocolHeader(false),
mcastFrameHandler(cluster.getMulticast(), self),
updateIn(c.getUpdateReceiver()),
@@ -99,9 +104,10 @@ Connection::Connection(Cluster& c, sys::ConnectionOutputHandler& out,
external,
isLink,
isCatchUp ? ++catchUpId : 0,
- // The first catch-up connection is not considered a shadow
- // as it needs to be authenticated.
- isCatchUp && self.second > 1),
+ // The first catch-up connection is not a shadow
+ isCatchUp && self.second > 1,
+ false, // delayManagement
+ true), // catch up connecytions are authenticated
expectProtocolHeader(isLink),
mcastFrameHandler(cluster.getMulticast(), self),
updateIn(c.getUpdateReceiver()),
@@ -403,11 +409,12 @@ void Connection::shadowSetUser(const std::string& userId) {
}
void Connection::consumerState(const string& name, bool blocked, bool notifyEnabled, const SequenceNumber& position,
- uint32_t usedMsgCredit, uint32_t usedByteCredit)
+ uint32_t usedMsgCredit, uint32_t usedByteCredit, const uint32_t deliveryCount)
{
broker::SemanticState::ConsumerImpl::shared_ptr c = semanticState().find(name);
c->setPosition(position);
c->setBlocked(blocked);
+ c->setDeliveryCount(deliveryCount);
if (c->getCredit().isWindowMode()) c->getCredit().consume(usedMsgCredit, usedByteCredit);
if (notifyEnabled) c->enableNotify(); else c->disableNotify();
updateIn.consumerNumbering.add(c);
@@ -521,6 +528,7 @@ broker::QueuedMessage Connection::getUpdateMessage() {
boost::shared_ptr<broker::Queue> updateq = findQueue(UpdateClient::UPDATE);
assert(!updateq->isDurable());
broker::QueuedMessage m = updateq->get();
+ updateq->dequeue(0, m);
if (!m.payload) throw Exception(QPID_MSG(cluster << " empty update queue"));
return m;
}
@@ -781,21 +789,71 @@ void Connection::managementSetupState(
void Connection::config(const std::string& encoded) {
Buffer buf(const_cast<char*>(encoded.data()), encoded.size());
string kind;
+ uint32_t p = buf.getPosition();
buf.getShortString (kind);
- if (kind == "link") {
+ buf.setPosition(p);
+ if (broker::Link::isEncodedLink(kind)) {
broker::Link::shared_ptr link =
- broker::Link::decode(cluster.getBroker().getLinks(), buf);
+ broker::Link::decode(cluster.getBroker().getLinks(), buf);
QPID_LOG(debug, cluster << " updated link "
<< link->getHost() << ":" << link->getPort());
}
- else if (kind == "bridge") {
+ else if (broker::Bridge::isEncodedBridge(kind)) {
broker::Bridge::shared_ptr bridge =
- broker::Bridge::decode(cluster.getBroker().getLinks(), buf);
+ broker::Bridge::decode(cluster.getBroker().getLinks(), buf);
QPID_LOG(debug, cluster << " updated bridge " << bridge->getName());
}
else throw Exception(QPID_MSG("Update failed, invalid kind of config: " << kind));
}
+namespace {
+ // find a Link that matches the given Address
+ class LinkFinder {
+ qpid::Address id;
+ boost::shared_ptr<broker::Link> link;
+ public:
+ LinkFinder(const qpid::Address& _id) : id(_id) {}
+ boost::shared_ptr<broker::Link> getLink() { return link; }
+ void operator() (boost::shared_ptr<broker::Link> l)
+ {
+ if (!link) {
+ qpid::Address addr(l->getTransport(), l->getHost(), l->getPort());
+ if (id == addr) {
+ link = l;
+ }
+ }
+ }
+ };
+}
+
+void Connection::internalState(const std::string& type,
+ const std::string& name,
+ const framing::FieldTable& state)
+{
+ if (type == "link") {
+ // name is the string representation of the Link's _configured_ destination address
+ Url dest;
+ try {
+ dest = name;
+ } catch(...) {
+ throw Exception(QPID_MSG("Update failed, invalid format for Link destination address: " << name));
+ }
+ assert(dest.size());
+ LinkFinder finder(dest[0]);
+ cluster.getBroker().getLinks().eachLink(boost::ref(finder));
+ if (finder.getLink()) {
+ try {
+ finder.getLink()->setState(state);
+ } catch(...) {
+ throw Exception(QPID_MSG("Update failed, invalid state for Link " << name << ", state: " << state));
+ }
+ QPID_LOG(debug, cluster << " updated link " << dest[0] << " with state: " << state);
+ } else throw Exception(QPID_MSG("Update failed, unable to find Link named: " << name));
+ }
+ else throw Exception(QPID_MSG("Update failed, invalid object type for internal state replication: " << type));
+}
+
+
void Connection::doCatchupIoCallbacks() {
// We need to process IO callbacks during the catch-up phase in
// order to service asynchronous completions for messages
diff --git a/qpid/cpp/src/qpid/cluster/Connection.h b/qpid/cpp/src/qpid/cluster/Connection.h
index 920c4937db..b0e7b3bd9e 100644
--- a/qpid/cpp/src/qpid/cluster/Connection.h
+++ b/qpid/cpp/src/qpid/cluster/Connection.h
@@ -110,7 +110,7 @@ class Connection :
void deliveredFrame(const EventFrame&);
void consumerState(const std::string& name, bool blocked, bool notifyEnabled, const qpid::framing::SequenceNumber& position,
- uint32_t usedMsgCredit, uint32_t usedByteCredit);
+ uint32_t usedMsgCredit, uint32_t usedByteCredit, const uint32_t deliveryCount);
// ==== Used in catch-up mode to build initial state.
//
@@ -200,6 +200,8 @@ class Connection :
const std::string& instance);
void config(const std::string& encoded);
+ void internalState(const std::string& type, const std::string& name,
+ const framing::FieldTable& state);
void setSecureConnection ( broker::SecureConnection * sc );
@@ -226,6 +228,7 @@ class Connection :
uint64_t objectId;
bool shadow;
bool delayManagement;
+ bool authenticated;
ConnectionCtor(
sys::ConnectionOutputHandler* out_,
@@ -235,17 +238,18 @@ class Connection :
bool isLink_=false,
uint64_t objectId_=0,
bool shadow_=false,
- bool delayManagement_=false
+ bool delayManagement_=false,
+ bool authenticated_=true
) : out(out_), broker(broker_), mgmtId(mgmtId_), external(external_),
isLink(isLink_), objectId(objectId_), shadow(shadow_),
- delayManagement(delayManagement_)
+ delayManagement(delayManagement_), authenticated(authenticated_)
{}
std::auto_ptr<broker::Connection> construct() {
return std::auto_ptr<broker::Connection>(
new broker::Connection(
out, broker, mgmtId, external, isLink, objectId,
- shadow, delayManagement)
+ shadow, delayManagement, authenticated)
);
}
};
diff --git a/qpid/cpp/src/qpid/cluster/ConnectionCodec.cpp b/qpid/cpp/src/qpid/cluster/ConnectionCodec.cpp
index d0ba8abfb3..54327fbfe2 100644
--- a/qpid/cpp/src/qpid/cluster/ConnectionCodec.cpp
+++ b/qpid/cpp/src/qpid/cluster/ConnectionCodec.cpp
@@ -7,9 +7,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -22,6 +22,7 @@
#include "qpid/cluster/Connection.h"
#include "qpid/cluster/Cluster.h"
#include "qpid/cluster/ProxyInputHandler.h"
+#include "qpid/broker/AclModule.h"
#include "qpid/broker/Connection.h"
#include "qpid/framing/ConnectionCloseBody.h"
#include "qpid/framing/ConnectionCloseOkBody.h"
@@ -40,17 +41,10 @@ ConnectionCodec::Factory::create(ProtocolVersion v, sys::OutputControl& out,
const std::string& id,
const qpid::sys::SecuritySettings& external)
{
- broker::Broker& broker = cluster.getBroker();
- if (broker.getConnectionCounter().allowConnection())
- {
- QPID_LOG(error, "Client max connection count limit exceeded: "
- << broker.getOptions().maxConnections << " connection refused");
- return 0;
- }
if (v == ProtocolVersion(0, 10))
return new ConnectionCodec(v, out, id, cluster, false, false, external);
else if (v == ProtocolVersion(0x80 + 0, 0x80 + 10)) // Catch-up connection
- return new ConnectionCodec(v, out, id, cluster, true, false, external);
+ return new ConnectionCodec(v, out, id, cluster, true, false, external);
return 0;
}
diff --git a/qpid/cpp/src/qpid/cluster/Cpg.cpp b/qpid/cpp/src/qpid/cluster/Cpg.cpp
index 0856bcd824..6e9e22a42f 100644
--- a/qpid/cpp/src/qpid/cluster/Cpg.cpp
+++ b/qpid/cpp/src/qpid/cluster/Cpg.cpp
@@ -32,7 +32,7 @@
// This is a macro instead of a function because we don't want to
// evaluate the MSG argument unless there is an error.
#define CPG_CHECK(RESULT, MSG) \
- if ((RESULT) != CPG_OK) throw Exception(errorStr((RESULT), (MSG)))
+ if ((RESULT) != CS_OK) throw Exception(errorStr((RESULT), (MSG)))
namespace qpid {
namespace cluster {
@@ -50,13 +50,13 @@ Cpg* Cpg::cpgFromHandle(cpg_handle_t handle) {
// Applies the same retry-logic to all cpg calls that need it.
void Cpg::callCpg ( CpgOp & c ) {
- cpg_error_t result;
+ cs_error_t result;
unsigned int snooze = 10;
for ( unsigned int nth_try = 0; nth_try < cpgRetries; ++ nth_try ) {
- if ( CPG_OK == (result = c.op(handle, & group))) {
+ if ( CS_OK == (result = c.op(handle, & group))) {
break;
}
- else if ( result == CPG_ERR_TRY_AGAIN ) {
+ else if ( result == CS_ERR_TRY_AGAIN ) {
QPID_LOG(info, "Retrying " << c.opName );
sys::usleep ( snooze );
snooze *= 10;
@@ -65,7 +65,7 @@ void Cpg::callCpg ( CpgOp & c ) {
else break; // Don't retry unless CPG tells us to.
}
- if ( result != CPG_OK )
+ if ( result != CS_OK )
CPG_CHECK(result, c.msg(group));
}
@@ -127,9 +127,9 @@ Cpg::Cpg(Handler& h) : IOHandle(new sys::IOHandlePrivate), handler(h), isShutdow
callbacks.cpg_confchg_fn = &globalConfigChange;
QPID_LOG(notice, "Initializing CPG");
- cpg_error_t err = cpg_initialize(&handle, &callbacks);
+ cs_error_t err = cpg_initialize(&handle, &callbacks);
int retries = 6; // FIXME aconway 2009-08-06: make this configurable.
- while (err == CPG_ERR_TRY_AGAIN && --retries) {
+ while (err == CS_ERR_TRY_AGAIN && --retries) {
QPID_LOG(notice, "Re-trying CPG initialization.");
sys::sleep(5);
err = cpg_initialize(&handle, &callbacks);
@@ -169,11 +169,11 @@ bool Cpg::mcast(const iovec* iov, int iovLen) {
if (flowState == CPG_FLOW_CONTROL_ENABLED)
return false;
- cpg_error_t result;
+ cs_error_t result;
do {
result = cpg_mcast_joined(handle, CPG_TYPE_AGREED, const_cast<iovec*>(iov), iovLen);
- if (result != CPG_ERR_TRY_AGAIN) CPG_CHECK(result, cantMcastMsg(group));
- } while(result == CPG_ERR_TRY_AGAIN);
+ if (result != CS_ERR_TRY_AGAIN) CPG_CHECK(result, cantMcastMsg(group));
+ } while(result == CS_ERR_TRY_AGAIN);
return true;
}
@@ -187,34 +187,34 @@ void Cpg::shutdown() {
}
void Cpg::dispatchOne() {
- CPG_CHECK(cpg_dispatch(handle,CPG_DISPATCH_ONE), "Error in CPG dispatch");
+ CPG_CHECK(cpg_dispatch(handle,CS_DISPATCH_ONE), "Error in CPG dispatch");
}
void Cpg::dispatchAll() {
- CPG_CHECK(cpg_dispatch(handle,CPG_DISPATCH_ALL), "Error in CPG dispatch");
+ CPG_CHECK(cpg_dispatch(handle,CS_DISPATCH_ALL), "Error in CPG dispatch");
}
void Cpg::dispatchBlocking() {
- CPG_CHECK(cpg_dispatch(handle,CPG_DISPATCH_BLOCKING), "Error in CPG dispatch");
+ CPG_CHECK(cpg_dispatch(handle,CS_DISPATCH_BLOCKING), "Error in CPG dispatch");
}
-string Cpg::errorStr(cpg_error_t err, const std::string& msg) {
+string Cpg::errorStr(cs_error_t err, const std::string& msg) {
std::ostringstream os;
os << msg << ": ";
switch (err) {
- case CPG_OK: os << "ok"; break;
- case CPG_ERR_LIBRARY: os << "library"; break;
- case CPG_ERR_TIMEOUT: os << "timeout"; break;
- case CPG_ERR_TRY_AGAIN: os << "try again"; break;
- case CPG_ERR_INVALID_PARAM: os << "invalid param"; break;
- case CPG_ERR_NO_MEMORY: os << "no memory"; break;
- case CPG_ERR_BAD_HANDLE: os << "bad handle"; break;
- case CPG_ERR_ACCESS: os << "access denied. You may need to set your group ID to 'ais'"; break;
- case CPG_ERR_NOT_EXIST: os << "not exist"; break;
- case CPG_ERR_EXIST: os << "exist"; break;
- case CPG_ERR_NOT_SUPPORTED: os << "not supported"; break;
- case CPG_ERR_SECURITY: os << "security"; break;
- case CPG_ERR_TOO_MANY_GROUPS: os << "too many groups"; break;
+ case CS_OK: os << "ok"; break;
+ case CS_ERR_LIBRARY: os << "library"; break;
+ case CS_ERR_TIMEOUT: os << "timeout"; break;
+ case CS_ERR_TRY_AGAIN: os << "try again"; break;
+ case CS_ERR_INVALID_PARAM: os << "invalid param"; break;
+ case CS_ERR_NO_MEMORY: os << "no memory"; break;
+ case CS_ERR_BAD_HANDLE: os << "bad handle"; break;
+ case CS_ERR_ACCESS: os << "access denied. You may need to set your group ID to 'ais'"; break;
+ case CS_ERR_NOT_EXIST: os << "not exist"; break;
+ case CS_ERR_EXIST: os << "exist"; break;
+ case CS_ERR_NOT_SUPPORTED: os << "not supported"; break;
+ case CS_ERR_SECURITY: os << "security"; break;
+ case CS_ERR_TOO_MANY_GROUPS: os << "too many groups"; break;
default: os << ": unknown cpg error " << err;
};
os << " (" << err << ")";
diff --git a/qpid/cpp/src/qpid/cluster/Cpg.h b/qpid/cpp/src/qpid/cluster/Cpg.h
index 6b81c602bd..1afbce8d75 100644
--- a/qpid/cpp/src/qpid/cluster/Cpg.h
+++ b/qpid/cpp/src/qpid/cluster/Cpg.h
@@ -131,7 +131,7 @@ class Cpg : public sys::IOHandle {
CpgOp ( std::string opName )
: opName(opName) { }
- virtual cpg_error_t op ( cpg_handle_t handle, struct cpg_name * ) = 0;
+ virtual cs_error_t op ( cpg_handle_t handle, struct cpg_name * ) = 0;
virtual std::string msg(const Name&) = 0;
virtual ~CpgOp ( ) { }
};
@@ -141,7 +141,7 @@ class Cpg : public sys::IOHandle {
CpgJoinOp ( )
: CpgOp ( std::string("cpg_join") ) { }
- cpg_error_t op(cpg_handle_t handle, struct cpg_name * group) {
+ cs_error_t op(cpg_handle_t handle, struct cpg_name * group) {
return cpg_join ( handle, group );
}
@@ -152,7 +152,7 @@ class Cpg : public sys::IOHandle {
CpgLeaveOp ( )
: CpgOp ( std::string("cpg_leave") ) { }
- cpg_error_t op(cpg_handle_t handle, struct cpg_name * group) {
+ cs_error_t op(cpg_handle_t handle, struct cpg_name * group) {
return cpg_leave ( handle, group );
}
@@ -163,7 +163,7 @@ class Cpg : public sys::IOHandle {
CpgFinalizeOp ( )
: CpgOp ( std::string("cpg_finalize") ) { }
- cpg_error_t op(cpg_handle_t handle, struct cpg_name *) {
+ cs_error_t op(cpg_handle_t handle, struct cpg_name *) {
return cpg_finalize ( handle );
}
@@ -177,7 +177,7 @@ class Cpg : public sys::IOHandle {
CpgLeaveOp cpgLeaveOp;
CpgFinalizeOp cpgFinalizeOp;
- static std::string errorStr(cpg_error_t err, const std::string& msg);
+ static std::string errorStr(cs_error_t err, const std::string& msg);
static std::string cantJoinMsg(const Name&);
static std::string cantLeaveMsg(const Name&);
static std::string cantMcastMsg(const Name&);
diff --git a/qpid/cpp/src/qpid/cluster/InitialStatusMap.cpp b/qpid/cpp/src/qpid/cluster/InitialStatusMap.cpp
index eb65005a9e..fc53d1076b 100644
--- a/qpid/cpp/src/qpid/cluster/InitialStatusMap.cpp
+++ b/qpid/cpp/src/qpid/cluster/InitialStatusMap.cpp
@@ -30,9 +30,9 @@ namespace qpid {
namespace cluster {
using namespace std;
-using namespace boost;
using namespace framing::cluster;
using namespace framing;
+using boost::optional;
InitialStatusMap::InitialStatusMap(const MemberId& self_, size_t size_)
: self(self_), completed(), resendNeeded(), size(size_)
diff --git a/qpid/cpp/src/qpid/cluster/UpdateClient.cpp b/qpid/cpp/src/qpid/cluster/UpdateClient.cpp
index 95c64ff060..53818aed85 100644
--- a/qpid/cpp/src/qpid/cluster/UpdateClient.cpp
+++ b/qpid/cpp/src/qpid/cluster/UpdateClient.cpp
@@ -57,6 +57,7 @@
#include "qpid/framing/ClusterConnectionShadowReadyBody.h"
#include "qpid/framing/ClusterConnectionSessionStateBody.h"
#include "qpid/framing/ClusterConnectionConsumerStateBody.h"
+#include "qpid/framing/FieldValue.h"
#include "qpid/framing/enum.h"
#include "qpid/framing/ProtocolVersion.h"
#include "qpid/framing/TypeCode.h"
@@ -73,6 +74,8 @@
namespace qpid {
namespace cluster {
+using std::string;
+
using amqp_0_10::ListCodec;
using broker::Broker;
using broker::Exchange;
@@ -544,7 +547,8 @@ void UpdateClient::updateConsumer(
ci->isNotifyEnabled(),
ci->getPosition(),
ci->getCredit().used().messages,
- ci->getCredit().used().bytes
+ ci->getCredit().used().bytes,
+ ci->getDeliveryCount()
);
consumerNumbering.add(ci.get());
@@ -687,7 +691,15 @@ void UpdateClient::updateLinks() {
void UpdateClient::updateLink(const boost::shared_ptr<broker::Link>& link) {
QPID_LOG(debug, *this << " updating link "
<< link->getHost() << ":" << link->getPort());
- ClusterConnectionProxy(session).config(encode(*link));
+ ClusterConnectionProxy(session).config(encode(*link)); // push the configuration
+ // now push the current state
+ framing::FieldTable state;
+ link->getState(state);
+ std::ostringstream os;
+ os << qpid::Address(link->getTransport(), link->getHost(), link->getPort());
+ ClusterConnectionProxy(session).internalState(std::string("link"),
+ os.str(),
+ state);
}
void UpdateClient::updateBridge(const boost::shared_ptr<broker::Bridge>& bridge) {
diff --git a/qpid/cpp/src/qpid/cluster/types.h b/qpid/cpp/src/qpid/cluster/types.h
index bfb4fd5b9e..c8ffb0b804 100644
--- a/qpid/cpp/src/qpid/cluster/types.h
+++ b/qpid/cpp/src/qpid/cluster/types.h
@@ -34,6 +34,29 @@
extern "C" {
#if defined (HAVE_OPENAIS_CPG_H)
# include <openais/cpg.h>
+
+// Provide translations back to the deprecated definitions in openais
+typedef cpg_error_t cs_error_t;
+#define CS_DISPATCH_ONE CPG_DISPATCH_ONE
+#define CS_DISPATCH_ALL CPG_DISPATCH_ALL
+#define CS_DISPATCH_BLOCKING CPG_DISPATCH_BLOCKING
+#define CS_FLOW_CONTROL_DISABLED CPG_FLOW_CONTROL_DISABLED
+#define CS_FLOW_CONTROL_ENABLED CPG_FLOW_CONTROL_ENABLED
+#define CS_OK CPG_OK
+#define CS_ERR_LIBRARY CPG_ERR_LIBRARY
+#define CS_ERR_TIMEOUT CPG_ERR_TIMEOUT
+#define CS_ERR_TRY_AGAIN CPG_ERR_TRY_AGAIN
+#define CS_ERR_INVALID_PARAM CPG_ERR_INVALID_PARAM
+#define CS_ERR_NO_MEMORY CPG_ERR_NO_MEMORY
+#define CS_ERR_BAD_HANDLE CPG_ERR_BAD_HANDLE
+#define CS_ERR_BUSY CPG_ERR_BUSY
+#define CS_ERR_ACCESS CPG_ERR_ACCESS
+#define CS_ERR_NOT_EXIST CPG_ERR_NOT_EXIST
+#define CS_ERR_EXIST CPG_ERR_EXIST
+#define CS_ERR_NOT_SUPPORTED CPG_ERR_NOT_SUPPORTED
+#define CS_ERR_SECURITY CPG_ERR_SECURITY
+#define CS_ERR_TOO_MANY_GROUPS CPG_ERR_TOO_MANY_GROUPS
+
#elif defined (HAVE_COROSYNC_CPG_H)
# include <corosync/cpg.h>
#else
diff --git a/qpid/cpp/src/qpid/console/ClassKey.cpp b/qpid/cpp/src/qpid/console/ClassKey.cpp
index 7a16113bae..d4b59fc413 100644
--- a/qpid/cpp/src/qpid/console/ClassKey.cpp
+++ b/qpid/cpp/src/qpid/console/ClassKey.cpp
@@ -21,6 +21,7 @@
#include "qpid/console/ClassKey.h"
#include <string.h>
+#include <iostream>
#include <cstdio>
using namespace std;
diff --git a/qpid/cpp/src/qpid/framing/AMQCommandControlBody.h b/qpid/cpp/src/qpid/framing/AMQCommandControlBody.h
deleted file mode 100644
index d12b70a168..0000000000
--- a/qpid/cpp/src/qpid/framing/AMQCommandControlBody.h
+++ /dev/null
@@ -1,70 +0,0 @@
-#ifndef QPID_FRAMING_AMQCOMMANDCONTROLBODY_H
-#define QPID_FRAMING_AMQCOMMANDCONTROLBODY_H
-
-/*
- *
- * 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.
- *
- */
-
-#include "qpid/amqp_0_10/helpers.h"
-#include "qpid/framing/AMQBody.h"
-
-namespace qpid {
-namespace framing {
-
-/**
- * AMQBody wrapper for Command and Control.
- * Temporary measure to fit with old code.
- */
-template <class T> class AMQCommandControlBody : public AMQBody, public T
-{
- public:
- virtual uint8_t type() const { return 100+T::SEGMENT_TYPE; }
-
- virtual void encode(Buffer& buffer) const {
- Codec::encode(buffer.getIterator(), static_cast<const T&>(*this));
- }
- virtual void decode(Buffer& buffer, uint32_t=0) {
- Codec::decode(buffer.getIterator(), static_cast<T&>(*this));
- }
- virtual uint32_t encodedSize() const {
- Codec::size(buffer.getIterator(), static_cast<const T&>(*this));
- }
-
- virtual void print(std::ostream& out) const {
- out << static_cast<const T&>(*this) << endl;
- }
- virtual void AMQBody::accept(AMQBodyConstVisitor&) const { assert(0); }
-};
-
-class CommandBody : public AMQCommandControlBody<amqp_0_10::Command> {
- using Command::accept; // Hide AMQBody::accept
- virtual Command* getCommand() { return this; }
- virtual const Command* getCommand() const { return this; }
-};
-
-class ControlBody : public AMQCommandControlBody<amqp_0_10::Control> {
- using Control::accept; // Hide AMQBody::accept
- virtual Control* getControl() { return this; }
- virtual const Control* getControl() const { return this; }
-};
-
-}} // namespace qpid::framing
-
-#endif /*!QPID_FRAMING_AMQCOMMANDCONTROLBODY_H*/
diff --git a/qpid/cpp/src/qpid/framing/AMQContentBody.cpp b/qpid/cpp/src/qpid/framing/AMQContentBody.cpp
index 72f7d9978e..18f6994f8f 100644
--- a/qpid/cpp/src/qpid/framing/AMQContentBody.cpp
+++ b/qpid/cpp/src/qpid/framing/AMQContentBody.cpp
@@ -24,7 +24,7 @@
qpid::framing::AMQContentBody::AMQContentBody(){
}
-qpid::framing::AMQContentBody::AMQContentBody(const string& _data) : data(_data){
+qpid::framing::AMQContentBody::AMQContentBody(const std::string& _data) : data(_data){
}
uint32_t qpid::framing::AMQContentBody::encodedSize() const{
diff --git a/qpid/cpp/src/qpid/framing/AMQContentBody.h b/qpid/cpp/src/qpid/framing/AMQContentBody.h
index e25451e354..148b293a2f 100644
--- a/qpid/cpp/src/qpid/framing/AMQContentBody.h
+++ b/qpid/cpp/src/qpid/framing/AMQContentBody.h
@@ -31,15 +31,15 @@ namespace framing {
class QPID_COMMON_CLASS_EXTERN AMQContentBody : public AMQBody
{
- string data;
+ std::string data;
public:
QPID_COMMON_EXTERN AMQContentBody();
- QPID_COMMON_EXTERN AMQContentBody(const string& data);
+ QPID_COMMON_EXTERN AMQContentBody(const std::string& data);
inline virtual ~AMQContentBody(){}
inline uint8_t type() const { return CONTENT_BODY; };
- inline const string& getData() const { return data; }
- inline string& getData() { return data; }
+ inline const std::string& getData() const { return data; }
+ inline std::string& getData() { return data; }
QPID_COMMON_EXTERN uint32_t encodedSize() const;
QPID_COMMON_EXTERN void encode(Buffer& buffer) const;
QPID_COMMON_EXTERN void decode(Buffer& buffer, uint32_t size);
diff --git a/qpid/cpp/src/qpid/framing/AMQFrame.cpp b/qpid/cpp/src/qpid/framing/AMQFrame.cpp
index 5b9673f0d0..5e065d598c 100644
--- a/qpid/cpp/src/qpid/framing/AMQFrame.cpp
+++ b/qpid/cpp/src/qpid/framing/AMQFrame.cpp
@@ -85,7 +85,7 @@ bool AMQFrame::decode(Buffer& buffer)
{
if(buffer.available() < frameOverhead())
return false;
- buffer.record();
+ uint32_t pos = buffer.getPosition();
uint8_t flags = buffer.getOctet();
uint8_t framing_version = (flags & 0xc0) >> 6;
@@ -115,7 +115,7 @@ bool AMQFrame::decode(Buffer& buffer)
// B,E,b,e flags
uint16_t body_size = frame_size - frameOverhead();
if (buffer.available() < body_size){
- buffer.restore();
+ buffer.setPosition(pos);
return false;
}
diff --git a/qpid/cpp/src/qpid/framing/BodyHandler.cpp b/qpid/cpp/src/qpid/framing/BodyHandler.cpp
deleted file mode 100644
index db302b1e4c..0000000000
--- a/qpid/cpp/src/qpid/framing/BodyHandler.cpp
+++ /dev/null
@@ -1,56 +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.
- *
- */
-#include "qpid/framing/BodyHandler.h"
-#include "qpid/framing/AMQMethodBody.h"
-#include "qpid/framing/AMQHeaderBody.h"
-#include "qpid/framing/AMQContentBody.h"
-#include "qpid/framing/AMQHeartbeatBody.h"
-#include <boost/cast.hpp>
-#include "qpid/framing/reply_exceptions.h"
-#include "qpid/Msg.h"
-
-using namespace qpid::framing;
-using namespace boost;
-
-BodyHandler::~BodyHandler() {}
-
-// TODO aconway 2007-08-13: Replace with visitor.
-void BodyHandler::handleBody(AMQBody* body) {
- switch(body->type())
- {
- case METHOD_BODY:
- handleMethod(polymorphic_downcast<AMQMethodBody*>(body));
- break;
- case HEADER_BODY:
- handleHeader(polymorphic_downcast<AMQHeaderBody*>(body));
- break;
- case CONTENT_BODY:
- handleContent(polymorphic_downcast<AMQContentBody*>(body));
- break;
- case HEARTBEAT_BODY:
- handleHeartbeat(polymorphic_downcast<AMQHeartbeatBody*>(body));
- break;
- default:
- throw FramingErrorException(
- QPID_MSG("Invalid frame type " << body->type()));
- }
-}
-
diff --git a/qpid/cpp/src/qpid/framing/Buffer.cpp b/qpid/cpp/src/qpid/framing/Buffer.cpp
index 5a5bc0325e..b71915aeb7 100644
--- a/qpid/cpp/src/qpid/framing/Buffer.cpp
+++ b/qpid/cpp/src/qpid/framing/Buffer.cpp
@@ -23,27 +23,17 @@
#include "qpid/Msg.h"
#include <string.h>
#include <boost/format.hpp>
+
namespace qpid {
namespace framing {
+using std::string;
+
Buffer::Buffer(char* _data, uint32_t _size)
: size(_size), data(_data), position(0) {
}
-void Buffer::record(){
- r_position = position;
-}
-
-void Buffer::restore(bool reRecord){
- uint32_t savedPosition = position;
-
- position = r_position;
-
- if (reRecord)
- r_position = savedPosition;
-}
-
void Buffer::reset(){
position = 0;
}
diff --git a/qpid/cpp/src/qpid/framing/FieldTable.cpp b/qpid/cpp/src/qpid/framing/FieldTable.cpp
index b696b5e54f..cd38db1ee8 100644
--- a/qpid/cpp/src/qpid/framing/FieldTable.cpp
+++ b/qpid/cpp/src/qpid/framing/FieldTable.cpp
@@ -28,22 +28,69 @@
#include "qpid/Msg.h"
#include <assert.h>
+// The locking rationale in the FieldTable seems a little odd, but it
+// maintains the concurrent guarantees and requirements that were in
+// place before the cachedBytes/cachedSize were added:
+//
+// The FieldTable client code needs to make sure that they call no write
+// operation in parallel with any other operation on the FieldTable.
+// However multiple parallel read operations are safe.
+//
+// To this end the only code that is locked is code that can transparently
+// change the state of the FieldTable during a read only operation.
+// (In other words the code that required the mutable members in the class
+// definition!)
+//
namespace qpid {
+
+using sys::Mutex;
+using sys::ScopedLock;
+
namespace framing {
FieldTable::FieldTable() :
- cachedSize(0)
+ cachedSize(0),
+ newBytes(false)
{
}
-FieldTable::FieldTable(const FieldTable& ft) :
- cachedBytes(ft.cachedBytes),
- cachedSize(ft.cachedSize)
+FieldTable::FieldTable(const FieldTable& ft)
{
+ ScopedLock<Mutex> l(ft.lock); // lock _source_ FieldTable
+
+ cachedBytes = ft.cachedBytes;
+ cachedSize = ft.cachedSize;
+ newBytes = ft.newBytes;
+
// Only copy the values if we have no raw data
// - copying the map is expensive and we can
// reconstruct it if necessary from the raw data
- if (!cachedBytes && !ft.values.empty()) values = ft.values;
+ if (cachedBytes) {
+ newBytes = true;
+ return;
+ }
+ // In practice Encoding the source field table and only copying
+ // the encoded bytes is faster than copying the whole value map.
+ // (Because we nearly always copy a field table internally before
+ // encoding it to send, but don't change it after the copy)
+ if (!ft.values.empty()) {
+ // Side effect of getting encoded size will cache it in ft.cachedSize
+ ft.cachedBytes = boost::shared_array<uint8_t>(new uint8_t[ft.encodedSize()]);
+
+ Buffer buffer((char*)&ft.cachedBytes[0], ft.cachedSize);
+
+ // Cut and paste ahead...
+ buffer.putLong(ft.encodedSize() - 4);
+ buffer.putLong(ft.values.size());
+ for (ValueMap::const_iterator i = ft.values.begin(); i!=ft.values.end(); ++i) {
+ buffer.putShortString(i->first);
+ i->second->encode(buffer);
+ }
+
+ cachedBytes = ft.cachedBytes;
+ cachedSize = ft.cachedSize;
+ newBytes = true;
+ }
}
FieldTable& FieldTable::operator=(const FieldTable& ft)
@@ -52,10 +99,13 @@ FieldTable& FieldTable::operator=(const FieldTable& ft)
values.swap(nft.values);
cachedBytes.swap(nft.cachedBytes);
cachedSize = nft.cachedSize;
+ newBytes = nft.newBytes;
return (*this);
}
uint32_t FieldTable::encodedSize() const {
+ ScopedLock<Mutex> l(lock);
+
if (cachedSize != 0) {
return cachedSize;
}
@@ -146,7 +196,7 @@ void FieldTable::setFloat(const std::string& name, const float value){
flushRawCache();
}
-void FieldTable::setDouble(const std::string& name, double value){
+void FieldTable::setDouble(const std::string& name, const double value){
realDecode();
values[name] = ValuePtr(new DoubleValue(value));
flushRawCache();
@@ -228,20 +278,15 @@ void FieldTable::encode(Buffer& buffer) const {
// If we've still got the input field table
// we can just copy it directly to the output
if (cachedBytes) {
+ ScopedLock<Mutex> l(lock);
buffer.putRawData(&cachedBytes[0], cachedSize);
} else {
- uint32_t p = buffer.getPosition();
buffer.putLong(encodedSize() - 4);
buffer.putLong(values.size());
for (ValueMap::const_iterator i = values.begin(); i!=values.end(); ++i) {
buffer.putShortString(i->first);
i->second->encode(buffer);
}
- // Now create raw bytes in case we are used again
- cachedSize = buffer.getPosition() - p;
- cachedBytes = boost::shared_array<uint8_t>(new uint8_t[cachedSize]);
- buffer.setPosition(p);
- buffer.getRawData(&cachedBytes[0], cachedSize);
}
}
@@ -256,19 +301,23 @@ void FieldTable::decode(Buffer& buffer){
if ((available < len) || (available < 4))
throw IllegalArgumentException(QPID_MSG("Not enough data for field table."));
}
+ ScopedLock<Mutex> l(lock);
// Throw away previous stored values
values.clear();
// Copy data into our buffer
cachedBytes = boost::shared_array<uint8_t>(new uint8_t[len + 4]);
cachedSize = len + 4;
+ newBytes = true;
buffer.setPosition(p);
buffer.getRawData(&cachedBytes[0], cachedSize);
}
void FieldTable::realDecode() const
{
+ ScopedLock<Mutex> l(lock);
+
// If we've got no raw data stored up then nothing to do
- if (!cachedBytes)
+ if (!newBytes)
return;
Buffer buffer((char*)&cachedBytes[0], cachedSize);
@@ -286,10 +335,14 @@ void FieldTable::realDecode() const
values[name] = ValuePtr(value);
}
}
+ newBytes = false;
}
-void FieldTable::flushRawCache() const
+void FieldTable::flushRawCache()
{
+ ScopedLock<Mutex> l(lock);
+ // We can only flush the cache if there are no cached bytes to decode
+ assert(newBytes==false);
// Avoid recreating shared array unless we actually have one.
if (cachedBytes) cachedBytes.reset();
cachedSize = 0;
@@ -319,6 +372,7 @@ void FieldTable::erase(const std::string& name)
void FieldTable::clear()
{
values.clear();
+ newBytes = false;
flushRawCache();
}
diff --git a/qpid/cpp/src/qpid/framing/FrameSet.cpp b/qpid/cpp/src/qpid/framing/FrameSet.cpp
index 255aaf6e6b..9aee7b98b9 100644
--- a/qpid/cpp/src/qpid/framing/FrameSet.cpp
+++ b/qpid/cpp/src/qpid/framing/FrameSet.cpp
@@ -26,7 +26,6 @@
#include "qpid/framing/TypeFilter.h"
using namespace qpid::framing;
-using namespace boost;
FrameSet::FrameSet(const SequenceNumber& _id) : id(_id),contentSize(0),recalculateSize(true) { }
FrameSet::FrameSet(const FrameSet& original) : id(original.id), contentSize(0), recalculateSize(true)
@@ -103,3 +102,7 @@ std::string FrameSet::getContent() const {
getContent(out);
return out;
}
+
+bool FrameSet::hasContent() const {
+ return parts.size() >= 3;
+}
diff --git a/qpid/cpp/src/qpid/framing/FrameSet.h b/qpid/cpp/src/qpid/framing/FrameSet.h
index cae75e5ec8..3b9f60950b 100644
--- a/qpid/cpp/src/qpid/framing/FrameSet.h
+++ b/qpid/cpp/src/qpid/framing/FrameSet.h
@@ -54,6 +54,7 @@ public:
QPID_COMMON_EXTERN void getContent(std::string&) const;
QPID_COMMON_EXTERN std::string getContent() const;
+ QPID_COMMON_EXTERN bool hasContent() const;
bool isContentBearing() const;
diff --git a/qpid/cpp/src/qpid/framing/ProtocolInitiation.cpp b/qpid/cpp/src/qpid/framing/ProtocolInitiation.cpp
index e617015d64..00ddb55a3b 100644
--- a/qpid/cpp/src/qpid/framing/ProtocolInitiation.cpp
+++ b/qpid/cpp/src/qpid/framing/ProtocolInitiation.cpp
@@ -20,6 +20,8 @@
*/
#include "qpid/framing/ProtocolInitiation.h"
+#include <iostream>
+
namespace qpid {
namespace framing {
diff --git a/qpid/cpp/src/qpid/framing/Uuid.cpp b/qpid/cpp/src/qpid/framing/Uuid.cpp
index b3d1e2e1e4..e377c1172d 100644
--- a/qpid/cpp/src/qpid/framing/Uuid.cpp
+++ b/qpid/cpp/src/qpid/framing/Uuid.cpp
@@ -43,6 +43,13 @@ Uuid::Uuid(const uint8_t* data) {
assign(data);
}
+Uuid::Uuid(const std::string& s) {
+ if (s.size() != UNPARSED_SIZE)
+ throw IllegalArgumentException(QPID_MSG("Invalid UUID: " << s));
+ if (uuid_parse(const_cast<char *>(&s[0]), c_array()) != 0)
+ throw IllegalArgumentException(QPID_MSG("Invalid UUID: " << s));
+}
+
void Uuid::assign(const uint8_t* data) {
// This const cast is for Solaris which has a
// uuid_copy that takes a non const 2nd argument
diff --git a/qpid/cpp/src/qpid/framing/amqp_framing.h b/qpid/cpp/src/qpid/framing/amqp_framing.h
index 3a8b39afb5..2e58922364 100644
--- a/qpid/cpp/src/qpid/framing/amqp_framing.h
+++ b/qpid/cpp/src/qpid/framing/amqp_framing.h
@@ -21,7 +21,6 @@
#include "qpid/framing/amqp_types.h"
#include "qpid/framing/AMQFrame.h"
#include "qpid/framing/AMQBody.h"
-#include "qpid/framing/BodyHandler.h"
#include "qpid/framing/AMQMethodBody.h"
#include "qpid/framing/AMQHeaderBody.h"
#include "qpid/framing/AMQContentBody.h"
diff --git a/qpid/cpp/src/qpid/ha/Backup.cpp b/qpid/cpp/src/qpid/ha/Backup.cpp
index 3d65e07202..4b9cef05bc 100644
--- a/qpid/cpp/src/qpid/ha/Backup.cpp
+++ b/qpid/cpp/src/qpid/ha/Backup.cpp
@@ -19,10 +19,10 @@
*
*/
#include "Backup.h"
-#include "Settings.h"
#include "BrokerReplicator.h"
+#include "HaBroker.h"
#include "ReplicatingSubscription.h"
-#include "ConnectionExcluder.h"
+#include "Settings.h"
#include "qpid/Url.h"
#include "qpid/amqp_0_10/Codecs.h"
#include "qpid/broker/Bridge.h"
@@ -33,6 +33,7 @@
#include "qpid/framing/AMQFrame.h"
#include "qpid/framing/FieldTable.h"
#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/sys/SystemInfo.h"
#include "qpid/types/Variant.h"
namespace qpid {
@@ -43,48 +44,72 @@ using namespace broker;
using types::Variant;
using std::string;
-Backup::Backup(broker::Broker& b, const Settings& s) :
- broker(b), settings(s), excluder(new ConnectionExcluder())
+Backup::Backup(HaBroker& hb, const Settings& s) :
+ logPrefix("Backup: "), haBroker(hb), broker(hb.getBroker()), settings(s)
{
- // Empty brokerUrl means delay initialization until setUrl() is called.
+ // Empty brokerUrl means delay initialization until seBrokertUrl() is called.
if (!s.brokerUrl.empty()) initialize(Url(s.brokerUrl));
}
-void Backup::initialize(const Url& url) {
- assert(!url.empty());
- QPID_LOG(notice, "HA: Backup started: " << url);
+bool Backup::isSelf(const Address& a) const {
+ return sys::SystemInfo::isLocalHost(a.host) &&
+ a.port == haBroker.getBroker().getPort(a.protocol);
+}
+
+// Remove my own address from the URL if possible.
+// This isn't 100% reliable given the many ways to specify a host,
+// but should work in most cases. We have additional measures to prevent
+// self-connection in ConnectionObserver
+Url Backup::removeSelf(const Url& brokers) const {
+ Url url;
+ for (Url::const_iterator i = brokers.begin(); i != brokers.end(); ++i)
+ if (!isSelf(*i)) url.push_back(*i);
+ if (url.empty())
+ throw Url::Invalid(logPrefix+"Failover URL is empty");
+ QPID_LOG(debug, logPrefix << "Failover URL (excluding self): " << url);
+ return url;
+}
+
+void Backup::initialize(const Url& brokers) {
+ if (brokers.empty()) throw Url::Invalid("HA broker URL is empty");
+ QPID_LOG(info, logPrefix << "Initialized, broker URL: " << brokers);
+ Url url = removeSelf(brokers);
string protocol = url[0].protocol.empty() ? "tcp" : url[0].protocol;
+ types::Uuid uuid(true);
// Declare the link
std::pair<Link::shared_ptr, bool> result = broker.getLinks().declare(
+ broker::QPID_NAME_PREFIX + string("ha.link.") + uuid.str(),
url[0].host, url[0].port, protocol,
- false, // durable
- settings.mechanism, settings.username, settings.password);
- assert(result.second); // FIXME aconway 2011-11-23: error handling
+ false, // durable
+ settings.mechanism, settings.username, settings.password,
+ false); // amq.failover
+
+ sys::Mutex::ScopedLock l(lock);
link = result.first;
link->setUrl(url);
-
- replicator.reset(new BrokerReplicator(link));
+ replicator.reset(new BrokerReplicator(haBroker, link));
+ replicator->initialize();
broker.getExchanges().registerExchange(replicator);
- broker.getConnectionObservers().add(excluder);
}
+Backup::~Backup() {
+ if (link) link->close();
+ if (replicator.get()) broker.getExchanges().destroy(replicator->getName());
+}
+
+
void Backup::setBrokerUrl(const Url& url) {
// Ignore empty URLs seen during start-up for some tests.
if (url.empty()) return;
- sys::Mutex::ScopedLock l(lock);
- if (link) { // URL changed after we initialized.
- QPID_LOG(info, "HA: Backup failover URL set to " << url);
- link->setUrl(url);
- }
- else {
- initialize(url); // Deferred initialization
+ {
+ sys::Mutex::ScopedLock l(lock);
+ if (link) {
+ QPID_LOG(info, logPrefix << "Broker URL set to: " << url);
+ link->setUrl(removeSelf(url));
+ return;
+ }
}
-}
-
-Backup::~Backup() {
- if (link) link->close();
- if (replicator.get()) broker.getExchanges().destroy(replicator->getName());
- broker.getConnectionObservers().remove(excluder); // This allows client connections.
+ initialize(url); // Deferred initialization
}
}} // namespace qpid::ha
diff --git a/qpid/cpp/src/qpid/ha/Backup.h b/qpid/cpp/src/qpid/ha/Backup.h
index 526b238b82..c3c4fbbbfc 100644
--- a/qpid/cpp/src/qpid/ha/Backup.h
+++ b/qpid/cpp/src/qpid/ha/Backup.h
@@ -36,8 +36,8 @@ class Link;
namespace ha {
class Settings;
-class ConnectionExcluder;
class BrokerReplicator;
+class HaBroker;
/**
* State associated with a backup broker. Manages connections to primary.
@@ -47,19 +47,23 @@ class BrokerReplicator;
class Backup
{
public:
- Backup(broker::Broker&, const Settings&);
+ Backup(HaBroker&, const Settings&);
~Backup();
void setBrokerUrl(const Url&);
private:
+ bool isSelf(const Address& a) const;
+ Url removeSelf(const Url&) const;
void initialize(const Url&);
+ std::string logPrefix;
+
sys::Mutex lock;
+ HaBroker& haBroker;
broker::Broker& broker;
Settings settings;
boost::shared_ptr<broker::Link> link;
boost::shared_ptr<BrokerReplicator> replicator;
- boost::shared_ptr<ConnectionExcluder> excluder;
};
}} // namespace qpid::ha
diff --git a/qpid/cpp/src/qpid/framing/BodyHandler.h b/qpid/cpp/src/qpid/ha/BackupConnectionExcluder.h
index 9ded737195..2b7ddef3f1 100644
--- a/qpid/cpp/src/qpid/framing/BodyHandler.h
+++ b/qpid/cpp/src/qpid/ha/BackupConnectionExcluder.h
@@ -1,5 +1,5 @@
-#ifndef _BodyHandler_
-#define _BodyHandler_
+#ifndef QPID_HA_BACKUPCONNECTIONEXCLUDER_H
+#define QPID_HA_BACKUPCONNECTIONEXCLUDER_H
/*
*
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -22,35 +22,28 @@
*
*/
-#include <boost/shared_ptr.hpp>
+#include "qpid/broker/ConnectionObserver.h"
+#include "qpid/broker/Connection.h"
+#include "qpid/log/Statement.h"
namespace qpid {
-namespace framing {
-class AMQBody;
-class AMQMethodBody;
-class AMQHeaderBody;
-class AMQContentBody;
-class AMQHeartbeatBody;
-
-// TODO aconway 2007-08-10: rework using Visitor pattern?
+namespace ha {
/**
- * Interface to handle incoming frame bodies.
- * Derived classes provide logic for each frame type.
+ * Exclude connections to a backup broker.
*/
-class BodyHandler {
+class BackupConnectionExcluder : public broker::ConnectionObserver
+{
public:
- virtual ~BodyHandler();
- virtual void handleBody(AMQBody* body);
+ void opened(broker::Connection& connection) {
+ // FIXME aconway 2012-06-13: suppress caught error message, make this an info message.
+ QPID_LOG(error, "Backup broker rejected connection "+connection.getMgmtId());
+ throw Exception("Backup broker rejected connection "+connection.getMgmtId());
+ }
- protected:
- virtual void handleMethod(AMQMethodBody*) = 0;
- virtual void handleHeader(AMQHeaderBody*) = 0;
- virtual void handleContent(AMQContentBody*) = 0;
- virtual void handleHeartbeat(AMQHeartbeatBody*) = 0;
+ void closed(broker::Connection&) {}
};
-}}
-
+}} // namespace qpid::ha
-#endif
+#endif /*!QPID_HA_BACKUPCONNECTIONEXCLUDER_H*/
diff --git a/qpid/cpp/src/qpid/ha/BrokerInfo.cpp b/qpid/cpp/src/qpid/ha/BrokerInfo.cpp
new file mode 100644
index 0000000000..c8bd1a14be
--- /dev/null
+++ b/qpid/cpp/src/qpid/ha/BrokerInfo.cpp
@@ -0,0 +1,118 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "BrokerInfo.h"
+#include "qpid/amqp_0_10/Codecs.h"
+#include "qpid/Exception.h"
+#include "qpid/log/Statement.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/FieldValue.h"
+#include <iostream>
+#include <iterator>
+#include <sstream>
+
+namespace qpid {
+namespace ha {
+
+namespace {
+std::string SYSTEM_ID="system-id";
+std::string HOST_NAME="host-name";
+std::string PORT="port";
+std::string STATUS="status";
+}
+
+using types::Uuid;
+using types::Variant;
+using framing::FieldTable;
+
+BrokerInfo::BrokerInfo(const std::string& host, uint16_t port_, const types::Uuid& id) :
+ hostName(host), port(port_), systemId(id)
+{
+ updateLogId();
+}
+
+void BrokerInfo::updateLogId() {
+ std::ostringstream o;
+ o << hostName << ":" << port;
+ logId = o.str();
+}
+
+FieldTable BrokerInfo::asFieldTable() const {
+ Variant::Map m = asMap();
+ FieldTable ft;
+ amqp_0_10::translate(m, ft);
+ return ft;
+}
+
+Variant::Map BrokerInfo::asMap() const {
+ Variant::Map m;
+ m[SYSTEM_ID] = systemId;
+ m[HOST_NAME] = hostName;
+ m[PORT] = port;
+ m[STATUS] = status;
+ return m;
+}
+
+void BrokerInfo::assign(const FieldTable& ft) {
+ Variant::Map m;
+ amqp_0_10::translate(ft, m);
+ assign(m);
+}
+
+namespace {
+const Variant& get(const Variant::Map& m, const std::string& k) {
+ Variant::Map::const_iterator i = m.find(k);
+ if (i == m.end()) throw Exception(
+ QPID_MSG("Missing field '" << k << "' in broker information"));
+ return i->second;
+}
+}
+
+void BrokerInfo::assign(const Variant::Map& m) {
+ systemId = get(m, SYSTEM_ID).asUuid();
+ hostName = get(m, HOST_NAME).asString();
+ port = get(m, PORT).asUint16();
+ status = BrokerStatus(get(m, STATUS).asUint8());
+ updateLogId();
+}
+
+std::ostream& operator<<(std::ostream& o, const BrokerInfo& b) {
+ return o << b.getHostName() << ":" << b.getPort() << "("
+ << printable(b.getStatus()) << ")";
+}
+
+std::ostream& operator<<(std::ostream& o, const BrokerInfo::Set& infos) {
+ std::ostream_iterator<BrokerInfo> out(o, " ");
+ copy(infos.begin(), infos.end(), out);
+ return o;
+}
+
+std::ostream& operator<<(std::ostream& o, const BrokerInfo::Map::value_type& v) {
+ return o << v.second;
+}
+
+std::ostream& operator<<(std::ostream& o, const BrokerInfo::Map& infos) {
+ std::ostream_iterator<BrokerInfo::Map::value_type> out(o, " ");
+ copy(infos.begin(), infos.end(), out);
+ return o;
+}
+
+}}
diff --git a/qpid/cpp/src/qpid/ha/BrokerInfo.h b/qpid/cpp/src/qpid/ha/BrokerInfo.h
new file mode 100644
index 0000000000..642f7c1361
--- /dev/null
+++ b/qpid/cpp/src/qpid/ha/BrokerInfo.h
@@ -0,0 +1,84 @@
+#ifndef QPID_HA_BROKERINFO_H
+#define QPID_HA_BROKERINFO_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "types.h"
+#include "qpid/Url.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/types/Uuid.h"
+#include "qpid/types/Variant.h"
+#include <string>
+#include <iosfwd>
+#include <vector>
+
+namespace qpid {
+namespace ha {
+
+/**
+ * Information about a cluster broker, maintained by the cluster primary.
+ */
+class BrokerInfo
+{
+ public:
+ typedef std::set<BrokerInfo> Set;
+ typedef std::map<types::Uuid, BrokerInfo> Map;
+
+ BrokerInfo() {}
+ BrokerInfo(const std::string& host, uint16_t port_, const types::Uuid& id);
+ BrokerInfo(const framing::FieldTable& ft) { assign(ft); }
+ BrokerInfo(const types::Variant::Map& m) { assign(m); }
+
+ types::Uuid getSystemId() const { return systemId; }
+ std::string getHostName() const { return hostName; }
+ BrokerStatus getStatus() const { return status; }
+ uint16_t getPort() const { return port; }
+ std::string getLogId() const { return logId; }
+
+ void setStatus(BrokerStatus s) { status = s; }
+
+ framing::FieldTable asFieldTable() const;
+ types::Variant::Map asMap() const;
+
+ void assign(const framing::FieldTable&);
+ void assign(const types::Variant::Map&);
+
+ // So it can be put in a set.
+ bool operator<(const BrokerInfo x) const { return systemId < x.systemId; }
+
+ private:
+ void updateLogId();
+ std::string logId;
+ std::string hostName;
+ uint16_t port;
+ types::Uuid systemId;
+ BrokerStatus status;
+};
+
+std::ostream& operator<<(std::ostream&, const BrokerInfo&);
+std::ostream& operator<<(std::ostream&, const BrokerInfo::Set&);
+std::ostream& operator<<(std::ostream&, const BrokerInfo::Map::value_type&);
+std::ostream& operator<<(std::ostream&, const BrokerInfo::Map&);
+
+}} // namespace qpid::ha
+
+#endif /*!QPID_HA_BROKERINFO_H*/
diff --git a/qpid/cpp/src/qpid/ha/BrokerReplicator.cpp b/qpid/cpp/src/qpid/ha/BrokerReplicator.cpp
index 609a3378ad..83c2eaa144 100644
--- a/qpid/cpp/src/qpid/ha/BrokerReplicator.cpp
+++ b/qpid/cpp/src/qpid/ha/BrokerReplicator.cpp
@@ -19,6 +19,7 @@
*
*/
#include "BrokerReplicator.h"
+#include "HaBroker.h"
#include "QueueReplicator.h"
#include "qpid/broker/Broker.h"
#include "qpid/broker/Queue.h"
@@ -36,7 +37,10 @@
#include "qmf/org/apache/qpid/broker/EventQueueDeclare.h"
#include "qmf/org/apache/qpid/broker/EventQueueDelete.h"
#include "qmf/org/apache/qpid/broker/EventSubscribe.h"
+#include "qmf/org/apache/qpid/ha/EventMembersUpdate.h"
#include <algorithm>
+#include <sstream>
+#include <assert.h>
namespace qpid {
namespace ha {
@@ -48,6 +52,7 @@ using qmf::org::apache::qpid::broker::EventExchangeDelete;
using qmf::org::apache::qpid::broker::EventQueueDeclare;
using qmf::org::apache::qpid::broker::EventQueueDelete;
using qmf::org::apache::qpid::broker::EventSubscribe;
+using qmf::org::apache::qpid::ha::EventMembersUpdate;
using namespace framing;
using std::string;
using types::Variant;
@@ -56,7 +61,6 @@ using namespace broker;
namespace {
const string QPID_CONFIGURATION_REPLICATOR("qpid.configuration-replicator");
-const string QPID_REPLICATE("qpid.replicate");
const string CLASS_NAME("_class_name");
const string EVENT("_event");
@@ -71,6 +75,8 @@ const string ARGS("args");
const string ARGUMENTS("arguments");
const string AUTODEL("autoDel");
const string AUTODELETE("autoDelete");
+const string EXCL("excl");
+const string EXCLUSIVE("exclusive");
const string BIND("bind");
const string UNBIND("unbind");
const string BINDING("binding");
@@ -87,8 +93,10 @@ const string QUEUE("queue");
const string RHOST("rhost");
const string TYPE("type");
const string USER("user");
+const string HA_BROKER("habroker");
-const string AGENT_IND_EVENT_ORG_APACHE_QPID_BROKER("agent.ind.event.org_apache_qpid_broker.#");
+const string AGENT_EVENT_BROKER("agent.ind.event.org_apache_qpid_broker.#");
+const string AGENT_EVENT_HA("agent.ind.event.org_apache_qpid_ha.#");
const string QMF2("qmf2");
const string QMF_CONTENT("qmf.content");
const string QMF_DEFAULT_TOPIC("qmf.default.topic");
@@ -100,9 +108,11 @@ const string _PACKAGE_NAME("_package_name");
const string _SCHEMA_ID("_schema_id");
const string OBJECT("OBJECT");
const string ORG_APACHE_QPID_BROKER("org.apache.qpid.broker");
+const string ORG_APACHE_QPID_HA("org.apache.qpid.ha");
const string QMF_DEFAULT_DIRECT("qmf.default.direct");
const string _QUERY_REQUEST("_query_request");
const string BROKER("broker");
+const string MEMBERS("members");
bool isQMFv2(const Message& message) {
const framing::MessageProperties* props = message.getProperties<framing::MessageProperties>();
@@ -113,36 +123,13 @@ template <class T> bool match(Variant::Map& schema) {
return T::match(schema[CLASS_NAME], schema[PACKAGE_NAME]);
}
-enum ReplicateLevel { RL_NONE=0, RL_CONFIGURATION, RL_ALL };
-const string S_NONE="none";
-const string S_CONFIGURATION="configuration";
-const string S_ALL="all";
-
-ReplicateLevel replicateLevel(const string& level) {
- if (level == S_NONE) return RL_NONE;
- if (level == S_CONFIGURATION) return RL_CONFIGURATION;
- if (level == S_ALL) return RL_ALL;
- throw Exception("Invalid value for "+QPID_REPLICATE+": "+level);
-}
-
-ReplicateLevel replicateLevel(const framing::FieldTable& f) {
- if (f.isSet(QPID_REPLICATE)) return replicateLevel(f.getAsString(QPID_REPLICATE));
- else return RL_NONE;
-}
-
-ReplicateLevel replicateLevel(const Variant::Map& m) {
- Variant::Map::const_iterator i = m.find(QPID_REPLICATE);
- if (i != m.end()) return replicateLevel(i->second.asString());
- else return RL_NONE;
-}
-
-void sendQuery(const string className, const string& queueName, SessionHandler& sessionHandler) {
+void sendQuery(const string& packageName, const string& className, const string& queueName, SessionHandler& sessionHandler) {
framing::AMQP_ServerProxy peer(sessionHandler.out);
Variant::Map request;
request[_WHAT] = OBJECT;
Variant::Map schema;
schema[_CLASS_NAME] = className;
- schema[_PACKAGE_NAME] = ORG_APACHE_QPID_BROKER;
+ schema[_PACKAGE_NAME] = packageName;
request[_SCHEMA_ID] = schema;
AMQFrame method((MessageTransferBody(ProtocolVersion(), QMF_DEFAULT_DIRECT, 0, 0)));
@@ -181,15 +168,20 @@ Variant::Map asMapVoid(const Variant& value) {
} // namespace
-BrokerReplicator::~BrokerReplicator() {}
-
-BrokerReplicator::BrokerReplicator(const boost::shared_ptr<Link>& l)
- : Exchange(QPID_CONFIGURATION_REPLICATOR), broker(*l->getBroker()), link(l)
-{
- QPID_LOG(info, "HA: Backup replicating from " <<
- link->getTransport() << ":" << link->getHost() << ":" << link->getPort());
+BrokerReplicator::BrokerReplicator(HaBroker& hb, const boost::shared_ptr<Link>& l)
+ : Exchange(QPID_CONFIGURATION_REPLICATOR),
+ logPrefix("Backup: "), replicationTest(hb.getReplicationTest()),
+ haBroker(hb), broker(hb.getBroker()), link(l),
+ initialized(false)
+{}
+
+void BrokerReplicator::initialize() {
+ // Can't do this in the constructor because we need a shared_ptr to this.
+ types::Uuid uuid(true);
+ const std::string name(QPID_CONFIGURATION_REPLICATOR + ".bridge." + uuid.str());
broker.getLinks().declare(
- link->getHost(), link->getPort(),
+ name, // name for bridge
+ *link, // parent
false, // durable
QPID_CONFIGURATION_REPLICATOR, // src
QPID_CONFIGURATION_REPLICATOR, // dest
@@ -200,32 +192,66 @@ BrokerReplicator::BrokerReplicator(const boost::shared_ptr<Link>& l)
"", // excludes
false, // dynamic
0, // sync?
- boost::bind(&BrokerReplicator::initializeBridge, this, _1, _2)
+ // shared_ptr keeps this in memory until outstanding initializeBridge
+ // calls are run.
+ boost::bind(&BrokerReplicator::initializeBridge, shared_from_this(), _1, _2)
);
}
+BrokerReplicator::~BrokerReplicator() { }
+
// This is called in the connection IO thread when the bridge is started.
void BrokerReplicator::initializeBridge(Bridge& bridge, SessionHandler& sessionHandler) {
- framing::AMQP_ServerProxy peer(sessionHandler.out);
+ qpid::Address primary;
+ link->getRemoteAddress(primary);
string queueName = bridge.getQueueName();
+
+ QPID_LOG(info, logPrefix << (initialized ? "Connecting" : "Failing-over")
+ << " to primary " << primary
+ << " status:" << printable(haBroker.getStatus()));
+ initialized = true;
+
+ switch (haBroker.getStatus()) {
+ case JOINING:
+ haBroker.setStatus(CATCHUP);
+ break;
+ case CATCHUP:
+ break;
+ case READY:
+ break;
+ case RECOVERING:
+ case ACTIVE:
+ assert(0); // Primary does not reconnect.
+ return;
+ case STANDALONE:
+ return;
+ }
+
+ framing::AMQP_ServerProxy peer(sessionHandler.out);
const qmf::org::apache::qpid::broker::ArgsLinkBridge& args(bridge.getArgs());
//declare and bind an event queue
- peer.getQueue().declare(queueName, "", false, false, true, true, FieldTable());
- peer.getExchange().bind(queueName, QMF_DEFAULT_TOPIC, AGENT_IND_EVENT_ORG_APACHE_QPID_BROKER, FieldTable());
+ FieldTable declareArgs;
+ declareArgs.setString(QPID_REPLICATE, printable(NONE).str());
+ peer.getQueue().declare(queueName, "", false, false, true, true, declareArgs);
+ peer.getExchange().bind(queueName, QMF_DEFAULT_TOPIC, AGENT_EVENT_BROKER, FieldTable());
+ peer.getExchange().bind(queueName, QMF_DEFAULT_TOPIC, AGENT_EVENT_HA, FieldTable());
//subscribe to the queue
peer.getMessage().subscribe(queueName, args.i_dest, 1, 0, false, "", 0, FieldTable());
peer.getMessage().flow(args.i_dest, 0, 0xFFFFFFFF);
peer.getMessage().flow(args.i_dest, 1, 0xFFFFFFFF);
- //issue a query request for queues and another for exchanges using event queue as the reply-to address
- sendQuery(QUEUE, queueName, sessionHandler);
- sendQuery(EXCHANGE, queueName, sessionHandler);
- sendQuery(BINDING, queueName, sessionHandler);
- QPID_LOG(debug, "HA: Backup activated configuration bridge: " << queueName);
+ // Issue a query request for queues, exchanges, bindings and the habroker
+ // using event queue as the reply-to address
+ sendQuery(ORG_APACHE_QPID_HA, HA_BROKER, queueName, sessionHandler);
+ sendQuery(ORG_APACHE_QPID_BROKER, QUEUE, queueName, sessionHandler);
+ sendQuery(ORG_APACHE_QPID_BROKER, EXCHANGE, queueName, sessionHandler);
+ sendQuery(ORG_APACHE_QPID_BROKER, BINDING, queueName, sessionHandler);
+
+ QPID_LOG(debug, logPrefix << "Connected to primary " << primary
+ << "(" << queueName << ")" << " status:" << printable(haBroker.getStatus()));
}
-// FIXME aconway 2011-12-02: error handling in route.
void BrokerReplicator::route(Deliverable& msg) {
const framing::FieldTable* headers = msg.getMessage().getApplicationHeaders();
Variant::List list;
@@ -235,10 +261,10 @@ void BrokerReplicator::route(Deliverable& msg) {
// decode as list
string content = msg.getMessage().getFrames().getContent();
amqp_0_10::ListCodec::decode(content, list);
-
if (headers->getAsString(QMF_CONTENT) == EVENT) {
for (Variant::List::iterator i = list.begin(); i != list.end(); ++i) {
Variant::Map& map = i->asMap();
+ QPID_LOG(trace, "Broker replicator event: " << map);
Variant::Map& schema = map[SCHEMA_ID].asMap();
Variant::Map& values = map[VALUES].asMap();
if (match<EventQueueDeclare>(schema)) doEventQueueDeclare(values);
@@ -247,130 +273,144 @@ void BrokerReplicator::route(Deliverable& msg) {
else if (match<EventExchangeDelete>(schema)) doEventExchangeDelete(values);
else if (match<EventBind>(schema)) doEventBind(values);
else if (match<EventUnbind>(schema)) doEventUnbind(values);
+ else if (match<EventMembersUpdate>(schema)) doEventMembersUpdate(values);
}
} else if (headers->getAsString(QMF_OPCODE) == QUERY_RESPONSE) {
for (Variant::List::iterator i = list.begin(); i != list.end(); ++i) {
- string type = i->asMap()[SCHEMA_ID].asMap()[CLASS_NAME];
- Variant::Map& values = i->asMap()[VALUES].asMap();
+ Variant::Map& map = i->asMap();
+ QPID_LOG(trace, "Broker replicator response: " << map);
+ string type = map[SCHEMA_ID].asMap()[CLASS_NAME].asString();
+ Variant::Map& values = map[VALUES].asMap();
framing::FieldTable args;
amqp_0_10::translate(asMapVoid(values[ARGUMENTS]), args);
if (type == QUEUE) doResponseQueue(values);
else if (type == EXCHANGE) doResponseExchange(values);
else if (type == BINDING) doResponseBind(values);
- else QPID_LOG(error, "HA: Backup received unknown response type=" << type
- << " values=" << values);
+ else if (type == HA_BROKER) doResponseHaBroker(values);
}
- } else QPID_LOG(error, "HA: Backup received unexpected message: " << *headers);
+ }
} catch (const std::exception& e) {
- QPID_LOG(error, "HA: Backup replication error: " << e.what() << ": while handling: " << list);
+ QPID_LOG(critical, logPrefix << "Configuration failed: " << e.what()
+ << ": while handling: " << list);
+ throw;
}
}
void BrokerReplicator::doEventQueueDeclare(Variant::Map& values) {
- QPID_LOG(debug, "HA: Backup queue declare event " << values);
- string name = values[QNAME].asString();
Variant::Map argsMap = asMapVoid(values[ARGS]);
- if (values[DISP] == CREATED && replicateLevel(argsMap)) {
+ bool autoDel = values[AUTODEL].asBool();
+ bool excl = values[EXCL].asBool();
+ if (values[DISP] == CREATED &&
+ replicationTest.isReplicated(CONFIGURATION, argsMap, autoDel, excl))
+ {
+ string name = values[QNAME].asString();
+ QPID_LOG(debug, logPrefix << "Queue declare event: " << name);
framing::FieldTable args;
amqp_0_10::translate(argsMap, args);
+ // If we already have a queue with this name, replace it.
+ // The queue was definitely created on the primary.
+ if (broker.getQueues().find(name)) {
+ QPID_LOG(warning, logPrefix << "Replacing exsiting queue: " << name);
+ broker.getQueues().destroy(name);
+ }
std::pair<boost::shared_ptr<Queue>, bool> result =
broker.createQueue(
name,
values[DURABLE].asBool(),
- values[AUTODEL].asBool(),
- 0 /*i.e. no owner regardless of exclusivity on master*/,
+ autoDel,
+ 0, // no owner regardless of exclusivity on primary
values[ALTEX].asString(),
args,
values[USER].asString(),
values[RHOST].asString());
- if (result.second) {
- // FIXME aconway 2011-11-22: should delete old queue and
- // re-create from event.
- // Events are always up to date, whereas responses may be
- // out of date.
- QPID_LOG(debug, "HA: Backup created queue: " << name);
- startQueueReplicator(result.first);
- } else {
- // FIXME aconway 2011-12-02: what's the right way to handle this?
- QPID_LOG(warning, "HA: Backup queue already exists: " << name);
- }
+ assert(result.second); // Should be true since we destroyed existing queue above
+ startQueueReplicator(result.first);
}
}
+boost::shared_ptr<QueueReplicator> BrokerReplicator::findQueueReplicator(
+ const std::string& qname)
+{
+ string rname = QueueReplicator::replicatorName(qname);
+ boost::shared_ptr<broker::Exchange> ex = broker.getExchanges().find(rname);
+ return boost::dynamic_pointer_cast<QueueReplicator>(ex);
+}
+
void BrokerReplicator::doEventQueueDelete(Variant::Map& values) {
- QPID_LOG(debug, "HA: Backup queue delete event " << values);
// The remote queue has already been deleted so replicator
// sessions may be closed by a "queue deleted" exception.
string name = values[QNAME].asString();
boost::shared_ptr<Queue> queue = broker.getQueues().find(name);
- if (queue && replicateLevel(queue->getSettings())) {
- QPID_LOG(debug, "HA: Backup deleting queue: " << name);
- string rname = QueueReplicator::replicatorName(name);
- boost::shared_ptr<broker::Exchange> ex = broker.getExchanges().find(rname);
- boost::shared_ptr<QueueReplicator> qr = boost::dynamic_pointer_cast<QueueReplicator>(ex);
- if (qr) qr->deactivate();
- // QueueReplicator's bridge is now queued for destruction but may not
- // actually be destroyed, deleting the exhange
- broker.getExchanges().destroy(rname);
+ if (queue && replicationTest.replicateLevel(queue->getSettings())) {
+ QPID_LOG(debug, logPrefix << "Queue delete event: " << name);
+ boost::shared_ptr<QueueReplicator> qr = findQueueReplicator(name);
+ if (qr) {
+ qr->deactivate();
+ // QueueReplicator's bridge is now queued for destruction but may not
+ // actually be destroyed.
+ broker.getExchanges().destroy(qr->getName());
+ }
broker.deleteQueue(name, values[USER].asString(), values[RHOST].asString());
}
}
void BrokerReplicator::doEventExchangeDeclare(Variant::Map& values) {
- QPID_LOG(debug, "HA: Backup exchange declare event " << values);
Variant::Map argsMap(asMapVoid(values[ARGS]));
- if (values[DISP] == CREATED && replicateLevel(argsMap)) {
+ if (!replicationTest.replicateLevel(argsMap)) return; // Not a replicated exchange.
+ if (values[DISP] == CREATED && replicationTest.replicateLevel(argsMap)) {
string name = values[EXNAME].asString();
+ QPID_LOG(debug, logPrefix << "Exchange declare event: " << name);
framing::FieldTable args;
amqp_0_10::translate(argsMap, args);
- if (broker.createExchange(
+ // If we already have a exchange with this name, replace it.
+ // The exchange was definitely created on the primary.
+ if (broker.getExchanges().find(name)) {
+ broker.getExchanges().destroy(name);
+ QPID_LOG(warning, logPrefix << "Replaced exsiting exchange: " << name);
+ }
+ std::pair<boost::shared_ptr<Exchange>, bool> result =
+ broker.createExchange(
name,
values[EXTYPE].asString(),
values[DURABLE].asBool(),
values[ALTEX].asString(),
args,
values[USER].asString(),
- values[RHOST].asString()).second)
- {
- QPID_LOG(debug, "HA: Backup created exchange: " << name);
- } else {
- // FIXME aconway 2011-11-22: should delete pre-exisitng exchange
- // and re-create from event. See comment in doEventQueueDeclare.
- QPID_LOG(warning, "HA: Backup exchange already exists: " << name);
- }
+ values[RHOST].asString());
+ assert(result.second);
}
}
void BrokerReplicator::doEventExchangeDelete(Variant::Map& values) {
- QPID_LOG(debug, "HA: Backup exchange delete event " << values);
string name = values[EXNAME].asString();
- try {
- boost::shared_ptr<Exchange> exchange = broker.getExchanges().find(name);
- if (exchange && replicateLevel(exchange->getArgs())) {
- QPID_LOG(debug, "HA: Backup deleting exchange:" << name);
- broker.deleteExchange(
- name,
- values[USER].asString(),
- values[RHOST].asString());
- }
- } catch (const framing::NotFoundException&) {}
+ boost::shared_ptr<Exchange> exchange = broker.getExchanges().find(name);
+ if (!exchange) {
+ QPID_LOG(warning, logPrefix << "Exchange delete event, does not exist: " << name);
+ } else if (!replicationTest.replicateLevel(exchange->getArgs())) {
+ QPID_LOG(warning, logPrefix << "Exchange delete event, not replicated: " << name);
+ } else {
+ QPID_LOG(debug, logPrefix << "Exchange delete event:" << name);
+ broker.deleteExchange(
+ name,
+ values[USER].asString(),
+ values[RHOST].asString());
+ }
}
void BrokerReplicator::doEventBind(Variant::Map& values) {
- QPID_LOG(debug, "HA: Backup bind event " << values);
boost::shared_ptr<Exchange> exchange =
broker.getExchanges().find(values[EXNAME].asString());
boost::shared_ptr<Queue> queue =
broker.getQueues().find(values[QNAME].asString());
// We only replicate binds for a replicated queue to replicated
// exchange that both exist locally.
- if (exchange && replicateLevel(exchange->getArgs()) &&
- queue && replicateLevel(queue->getSettings()))
+ if (exchange && replicationTest.replicateLevel(exchange->getArgs()) &&
+ queue && replicationTest.replicateLevel(queue->getSettings()))
{
framing::FieldTable args;
amqp_0_10::translate(asMapVoid(values[ARGS]), args);
string key = values[KEY].asString();
- QPID_LOG(debug, "HA: Backup replicated binding exchange=" << exchange->getName()
+ QPID_LOG(debug, logPrefix << "Bind event: exchange=" << exchange->getName()
<< " queue=" << queue->getName()
<< " key=" << key);
exchange->bind(queue, key, &args);
@@ -378,34 +418,43 @@ void BrokerReplicator::doEventBind(Variant::Map& values) {
}
void BrokerReplicator::doEventUnbind(Variant::Map& values) {
- QPID_LOG(debug, "HA: Backup unbind event " << values);
boost::shared_ptr<Exchange> exchange =
broker.getExchanges().find(values[EXNAME].asString());
boost::shared_ptr<Queue> queue =
broker.getQueues().find(values[QNAME].asString());
// We only replicate unbinds for a replicated queue to replicated
// exchange that both exist locally.
- if (exchange && replicateLevel(exchange->getArgs()) &&
- queue && replicateLevel(queue->getSettings()))
+ if (exchange && replicationTest.replicateLevel(exchange->getArgs()) &&
+ queue && replicationTest.replicateLevel(queue->getSettings()))
{
framing::FieldTable args;
amqp_0_10::translate(asMapVoid(values[ARGS]), args);
string key = values[KEY].asString();
- QPID_LOG(debug, "HA: Backup replicated unbinding exchange=" << exchange->getName()
+ QPID_LOG(debug, logPrefix << "Unbind event: exchange=" << exchange->getName()
<< " queue=" << queue->getName()
<< " key=" << key);
exchange->unbind(queue, key, &args);
}
}
+void BrokerReplicator::doEventMembersUpdate(Variant::Map& values) {
+ Variant::List members = values[MEMBERS].asList();
+ QPID_LOG(debug, logPrefix << "Membership update event: " << members);
+ haBroker.setMembership(members);
+}
+
void BrokerReplicator::doResponseQueue(Variant::Map& values) {
- QPID_LOG(debug, "HA: Backup queue response " << values);
- // FIXME aconway 2011-11-22: more flexible ways & defaults to indicate replication
Variant::Map argsMap(asMapVoid(values[ARGUMENTS]));
- if (!replicateLevel(argsMap)) return;
+ if (!replicationTest.isReplicated(
+ CONFIGURATION,
+ values[ARGUMENTS].asMap(),
+ values[AUTODELETE].asBool(),
+ values[EXCLUSIVE].asBool()))
+ return;
+ string name(values[NAME].asString());
+ QPID_LOG(debug, logPrefix << "Queue response: " << name);
framing::FieldTable args;
amqp_0_10::translate(argsMap, args);
- string name(values[NAME].asString());
std::pair<boost::shared_ptr<Queue>, bool> result =
broker.createQueue(
name,
@@ -416,35 +465,29 @@ void BrokerReplicator::doResponseQueue(Variant::Map& values) {
args,
""/*TODO: who is the user?*/,
""/*TODO: what should we use as connection id?*/);
- if (result.second) {
- QPID_LOG(debug, "HA: Backup created catch-up queue: " << values[NAME]);
- startQueueReplicator(result.first);
- } else {
- // FIXME aconway 2011-11-22: Normal to find queue already
- // exists if we're failing over.
- QPID_LOG(warning, "HA: Backup catch-up queue already exists: " << name);
- }
+ // It is normal for the queue to already exist if we are failing over.
+ if (result.second)
+ startQueueReplicator(result.first);
+ else
+ QPID_LOG(debug, logPrefix << "Queue already replicated: " << name);
}
void BrokerReplicator::doResponseExchange(Variant::Map& values) {
- QPID_LOG(debug, "HA: Backup exchange response " << values);
Variant::Map argsMap(asMapVoid(values[ARGUMENTS]));
- if (!replicateLevel(argsMap)) return;
+ if (!replicationTest.replicateLevel(argsMap)) return;
+ string name = values[NAME].asString();
+ QPID_LOG(debug, logPrefix << "Exchange response: " << name);
framing::FieldTable args;
amqp_0_10::translate(argsMap, args);
- if (broker.createExchange(
- values[NAME].asString(),
- values[TYPE].asString(),
- values[DURABLE].asBool(),
- ""/*TODO: need to include alternate-exchange*/,
- args,
- ""/*TODO: who is the user?*/,
- ""/*TODO: what should we use as connection id?*/).second)
- {
- QPID_LOG(debug, "HA: Backup catch-up exchange: " << values[NAME]);
- } else {
- QPID_LOG(warning, "HA: Backup catch-up exchange already exists: " << values[QNAME]);
- }
+ bool created = broker.createExchange(
+ name,
+ values[TYPE].asString(),
+ values[DURABLE].asBool(),
+ ""/*TODO: need to include alternate-exchange*/,
+ args,
+ ""/*TODO: who is the user?*/,
+ ""/*TODO: what should we use as connection id?*/).second;
+ QPID_LOG_IF(debug, !created, logPrefix << "Exchange already exists: " << name);
}
namespace {
@@ -469,31 +512,53 @@ const std::string QUEUE_REF("queueRef");
} // namespace
void BrokerReplicator::doResponseBind(Variant::Map& values) {
- QPID_LOG(debug, "HA: Backup bind response " << values);
std::string exName = getRefName(EXCHANGE_REF_PREFIX, values[EXCHANGE_REF]);
std::string qName = getRefName(QUEUE_REF_PREFIX, values[QUEUE_REF]);
boost::shared_ptr<Exchange> exchange = broker.getExchanges().find(exName);
boost::shared_ptr<Queue> queue = broker.getQueues().find(qName);
- // FIXME aconway 2011-11-24: more flexible configuration for binding replication.
// Automatically replicate binding if queue and exchange exist and are replicated
- if (exchange && replicateLevel(exchange->getArgs()) &&
- queue && replicateLevel(queue->getSettings()))
+ if (exchange && replicationTest.replicateLevel(exchange->getArgs()) &&
+ queue && replicationTest.replicateLevel(queue->getSettings()))
{
+ string key = values[KEY].asString();
+ QPID_LOG(debug, logPrefix << "Bind response: exchange:" << exName
+ << " queue:" << qName
+ << " key:" << key);
framing::FieldTable args;
amqp_0_10::translate(asMapVoid(values[ARGUMENTS]), args);
- string key = values[KEY].asString();
exchange->bind(queue, key, &args);
- QPID_LOG(debug, "HA: Backup catch-up binding: exchange=" << exchange->getName()
- << " queue=" << queue->getName()
- << " key=" << key);
}
}
-void BrokerReplicator::startQueueReplicator(const boost::shared_ptr<Queue>& queue) {
- if (replicateLevel(queue->getSettings()) == RL_ALL) {
- boost::shared_ptr<QueueReplicator> qr(new QueueReplicator(queue, link));
- broker.getExchanges().registerExchange(qr);
+namespace {
+const string REPLICATE_DEFAULT="replicateDefault";
+}
+
+// Received the ha-broker configuration object for the primary broker.
+void BrokerReplicator::doResponseHaBroker(Variant::Map& values) {
+ try {
+ QPID_LOG(debug, logPrefix << "HA Broker response: " << values);
+ ReplicateLevel mine = haBroker.getSettings().replicateDefault.get();
+ ReplicateLevel primary = replicationTest.replicateLevel(
+ values[REPLICATE_DEFAULT].asString());
+ if (mine != primary)
+ throw Exception(QPID_MSG("Replicate default on backup (" << mine
+ << ") does not match primary (" << primary << ")"));
+ haBroker.setMembership(values[MEMBERS].asList());
+ } catch (const std::exception& e) {
+ QPID_LOG(critical, logPrefix << "Invalid HA Broker response: " << e.what());
+ haBroker.shutdown();
+ }
+}
+
+void BrokerReplicator::startQueueReplicator(const boost::shared_ptr<Queue>& queue)
+{
+ if (replicationTest.replicateLevel(queue->getSettings()) == ALL) {
+ boost::shared_ptr<QueueReplicator> qr(
+ new QueueReplicator(haBroker.getBrokerInfo(), queue, link));
+ if (!broker.getExchanges().registerExchange(qr))
+ throw Exception(QPID_MSG("Duplicate queue replicator " << qr->getName()));
qr->activate();
}
}
diff --git a/qpid/cpp/src/qpid/ha/BrokerReplicator.h b/qpid/cpp/src/qpid/ha/BrokerReplicator.h
index 483c251126..f7439fe892 100644
--- a/qpid/cpp/src/qpid/ha/BrokerReplicator.h
+++ b/qpid/cpp/src/qpid/ha/BrokerReplicator.h
@@ -22,9 +22,13 @@
*
*/
+#include "Counter.h"
+#include "types.h"
+#include "ReplicationTest.h"
#include "qpid/broker/Exchange.h"
#include "qpid/types/Variant.h"
#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
namespace qpid {
@@ -35,7 +39,13 @@ class Bridge;
class SessionHandler;
}
+namespace framing {
+class FieldTable;
+}
+
namespace ha {
+class HaBroker;
+class QueueReplicator;
/**
* Replicate configuration on a backup broker.
@@ -48,20 +58,25 @@ namespace ha {
* THREAD SAFE: Has no mutable state.
*
*/
-class BrokerReplicator : public broker::Exchange
+class BrokerReplicator : public broker::Exchange,
+ public boost::enable_shared_from_this<BrokerReplicator>
{
public:
- BrokerReplicator(const boost::shared_ptr<broker::Link>&);
+ BrokerReplicator(HaBroker&, const boost::shared_ptr<broker::Link>&);
~BrokerReplicator();
- std::string getType() const;
+
+ void initialize();
// Exchange methods
+ std::string getType() const;
bool bind(boost::shared_ptr<broker::Queue>, const std::string&, const framing::FieldTable*);
bool unbind(boost::shared_ptr<broker::Queue>, const std::string&, const framing::FieldTable*);
void route(broker::Deliverable&);
bool isBound(boost::shared_ptr<broker::Queue>, const std::string* const, const framing::FieldTable* const);
private:
+ typedef boost::shared_ptr<QueueReplicator> QueueReplicatorPtr;
+
void initializeBridge(broker::Bridge&, broker::SessionHandler&);
void doEventQueueDeclare(types::Variant::Map& values);
@@ -70,15 +85,22 @@ class BrokerReplicator : public broker::Exchange
void doEventExchangeDelete(types::Variant::Map& values);
void doEventBind(types::Variant::Map&);
void doEventUnbind(types::Variant::Map&);
+ void doEventMembersUpdate(types::Variant::Map&);
void doResponseQueue(types::Variant::Map& values);
void doResponseExchange(types::Variant::Map& values);
void doResponseBind(types::Variant::Map& values);
+ void doResponseHaBroker(types::Variant::Map& values);
+ QueueReplicatorPtr findQueueReplicator(const std::string& qname);
void startQueueReplicator(const boost::shared_ptr<broker::Queue>&);
+ std::string logPrefix;
+ ReplicationTest replicationTest;
+ HaBroker& haBroker;
broker::Broker& broker;
boost::shared_ptr<broker::Link> link;
+ bool initialized;
};
}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/ha/ConnectionObserver.cpp b/qpid/cpp/src/qpid/ha/ConnectionObserver.cpp
new file mode 100644
index 0000000000..a5bacd5e31
--- /dev/null
+++ b/qpid/cpp/src/qpid/ha/ConnectionObserver.cpp
@@ -0,0 +1,83 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "ConnectionObserver.h"
+#include "BrokerInfo.h"
+#include "HaBroker.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/broker/Connection.h"
+#include "qpid/log/Statement.h"
+
+namespace qpid {
+namespace ha {
+
+ConnectionObserver::ConnectionObserver(HaBroker& hb, const types::Uuid& uuid)
+ : haBroker(hb), logPrefix("Connections: "), self(uuid) {}
+
+bool ConnectionObserver::getBrokerInfo(broker::Connection& connection, BrokerInfo& info) {
+ framing::FieldTable ft;
+ if (connection.getClientProperties().getTable(ConnectionObserver::BACKUP_TAG, ft)) {
+ info = BrokerInfo(ft);
+ return true;
+ }
+ return false;
+}
+
+void ConnectionObserver::setObserver(const ObserverPtr& o){
+ sys::Mutex::ScopedLock l(lock);
+ observer = o;
+}
+
+ConnectionObserver::ObserverPtr ConnectionObserver::getObserver() {
+ sys::Mutex::ScopedLock l(lock);
+ return observer;
+}
+
+void ConnectionObserver::opened(broker::Connection& connection) {
+ if (connection.isLink()) return; // Allow outgoing links.
+ if (connection.getClientProperties().isSet(ADMIN_TAG)) {
+ QPID_LOG(debug, logPrefix << "Allowing admin connection: "
+ << connection.getMgmtId());
+ return; // No need to call observer, always allow admins.
+ }
+ BrokerInfo info; // Avoid self connections.
+ if (getBrokerInfo(connection, info)) {
+ if (info.getSystemId() == self) {
+ // FIXME aconway 2012-06-13: suppress caught error message, make this an info message.
+ QPID_LOG(error, "HA broker rejected self connection "+connection.getMgmtId());
+ throw Exception("HA broker rejected self connection "+connection.getMgmtId());
+ }
+
+ }
+ ObserverPtr o(getObserver());
+ if (o) o->opened(connection);
+}
+
+void ConnectionObserver::closed(broker::Connection& connection) {
+ BrokerInfo info;
+ ObserverPtr o(getObserver());
+ if (o) o->closed(connection);
+}
+
+const std::string ConnectionObserver::ADMIN_TAG="qpid.ha-admin";
+const std::string ConnectionObserver::BACKUP_TAG="qpid.ha-backup";
+
+}} // namespace qpid::ha
diff --git a/qpid/cpp/src/qpid/ha/ConnectionObserver.h b/qpid/cpp/src/qpid/ha/ConnectionObserver.h
new file mode 100644
index 0000000000..5c1dabe8f8
--- /dev/null
+++ b/qpid/cpp/src/qpid/ha/ConnectionObserver.h
@@ -0,0 +1,74 @@
+#ifndef QPID_HA_CONNECTIONOBSERVER_H
+#define QPID_HA_CONNECTIONOBSERVER_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "types.h"
+#include "qpid/broker/ConnectionObserver.h"
+#include "qpid/types/Uuid.h"
+#include "qpid/sys/Mutex.h"
+#include "boost/shared_ptr.hpp"
+
+namespace qpid {
+namespace ha {
+class BrokerInfo;
+class HaBroker;
+
+/**
+ * Observes connections, delegates to another ConnectionObserver for
+ * actions specific to primary or backup.
+ *
+ * THREAD SAFE: called in arbitrary connection threads.
+ *
+ * Main role of this class is to provide a continuous observer object
+ * on the connection so we can't lose observations between removing
+ * one observer and adding another.
+ */
+class ConnectionObserver : public broker::ConnectionObserver
+{
+ public:
+ typedef boost::shared_ptr<broker::ConnectionObserver> ObserverPtr;
+
+ static const std::string ADMIN_TAG;
+ static const std::string BACKUP_TAG;
+
+ static bool getBrokerInfo(broker::Connection& connection, BrokerInfo& info);
+
+ ConnectionObserver(HaBroker& haBroker, const types::Uuid& self);
+
+ void setObserver(const ObserverPtr&);
+ ObserverPtr getObserver();
+
+ void opened(broker::Connection& connection);
+ void closed(broker::Connection& connection);
+
+ private:
+ sys::Mutex lock;
+ HaBroker& haBroker;
+ std::string logPrefix;
+ ObserverPtr observer;
+ types::Uuid self;
+};
+
+}} // namespace qpid::ha
+
+#endif /*!QPID_HA_CONNECTIONOBSERVER_H*/
diff --git a/qpid/cpp/src/qpid/ha/ConnectionExcluder.h b/qpid/cpp/src/qpid/ha/Counter.h
index f8f2843a0c..04dd672126 100644
--- a/qpid/cpp/src/qpid/ha/ConnectionExcluder.h
+++ b/qpid/cpp/src/qpid/ha/Counter.h
@@ -1,5 +1,5 @@
-#ifndef QPID_HA_CONNECTIONEXCLUDER_H
-#define QPID_HA_CONNECTIONEXCLUDER_H
+#ifndef QPID_HA_COUNTER_H
+#define QPID_HA_COUNTER_H
/*
*
@@ -22,33 +22,36 @@
*
*/
-#include "qpid/broker/ConnectionObserver.h"
+#include "qpid/sys/AtomicValue.h"
#include <boost/function.hpp>
namespace qpid {
-
-namespace broker {
-class Connection;
-}
-
namespace ha {
/**
- * Exclude normal connections to a backup broker.
- * Admin connections are identified by a special flag in client-properties
- * during connection negotiation.
+ * Keep a count, call a callback when it reaches 0.
*/
-class ConnectionExcluder : public broker::ConnectionObserver
+class Counter
{
public:
- ConnectionExcluder();
+ Counter(boost::function<void()> f) : callback(f) {}
+
+ void operator++() { ++count; }
- void opened(broker::Connection& connection);
+ void operator--() {
+ size_t n = --count;
+ assert(n != size_t(-1)); // No underflow
+ if (n == 0) callback();
+ }
+
+ size_t get() { return count.get(); }
+
+ Counter& operator=(size_t n) { count = n; return *this; }
private:
- static const std::string ADMIN_TAG;
+ boost::function<void()> callback;
+ sys::AtomicValue<size_t> count;
};
-
}} // namespace qpid::ha
-#endif /*!QPID_HA_CONNECTIONEXCLUDER_H*/
+#endif /*!QPID_HA_COUNTER_H*/
diff --git a/qpid/cpp/src/qpid/ha/HaBroker.cpp b/qpid/cpp/src/qpid/ha/HaBroker.cpp
index ad6719f207..395a3ce6fa 100644
--- a/qpid/cpp/src/qpid/ha/HaBroker.cpp
+++ b/qpid/cpp/src/qpid/ha/HaBroker.cpp
@@ -19,21 +19,31 @@
*
*/
#include "Backup.h"
-#include "ConnectionExcluder.h"
+#include "BackupConnectionExcluder.h"
+#include "ConnectionObserver.h"
#include "HaBroker.h"
-#include "Settings.h"
+#include "Primary.h"
+#include "QueueReplicator.h"
#include "ReplicatingSubscription.h"
+#include "Settings.h"
+#include "qpid/amqp_0_10/Codecs.h"
#include "qpid/Exception.h"
#include "qpid/broker/Broker.h"
#include "qpid/broker/Link.h"
#include "qpid/broker/Queue.h"
+#include "qpid/broker/SignalHandler.h"
+#include "qpid/framing/FieldTable.h"
#include "qpid/management/ManagementAgent.h"
+#include "qpid/sys/SystemInfo.h"
+#include "qpid/types/Uuid.h"
+#include "qpid/framing/Uuid.h"
#include "qmf/org/apache/qpid/ha/Package.h"
#include "qmf/org/apache/qpid/ha/ArgsHaBrokerReplicate.h"
-#include "qmf/org/apache/qpid/ha/ArgsHaBrokerSetBrokers.h"
-#include "qmf/org/apache/qpid/ha/ArgsHaBrokerSetPublicBrokers.h"
-#include "qmf/org/apache/qpid/ha/ArgsHaBrokerSetExpectedBackups.h"
+#include "qmf/org/apache/qpid/ha/ArgsHaBrokerSetBrokersUrl.h"
+#include "qmf/org/apache/qpid/ha/ArgsHaBrokerSetPublicUrl.h"
+#include "qmf/org/apache/qpid/ha/EventMembersUpdate.h"
#include "qpid/log/Statement.h"
+#include <boost/shared_ptr.hpp>
namespace qpid {
namespace ha {
@@ -41,91 +51,134 @@ namespace ha {
namespace _qmf = ::qmf::org::apache::qpid::ha;
using namespace management;
using namespace std;
-
-namespace {
-
-const std::string STANDALONE="standalone";
-const std::string CATCH_UP="catch-up";
-const std::string BACKUP="backup";
-const std::string PRIMARY="primary";
-
-} // namespace
-
+using types::Variant;
+using types::Uuid;
+using sys::Mutex;
HaBroker::HaBroker(broker::Broker& b, const Settings& s)
- : broker(b),
+ : logPrefix("Broker: "),
+ broker(b),
+ systemId(broker.getSystem()->getSystemId().data()),
settings(s),
- mgmtObject(0)
+ mgmtObject(0),
+ status(STANDALONE),
+ observer(new ConnectionObserver(*this, systemId)),
+ brokerInfo(broker.getSystem()->getNodeName(),
+ // TODO aconway 2012-05-24: other transports?
+ broker.getPort(broker::Broker::TCP_TRANSPORT), systemId),
+ membership(systemId),
+ replicationTest(s.replicateDefault.get())
{
+ // Set up the management object.
+ ManagementAgent* ma = broker.getManagementAgent();
+ if (settings.cluster && !ma)
+ throw Exception("Cannot start HA: management is disabled");
+ _qmf::Package packageInit(ma);
+ mgmtObject = new _qmf::HaBroker(ma, this, "ha-broker");
+ mgmtObject->set_replicateDefault(settings.replicateDefault.str());
+ mgmtObject->set_systemId(systemId);
+ ma->addObject(mgmtObject);
+
// Register a factory for replicating subscriptions.
broker.getConsumerFactories().add(
boost::shared_ptr<ReplicatingSubscription::Factory>(
new ReplicatingSubscription::Factory()));
- broker.getKnownBrokers = boost::bind(&HaBroker::getKnownBrokers, this);
+ // If we are in a cluster, start as backup in joining state.
+ if (settings.cluster) {
+ status = JOINING;
+ observer->setObserver(boost::shared_ptr<broker::ConnectionObserver>(
+ new BackupConnectionExcluder));
+ broker.getConnectionObservers().add(observer);
+ backup.reset(new Backup(*this, s));
+ broker.getKnownBrokers = boost::bind(&HaBroker::getKnownBrokers, this);
+ }
- ManagementAgent* ma = broker.getManagementAgent();
- if (!ma)
- throw Exception("Cannot start HA: management is disabled");
- _qmf::Package packageInit(ma);
- mgmtObject = new _qmf::HaBroker(ma, this, "ha-broker");
- // FIXME aconway 2012-03-01: should start in catch-up state and move to backup
- // only when caught up.
- mgmtObject->set_status(BACKUP);
- ma->addObject(mgmtObject);
- sys::Mutex::ScopedLock l(lock);
+ // NOTE: lock is not needed in a constructor, but create one
+ // to pass to functions that have a ScopedLock parameter.
+ Mutex::ScopedLock l(lock);
if (!settings.clientUrl.empty()) setClientUrl(Url(settings.clientUrl), l);
if (!settings.brokerUrl.empty()) setBrokerUrl(Url(settings.brokerUrl), l);
+ statusChanged(l);
+
+ QPID_LOG(notice, logPrefix << "Broker starting: " << brokerInfo);
+}
+
+HaBroker::~HaBroker() {
+ QPID_LOG(debug, logPrefix << "Broker shut down: " << brokerInfo);
+ broker.getConnectionObservers().remove(observer);
+}
- // If we are in a cluster, we start in backup mode.
- if (settings.cluster) backup.reset(new Backup(b, s));
+void HaBroker::recover(Mutex::ScopedLock&) {
+ // No longer replicating, close link. Note: link must be closed before we
+ // setStatus(RECOVERING) as that will remove our broker info from the
+ // outgoing link properties so we won't recognize self-connects.
+ backup.reset();
+ setStatus(RECOVERING);
+ BrokerInfo::Set backups = membership.otherBackups();
+ membership.reset(brokerInfo);
+ // Drop the lock, new Primary may call back on activate.
+ Mutex::ScopedUnlock u(lock);
+ primary.reset(new Primary(*this, backups)); // Starts primary-ready check.
}
-HaBroker::~HaBroker() {}
+// Called back from Primary active check.
+void HaBroker::activate() {
+ Mutex::ScopedLock l(lock);
+ activate(l);
+}
+
+void HaBroker::activate(Mutex::ScopedLock&) { setStatus(ACTIVE); }
Manageable::status_t HaBroker::ManagementMethod (uint32_t methodId, Args& args, string&) {
- sys::Mutex::ScopedLock l(lock);
+ Mutex::ScopedLock l(lock);
switch (methodId) {
case _qmf::HaBroker::METHOD_PROMOTE: {
- if (backup.get()) { // I am a backup
- // NOTE: resetting backup allows client connections, so any
- // primary state should be set up here before backup.reset()
- backup.reset();
- QPID_LOG(notice, "HA: Primary promoted from backup");
- mgmtObject->set_status(PRIMARY);
+ switch (status) {
+ case JOINING: recover(l); break;
+ case CATCHUP:
+ // FIXME aconway 2012-04-27: don't allow promotion in catch-up
+ // QPID_LOG(error, logPrefix << "Still catching up, cannot be promoted.");
+ // throw Exception("Still catching up, cannot be promoted.");
+ recover(l);
+ break;
+ case READY: recover(l); break;
+ case RECOVERING: break;
+ case ACTIVE: break;
+ case STANDALONE: break;
}
break;
}
- case _qmf::HaBroker::METHOD_SETBROKERS: {
- setBrokerUrl(Url(dynamic_cast<_qmf::ArgsHaBrokerSetBrokers&>(args).i_url), l);
+ case _qmf::HaBroker::METHOD_SETBROKERSURL: {
+ setBrokerUrl(Url(dynamic_cast<_qmf::ArgsHaBrokerSetBrokersUrl&>(args).i_url), l);
break;
}
- case _qmf::HaBroker::METHOD_SETPUBLICBROKERS: {
- setClientUrl(Url(dynamic_cast<_qmf::ArgsHaBrokerSetPublicBrokers&>(args).i_url), l);
+ case _qmf::HaBroker::METHOD_SETPUBLICURL: {
+ setClientUrl(Url(dynamic_cast<_qmf::ArgsHaBrokerSetPublicUrl&>(args).i_url), l);
break;
}
- case _qmf::HaBroker::METHOD_SETEXPECTEDBACKUPS: {
- setExpectedBackups(dynamic_cast<_qmf::ArgsHaBrokerSetExpectedBackups&>(args).i_expectedBackups, l);
- break;
- }
case _qmf::HaBroker::METHOD_REPLICATE: {
_qmf::ArgsHaBrokerReplicate& bq_args =
dynamic_cast<_qmf::ArgsHaBrokerReplicate&>(args);
- QPID_LOG(debug, "HA replicating individual queue "<< bq_args.i_queue << " from " << bq_args.i_broker);
+ QPID_LOG(debug, logPrefix << "Replicate individual queue "
+ << bq_args.i_queue << " from " << bq_args.i_broker);
boost::shared_ptr<broker::Queue> queue = broker.getQueues().get(bq_args.i_queue);
Url url(bq_args.i_broker);
string protocol = url[0].protocol.empty() ? "tcp" : url[0].protocol;
+ Uuid uuid(true);
std::pair<broker::Link::shared_ptr, bool> result = broker.getLinks().declare(
+ broker::QPID_NAME_PREFIX + string("ha.link.") + uuid.str(),
url[0].host, url[0].port, protocol,
false, // durable
settings.mechanism, settings.username, settings.password);
boost::shared_ptr<broker::Link> link = result.first;
link->setUrl(url);
// Create a queue replicator
- boost::shared_ptr<QueueReplicator> qr(new QueueReplicator(queue, link));
- broker.getExchanges().registerExchange(qr);
+ boost::shared_ptr<QueueReplicator> qr(
+ new QueueReplicator(brokerInfo, queue, link));
qr->activate();
+ broker.getExchanges().registerExchange(qr);
break;
}
@@ -135,38 +188,149 @@ Manageable::status_t HaBroker::ManagementMethod (uint32_t methodId, Args& args,
return Manageable::STATUS_OK;
}
-void HaBroker::setClientUrl(const Url& url, const sys::Mutex::ScopedLock& l) {
+void HaBroker::setClientUrl(const Url& url, Mutex::ScopedLock& l) {
if (url.empty()) throw Exception("Invalid empty URL for HA client failover");
clientUrl = url;
updateClientUrl(l);
}
-void HaBroker::updateClientUrl(const sys::Mutex::ScopedLock&) {
+void HaBroker::updateClientUrl(Mutex::ScopedLock&) {
Url url = clientUrl.empty() ? brokerUrl : clientUrl;
- assert(!url.empty());
- mgmtObject->set_publicBrokers(url.str());
+ if (url.empty()) throw Url::Invalid("HA client URL is empty");
+ mgmtObject->set_publicUrl(url.str());
knownBrokers.clear();
knownBrokers.push_back(url);
- QPID_LOG(debug, "HA: Setting client URL to: " << url);
+ QPID_LOG(debug, logPrefix << "Setting client URL to: " << url);
}
-void HaBroker::setBrokerUrl(const Url& url, const sys::Mutex::ScopedLock& l) {
- if (url.empty()) throw Exception("Invalid empty URL for HA broker failover");
- QPID_LOG(debug, "HA: Setting broker URL to: " << url);
+void HaBroker::setBrokerUrl(const Url& url, Mutex::ScopedLock& l) {
+ if (url.empty()) throw Url::Invalid("HA broker URL is empty");
brokerUrl = url;
- mgmtObject->set_brokers(brokerUrl.str());
+ mgmtObject->set_brokersUrl(brokerUrl.str());
if (backup.get()) backup->setBrokerUrl(brokerUrl);
// Updating broker URL also updates defaulted client URL:
if (clientUrl.empty()) updateClientUrl(l);
}
-void HaBroker::setExpectedBackups(size_t n, const sys::Mutex::ScopedLock&) {
- expectedBackups = n;
- mgmtObject->set_expectedBackups(n);
-}
-
std::vector<Url> HaBroker::getKnownBrokers() const {
return knownBrokers;
}
+void HaBroker::shutdown() {
+ QPID_LOG(critical, logPrefix << "Critical error, shutting down.");
+ broker.shutdown();
+}
+
+BrokerStatus HaBroker::getStatus() const {
+ Mutex::ScopedLock l(lock);
+ return status;
+}
+
+void HaBroker::setStatus(BrokerStatus newStatus) {
+ Mutex::ScopedLock l(lock);
+ setStatus(newStatus, l);
+}
+
+namespace {
+bool checkTransition(BrokerStatus from, BrokerStatus to) {
+ // Legal state transitions. Initial state is JOINING, ACTIVE is terminal.
+ static const BrokerStatus TRANSITIONS[][2] = {
+ { CATCHUP, RECOVERING }, // FIXME aconway 2012-04-27: illegal transition, allow while fixing behavior
+ { JOINING, CATCHUP }, // Connected to primary
+ { JOINING, RECOVERING }, // Chosen as initial primary.
+ { CATCHUP, READY }, // Caught up all queues, ready to take over.
+ { READY, RECOVERING }, // Chosen as new primary
+ { RECOVERING, ACTIVE }
+ };
+ static const size_t N = sizeof(TRANSITIONS)/sizeof(TRANSITIONS[0]);
+ for (size_t i = 0; i < N; ++i) {
+ if (TRANSITIONS[i][0] == from && TRANSITIONS[i][1] == to)
+ return true;
+ }
+ return false;
+}
+} // namespace
+
+void HaBroker::setStatus(BrokerStatus newStatus, Mutex::ScopedLock& l) {
+ QPID_LOG(notice, logPrefix << "Status change: "
+ << printable(status) << " -> " << printable(newStatus));
+ bool legal = checkTransition(status, newStatus);
+ assert(legal);
+ if (!legal) {
+ QPID_LOG(critical, logPrefix << "Illegal state transition: "
+ << printable(status) << " -> " << printable(newStatus));
+ shutdown();
+ }
+ status = newStatus;
+ statusChanged(l);
+}
+
+void HaBroker::statusChanged(Mutex::ScopedLock& l) {
+ mgmtObject->set_status(printable(status).str());
+ brokerInfo.setStatus(status);
+ setLinkProperties(l);
+}
+
+void HaBroker::membershipUpdated(const Variant::List& brokers) {
+ // No lock, these are thread-safe.
+ mgmtObject->set_members(brokers);
+ broker.getManagementAgent()->raiseEvent(_qmf::EventMembersUpdate(brokers));
+}
+
+void HaBroker::setMembership(const Variant::List& brokers) {
+ Mutex::ScopedLock l(lock);
+ membership.assign(brokers);
+ BrokerInfo info;
+ // Check if my own status has been updated to READY
+ if (getStatus() == CATCHUP &&
+ membership.get(systemId, info) && info.getStatus() == READY)
+ setStatus(READY, l);
+ membershipUpdated(brokers);
+}
+
+void HaBroker::resetMembership(const BrokerInfo& b) {
+ Variant::List members;
+ {
+ Mutex::ScopedLock l(lock);
+ membership.reset(b);
+ members = membership.asList();
+ }
+ membershipUpdated(members);
+}
+
+void HaBroker::addBroker(const BrokerInfo& b) {
+ Variant::List members;
+ {
+ Mutex::ScopedLock l(lock);
+ membership.add(b);
+ members = membership.asList();
+ }
+ membershipUpdated(members);
+}
+
+void HaBroker::removeBroker(const Uuid& id) {
+ Variant::List members;
+ {
+ Mutex::ScopedLock l(lock);
+ membership.remove(id);
+ members = membership.asList();
+ }
+ membershipUpdated(members);
+}
+
+void HaBroker::setLinkProperties(Mutex::ScopedLock&) {
+ framing::FieldTable linkProperties = broker.getLinkClientProperties();
+ if (isBackup(status)) {
+ // If this is a backup then any outgoing links are backup
+ // links and need to be tagged.
+ linkProperties.setTable(ConnectionObserver::BACKUP_TAG, brokerInfo.asFieldTable());
+ }
+ else {
+ // If this is a primary then any outgoing links are federation links
+ // and should not be tagged.
+ linkProperties.erase(ConnectionObserver::BACKUP_TAG);
+ }
+ broker.setLinkClientProperties(linkProperties);
+}
+
}} // namespace qpid::ha
diff --git a/qpid/cpp/src/qpid/ha/HaBroker.h b/qpid/cpp/src/qpid/ha/HaBroker.h
index 4f4ee4c944..28fe3c755e 100644
--- a/qpid/cpp/src/qpid/ha/HaBroker.h
+++ b/qpid/cpp/src/qpid/ha/HaBroker.h
@@ -22,22 +22,41 @@
*
*/
+#include "BrokerInfo.h"
+#include "Membership.h"
+#include "types.h"
+#include "ReplicationTest.h"
#include "Settings.h"
#include "qpid/Url.h"
#include "qpid/sys/Mutex.h"
#include "qmf/org/apache/qpid/ha/HaBroker.h"
#include "qpid/management/Manageable.h"
+#include "qpid/types/Variant.h"
#include <memory>
+#include <set>
+#include <boost/shared_ptr.hpp>
namespace qpid {
+
+namespace types {
+class Variant;
+}
+
namespace broker {
class Broker;
+class Queue;
+}
+namespace framing {
+class FieldTable;
}
+
namespace ha {
class Backup;
+class ConnectionObserver;
+class Primary;
/**
- * HA state and actions associated with a broker.
+ * HA state and actions associated with a HA broker. Holds all the management info.
*
* THREAD SAFE: may be called in arbitrary broker IO or timer threads.
*/
@@ -52,23 +71,61 @@ class HaBroker : public management::Manageable
management::Manageable::status_t ManagementMethod (
uint32_t methodId, management::Args& args, std::string& text);
+ broker::Broker& getBroker() { return broker; }
+ const Settings& getSettings() const { return settings; }
+
+ /** Shut down the broker. Caller should log a critical error message. */
+ void shutdown();
+
+ BrokerStatus getStatus() const;
+ void setStatus(BrokerStatus);
+ void activate();
+
+ Backup* getBackup() { return backup.get(); }
+ ReplicationTest getReplicationTest() const { return replicationTest; }
+
+ boost::shared_ptr<ConnectionObserver> getObserver() { return observer; }
+
+ const BrokerInfo& getBrokerInfo() const { return brokerInfo; }
+
+ void setMembership(const types::Variant::List&); // Set membership from list.
+ void resetMembership(const BrokerInfo& b); // Reset to contain just one member.
+ void addBroker(const BrokerInfo& b); // Add a broker to the membership.
+ void removeBroker(const types::Uuid& id); // Remove a broker from membership.
+
private:
- void setClientUrl(const Url&, const sys::Mutex::ScopedLock&);
- void setBrokerUrl(const Url&, const sys::Mutex::ScopedLock&);
- void setExpectedBackups(size_t, const sys::Mutex::ScopedLock&);
- void updateClientUrl(const sys::Mutex::ScopedLock&);
- bool isPrimary(const sys::Mutex::ScopedLock&) { return !backup.get(); }
+ void setClientUrl(const Url&, sys::Mutex::ScopedLock&);
+ void setBrokerUrl(const Url&, sys::Mutex::ScopedLock&);
+ void updateClientUrl(sys::Mutex::ScopedLock&);
+
+ bool isPrimary(sys::Mutex::ScopedLock&) { return !backup.get(); }
+
+ void setStatus(BrokerStatus, sys::Mutex::ScopedLock&);
+ void recover(sys::Mutex::ScopedLock&);
+ void activate(sys::Mutex::ScopedLock&);
+ void statusChanged(sys::Mutex::ScopedLock&);
+ void setLinkProperties(sys::Mutex::ScopedLock&);
+
std::vector<Url> getKnownBrokers() const;
+ void membershipUpdated(const types::Variant::List&);
+
+ std::string logPrefix;
broker::Broker& broker;
+ types::Uuid systemId;
const Settings settings;
- sys::Mutex lock;
+ mutable sys::Mutex lock;
std::auto_ptr<Backup> backup;
+ std::auto_ptr<Primary> primary;
qmf::org::apache::qpid::ha::HaBroker* mgmtObject;
Url clientUrl, brokerUrl;
std::vector<Url> knownBrokers;
- size_t expectedBackups;
+ BrokerStatus status;
+ boost::shared_ptr<ConnectionObserver> observer;
+ BrokerInfo brokerInfo;
+ Membership membership;
+ ReplicationTest replicationTest;
};
}} // namespace qpid::ha
diff --git a/qpid/cpp/src/qpid/ha/HaPlugin.cpp b/qpid/cpp/src/qpid/ha/HaPlugin.cpp
index 6a43b591b0..42758c4689 100644
--- a/qpid/cpp/src/qpid/ha/HaPlugin.cpp
+++ b/qpid/cpp/src/qpid/ha/HaPlugin.cpp
@@ -20,7 +20,7 @@
#include "qpid/Plugin.h"
#include "qpid/Options.h"
#include "qpid/broker/Broker.h"
-
+#include <boost/bind.hpp>
namespace qpid {
namespace ha {
@@ -33,12 +33,13 @@ struct Options : public qpid::Options {
addOptions()
("ha-cluster", optValue(settings.cluster, "yes|no"),
"Join a HA active/passive cluster.")
- ("ha-brokers", optValue(settings.brokerUrl,"URL"),
- "URL that backup brokers use to connect and fail over.")
- ("ha-public-brokers", optValue(settings.clientUrl,"URL"),
- "URL that clients use to connect and fail over, defaults to ha-brokers.")
- ("ha-expected-backups", optValue(settings.expectedBackups, "N"),
- "Number of backups expected to be active in the HA cluster.")
+ ("ha-brokers-url", optValue(settings.brokerUrl,"URL"),
+ "URL with address of each broker in the cluster.")
+ ("ha-public-url", optValue(settings.clientUrl,"URL"),
+ "URL advertized to clients to connect to the cluster.")
+ ("ha-replicate",
+ optValue(settings.replicateDefault, "LEVEL"),
+ "Replication level for creating queues and exchanges if there is no qpid.replicate argument supplied. LEVEL is 'none', 'configuration' or 'all'")
("ha-username", optValue(settings.username, "USER"),
"Username for connections between HA brokers")
("ha-password", optValue(settings.password, "PASS"),
@@ -63,10 +64,17 @@ struct HaPlugin : public Plugin {
void initialize(Plugin::Target& target) {
broker::Broker* broker = dynamic_cast<broker::Broker*>(&target);
- if (broker) haBroker.reset(new ha::HaBroker(*broker, settings));
+ if (broker) {
+ haBroker.reset(new ha::HaBroker(*broker, settings));
+ broker->addFinalizer(boost::bind(&HaPlugin::finalize, this));
+ }
+ }
+
+ void finalize() {
+ haBroker.reset();
}
};
-static HaPlugin instance; // Static initialization.
+HaPlugin instance; // Static initialization.
}} // namespace qpid::ha
diff --git a/qpid/cpp/src/qpid/ha/Membership.cpp b/qpid/cpp/src/qpid/ha/Membership.cpp
new file mode 100644
index 0000000000..b85c4d4164
--- /dev/null
+++ b/qpid/cpp/src/qpid/ha/Membership.cpp
@@ -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.
+ *
+ */
+#include "Membership.h"
+#include <boost/bind.hpp>
+#include <iostream>
+#include <iterator>
+
+namespace qpid {
+namespace ha {
+
+
+void Membership::reset(const BrokerInfo& b) {
+ brokers.clear();
+ brokers[b.getSystemId()] = b;
+}
+
+void Membership::add(const BrokerInfo& b) {
+ brokers[b.getSystemId()] = b;
+}
+
+
+void Membership::remove(const types::Uuid& id) {
+ BrokerInfo::Map::iterator i = brokers.find(id);
+ if (i != brokers.end()) {
+ brokers.erase(i);
+ }
+}
+
+bool Membership::contains(const types::Uuid& id) {
+ return brokers.find(id) != brokers.end();
+}
+
+void Membership::assign(const types::Variant::List& list) {
+ brokers.clear();
+ for (types::Variant::List::const_iterator i = list.begin(); i != list.end(); ++i) {
+ BrokerInfo b(i->asMap());
+ brokers[b.getSystemId()] = b;
+ }
+}
+
+types::Variant::List Membership::asList() const {
+ types::Variant::List list;
+ for (BrokerInfo::Map::const_iterator i = brokers.begin(); i != brokers.end(); ++i)
+ list.push_back(i->second.asMap());
+ return list;
+}
+
+BrokerInfo::Set Membership::otherBackups() const {
+ BrokerInfo::Set result;
+ for (BrokerInfo::Map::const_iterator i = brokers.begin(); i != brokers.end(); ++i)
+ if (isBackup(i->second.getStatus()) && i->second.getSystemId() != self)
+ result.insert(i->second);
+ return result;
+}
+
+bool Membership::get(const types::Uuid& id, BrokerInfo& result) {
+ BrokerInfo::Map::iterator i = brokers.find(id);
+ if (i == brokers.end()) return false;
+ result = i->second;
+ return true;
+}
+
+}} // namespace qpid::ha
diff --git a/qpid/cpp/src/qpid/ha/Membership.h b/qpid/cpp/src/qpid/ha/Membership.h
new file mode 100644
index 0000000000..41245f0e06
--- /dev/null
+++ b/qpid/cpp/src/qpid/ha/Membership.h
@@ -0,0 +1,65 @@
+#ifndef QPID_HA_MEMBERSHIP_H
+#define QPID_HA_MEMBERSHIP_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "BrokerInfo.h"
+#include "types.h"
+#include "qpid/framing/Uuid.h"
+#include "qpid/log/Statement.h"
+#include "qpid/types/Variant.h"
+#include <boost/function.hpp>
+#include <set>
+#include <vector>
+#include <iosfwd>
+namespace qpid {
+namespace ha {
+
+/**
+ * Keep track of the brokers in the membership.
+ * THREAD UNSAFE: caller must serialize
+ */
+class Membership
+{
+ public:
+ Membership(const types::Uuid& self_) : self(self_) {}
+
+ void reset(const BrokerInfo& b); ///< Reset to contain just one member.
+ void add(const BrokerInfo& b);
+ void remove(const types::Uuid& id);
+ bool contains(const types::Uuid& id);
+ /** Return IDs of all backups other than self */
+ BrokerInfo::Set otherBackups() const;
+
+ void assign(const types::Variant::List&);
+ types::Variant::List asList() const;
+
+ bool get(const types::Uuid& id, BrokerInfo& result);
+
+ private:
+ types::Uuid self;
+ BrokerInfo::Map brokers;
+};
+
+}} // namespace qpid::ha
+
+#endif /*!QPID_HA_MEMBERSHIP_H*/
diff --git a/qpid/cpp/src/qpid/ha/Primary.cpp b/qpid/cpp/src/qpid/ha/Primary.cpp
new file mode 100644
index 0000000000..b315142e70
--- /dev/null
+++ b/qpid/cpp/src/qpid/ha/Primary.cpp
@@ -0,0 +1,189 @@
+/*
+ *
+ * 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.
+ *
+ */
+#include "Backup.h"
+#include "HaBroker.h"
+#include "Primary.h"
+#include "ReplicationTest.h"
+#include "ReplicatingSubscription.h"
+#include "RemoteBackup.h"
+#include "ConnectionObserver.h"
+#include "qpid/assert.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/ConfigurationObserver.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/log/Statement.h"
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace ha {
+
+using sys::Mutex;
+
+namespace {
+// No-op connection observer, allows all connections.
+class PrimaryConnectionObserver : public broker::ConnectionObserver
+{
+ public:
+ PrimaryConnectionObserver(Primary& p) : primary(p) {}
+ void opened(broker::Connection& c) { primary.opened(c); }
+ void closed(broker::Connection& c) { primary.closed(c); }
+ private:
+ Primary& primary;
+};
+
+class PrimaryConfigurationObserver : public broker::ConfigurationObserver
+{
+ public:
+ PrimaryConfigurationObserver(Primary& p) : primary(p) {}
+ void queueCreate(const Primary::QueuePtr& q) { primary.queueCreate(q); }
+ void queueDestroy(const Primary::QueuePtr& q) { primary.queueDestroy(q); }
+ private:
+ Primary& primary;
+};
+
+} // namespace
+
+Primary* Primary::instance = 0;
+
+Primary::Primary(HaBroker& hb, const BrokerInfo::Set& expect) :
+ haBroker(hb), logPrefix("Primary: "), active(false)
+{
+ assert(instance == 0);
+ instance = this; // Let queue replicators find us.
+ if (expect.empty()) {
+ QPID_LOG(debug, logPrefix << "Promoted, no expected backups");
+ }
+ else {
+ // NOTE: RemoteBackups must be created before we set the ConfigurationObserver
+ // orr ConnectionObserver so that there is no client activity while
+ // the QueueGuards are created.
+ QPID_LOG(debug, logPrefix << "Promoted, expected backups: " << expect);
+ for (BrokerInfo::Set::const_iterator i = expect.begin(); i != expect.end(); ++i) {
+ bool guard = true; // Create queue guards immediately for expected backups.
+ boost::shared_ptr<RemoteBackup> backup(
+ new RemoteBackup(*i, haBroker.getBroker(), haBroker.getReplicationTest(), guard));
+ backups[i->getSystemId()] = backup;
+ if (!backup->isReady()) initialBackups.insert(backup);
+ }
+ }
+
+ configurationObserver.reset(new PrimaryConfigurationObserver(*this));
+ haBroker.getBroker().getConfigurationObservers().add(configurationObserver);
+
+ Mutex::ScopedLock l(lock); // We are now active as a configurationObserver
+ checkReady(l);
+ // Allow client connections
+ connectionObserver.reset(new PrimaryConnectionObserver(*this));
+ haBroker.getObserver()->setObserver(connectionObserver);
+}
+
+Primary::~Primary() {
+ haBroker.getObserver()->setObserver(boost::shared_ptr<broker::ConnectionObserver>());
+ haBroker.getBroker().getConfigurationObservers().remove(configurationObserver);
+}
+
+void Primary::checkReady(Mutex::ScopedLock&) {
+ if (!active && initialBackups.empty()) {
+ active = true;
+ QPID_LOG(notice, logPrefix << "All initial backups are ready.");
+ Mutex::ScopedUnlock u(lock); // Don't hold lock across callback
+ haBroker.activate();
+ }
+}
+
+void Primary::checkReady(BackupMap::iterator i, Mutex::ScopedLock& l) {
+ if (i != backups.end() && i->second->isReady()) {
+ BrokerInfo info = i->second->getBrokerInfo();
+ info.setStatus(READY);
+ haBroker.addBroker(info);
+ initialBackups.erase(i->second);
+ checkReady(l);
+ }
+}
+
+void Primary::readyReplica(const ReplicatingSubscription& rs) {
+ sys::Mutex::ScopedLock l(lock);
+ BackupMap::iterator i = backups.find(rs.getBrokerInfo().getSystemId());
+ if (i != backups.end()) {
+ i->second->ready(rs.getQueue());
+ checkReady(i, l);
+ }
+}
+
+void Primary::queueCreate(const QueuePtr& q) {
+ // Throw if there is an invalid replication level in the queue settings.
+ haBroker.getReplicationTest().replicateLevel(q->getSettings());
+ Mutex::ScopedLock l(lock);
+ for (BackupMap::iterator i = backups.begin(); i != backups.end(); ++i) {
+ i->second->queueCreate(q);
+ checkReady(i, l);
+ }
+}
+
+void Primary::queueDestroy(const QueuePtr& q) {
+ Mutex::ScopedLock l(lock);
+ for (BackupMap::iterator i = backups.begin(); i != backups.end(); ++i)
+ i->second->queueDestroy(q);
+ checkReady(l);
+}
+
+void Primary::opened(broker::Connection& connection) {
+ Mutex::ScopedLock l(lock);
+ BrokerInfo info;
+ if (ha::ConnectionObserver::getBrokerInfo(connection, info)) {
+ BackupMap::iterator i = backups.find(info.getSystemId());
+ if (i == backups.end()) {
+ QPID_LOG(debug, logPrefix << "New backup connected: " << info);
+ bool guard = false; // Lazy-create guards for new backups. Creating them here could deadlock.
+ backups[info.getSystemId()].reset(
+ new RemoteBackup(info, haBroker.getBroker(), haBroker.getReplicationTest(), guard));
+ }
+ else {
+ QPID_LOG(debug, logPrefix << "Known backup connected: " << info);
+ }
+ haBroker.addBroker(info);
+ }
+}
+
+void Primary::closed(broker::Connection& connection) {
+ Mutex::ScopedLock l(lock);
+ BrokerInfo info;
+ if (ha::ConnectionObserver::getBrokerInfo(connection, info)) {
+ haBroker.removeBroker(info.getSystemId());
+ QPID_LOG(debug, logPrefix << "Backup disconnected: " << info);
+ }
+ // NOTE: we do not modify backups here, we only add to the known backups set
+ // we never remove from it.
+
+ // It is possible for a backup connection to be rejected while we are a backup,
+ // but the closed is seen when we have become primary. Removing the entry
+ // from backups in this case would be incorrect.
+}
+
+
+boost::shared_ptr<QueueGuard> Primary::getGuard(const QueuePtr& q, const BrokerInfo& info)
+{
+ BackupMap::iterator i = backups.find(info.getSystemId());
+ return i == backups.end() ? boost::shared_ptr<QueueGuard>() : i->second->guard(q);
+}
+
+}} // namespace qpid::ha
diff --git a/qpid/cpp/src/qpid/ha/Primary.h b/qpid/cpp/src/qpid/ha/Primary.h
new file mode 100644
index 0000000000..3de579a88d
--- /dev/null
+++ b/qpid/cpp/src/qpid/ha/Primary.h
@@ -0,0 +1,106 @@
+#ifndef QPID_HA_PRIMARY_H
+#define QPID_HA_PRIMARY_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "types.h"
+#include "BrokerInfo.h"
+#include "qpid/sys/Mutex.h"
+#include <boost/shared_ptr.hpp>
+#include <map>
+#include <string>
+
+namespace qpid {
+
+namespace broker {
+class Queue;
+class Connection;
+class ConnectionObserver;
+class ConfigurationObserver;
+}
+
+namespace ha {
+class HaBroker;
+class ReplicatingSubscription;
+class RemoteBackup;
+class QueueGuard;
+
+/**
+ * State associated with a primary broker:
+ * - sets queue guards and tracks readiness of initial backups till active.
+ * - sets queue guards on new queues for each backup.
+ *
+ * THREAD SAFE: called concurrently in arbitrary connection threads.
+ */
+class Primary
+{
+ public:
+ typedef boost::shared_ptr<broker::Queue> QueuePtr;
+
+ static Primary* get() { return instance; }
+
+ Primary(HaBroker& hb, const BrokerInfo::Set& expectedBackups);
+ ~Primary();
+
+ void readyReplica(const ReplicatingSubscription&);
+ void removeReplica(const std::string& q);
+
+ // Called via ConfigurationObserver
+ void queueCreate(const QueuePtr&);
+ void queueDestroy(const QueuePtr&);
+
+ // Called via ConnectionObserver
+ void opened(broker::Connection& connection);
+ void closed(broker::Connection& connection);
+
+ boost::shared_ptr<QueueGuard> getGuard(const QueuePtr& q, const BrokerInfo&);
+
+ private:
+ typedef std::map<types::Uuid, boost::shared_ptr<RemoteBackup> > BackupMap;
+ typedef std::set<boost::shared_ptr<RemoteBackup> > BackupSet;
+
+ void checkReady(sys::Mutex::ScopedLock&);
+ void checkReady(BackupMap::iterator, sys::Mutex::ScopedLock&);
+
+ sys::Mutex lock;
+ HaBroker& haBroker;
+ std::string logPrefix;
+ bool active;
+ /**
+ * Set of expected backups that must be ready before we declare ourselves
+ * active
+ */
+ BackupSet initialBackups;
+ /**
+ * Map of all the remote backups we know about: any expected backups plus
+ * all actual backups that have connected. We do not remove entries when a
+ * backup disconnects. @see Primary::closed()
+ */
+ BackupMap backups;
+ boost::shared_ptr<broker::ConnectionObserver> connectionObserver;
+ boost::shared_ptr<broker::ConfigurationObserver> configurationObserver;
+
+ static Primary* instance;
+};
+}} // namespace qpid::ha
+
+#endif /*!QPID_HA_PRIMARY_H*/
diff --git a/qpid/cpp/src/qpid/ha/QueueGuard.cpp b/qpid/cpp/src/qpid/ha/QueueGuard.cpp
new file mode 100644
index 0000000000..1f4ff4e48b
--- /dev/null
+++ b/qpid/cpp/src/qpid/ha/QueueGuard.cpp
@@ -0,0 +1,140 @@
+/*
+ *
+ * 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.
+ *
+ */
+#include "QueueGuard.h"
+#include "ReplicatingSubscription.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/QueuedMessage.h"
+#include "qpid/broker/QueueObserver.h"
+#include "qpid/log/Statement.h"
+#include <boost/bind.hpp>
+#include <sstream>
+
+namespace qpid {
+namespace ha {
+
+using namespace broker;
+using sys::Mutex;
+using framing::SequenceNumber;
+using framing::SequenceSet;
+
+class QueueGuard::QueueObserver : public broker::QueueObserver
+{
+ public:
+ QueueObserver(QueueGuard& g) : guard(g) {}
+ void enqueued(const broker::QueuedMessage& qm) { guard.enqueued(qm); }
+ void dequeued(const broker::QueuedMessage& qm) { guard.dequeued(qm); }
+ void acquired(const broker::QueuedMessage&) {}
+ void requeued(const broker::QueuedMessage&) {}
+ private:
+ QueueGuard& guard;
+};
+
+
+
+QueueGuard::QueueGuard(broker::Queue& q, const BrokerInfo& info)
+ : queue(q), subscription(0), range(q)
+{
+ // NOTE: The QueueGuard is created before the queue becomes active: either
+ // when a backup is promoted, or when a new queue is created on the primary.
+ std::ostringstream os;
+ os << "Primary guard " << queue.getName() << "@" << info.getLogId() << ": ";
+ logPrefix = os.str();
+ observer.reset(new QueueObserver(*this));
+ queue.addObserver(observer);
+}
+
+QueueGuard::~QueueGuard() { cancel(); }
+
+// NOTE: Called with message lock held.
+void QueueGuard::enqueued(const QueuedMessage& qm) {
+ assert(qm.queue == &queue);
+ // Delay completion
+ QPID_LOG(trace, logPrefix << "Delayed completion of " << qm);
+ qm.payload->getIngressCompletion().startCompleter();
+ {
+ Mutex::ScopedLock l(lock);
+ assert(!delayed.contains(qm.position));
+ delayed += qm.position;
+ }
+}
+
+// NOTE: Called with message lock held.
+void QueueGuard::dequeued(const QueuedMessage& qm) {
+ assert(qm.queue == &queue);
+ QPID_LOG(trace, logPrefix << "Dequeued " << qm);
+ ReplicatingSubscription* rs=0;
+ {
+ Mutex::ScopedLock l(lock);
+ rs = subscription;
+ }
+ if (rs) rs->dequeued(qm);
+ complete(qm);
+}
+
+void QueueGuard::cancel() {
+ queue.removeObserver(observer);
+ {
+ Mutex::ScopedLock l(lock);
+ if (delayed.empty()) return; // No need if no delayed messages.
+ }
+ // FIXME aconway 2012-06-15: optimize, only messages in delayed set.
+ queue.eachMessage(boost::bind(&QueueGuard::complete, this, _1));
+}
+
+void QueueGuard::attach(ReplicatingSubscription& rs) {
+ Mutex::ScopedLock l(lock);
+ subscription = &rs;
+}
+
+namespace {
+void completeBefore(QueueGuard* guard, SequenceNumber position, const QueuedMessage& qm) {
+ if (qm.position <= position) guard->complete(qm);
+}
+}
+
+bool QueueGuard::subscriptionStart(SequenceNumber position) {
+ // Complete any messages before or at the ReplicatingSubscription start position.
+ if (!delayed.empty() && delayed.front() <= position) {
+ // FIXME aconway 2012-06-15: queue iteration, only messages in delayed
+ queue.eachMessage(boost::bind(&completeBefore, this, position, _1));
+ }
+ return position >= range.back;
+}
+
+void QueueGuard::complete(const QueuedMessage& qm) {
+ assert(qm.queue == &queue);
+ {
+ Mutex::ScopedLock l(lock);
+ // The same message can be completed twice, by
+ // ReplicatingSubscription::acknowledged and dequeued. Remove it
+ // from the set so we only call finishCompleter() once
+ if (delayed.contains(qm.position))
+ delayed -= qm.position;
+ else
+ return;
+ }
+ QPID_LOG(trace, logPrefix << "Completed " << qm);
+ qm.payload->getIngressCompletion().finishCompleter();
+}
+
+// FIXME aconway 2012-06-04: TODO support for timeout.
+
+}} // namespaces qpid::ha
diff --git a/qpid/cpp/src/qpid/ha/QueueGuard.h b/qpid/cpp/src/qpid/ha/QueueGuard.h
new file mode 100644
index 0000000000..9c6fb55015
--- /dev/null
+++ b/qpid/cpp/src/qpid/ha/QueueGuard.h
@@ -0,0 +1,109 @@
+#ifndef QPID_HA_QUEUEGUARD_H
+#define QPID_HA_QUEUEGUARD_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "types.h"
+#include "QueueRange.h"
+#include "qpid/framing/SequenceNumber.h"
+#include "qpid/framing/SequenceSet.h"
+#include "qpid/types/Uuid.h"
+#include "qpid/sys/Mutex.h"
+#include <boost/shared_ptr.hpp>
+#include <deque>
+#include <set>
+
+namespace qpid {
+namespace broker {
+class Queue;
+struct QueuedMessage;
+class Message;
+}
+
+namespace ha {
+class BrokerInfo;
+class ReplicatingSubscription;
+
+/**
+ * A queue guard is a QueueObserver that delays completion of new messages
+ * arriving on a queue. It works as part of a ReplicatingSubscription to ensure
+ * messages are not acknowledged till they have been replicated.
+ *
+ * The guard can be created before the ReplicatingSubscription to protect
+ * messages arriving before the creation of the subscription.
+ *
+ * THREAD SAFE: Concurrent calls:
+ * - enqueued() via QueueObserver in arbitrary connection threads.
+ * - attach(), cancel(), complete() from ReplicatingSubscription in subscription thread.
+ */
+class QueueGuard {
+ public:
+ QueueGuard(broker::Queue& q, const BrokerInfo&);
+ ~QueueGuard();
+
+ /** QueueObserver override. Delay completion of the message.
+ * NOTE: Called under the queues message lock.
+ */
+ void enqueued(const broker::QueuedMessage&);
+
+ /** QueueObserver override: Complete a delayed message.
+ * NOTE: Called under the queues message lock.
+ */
+ void dequeued(const broker::QueuedMessage&);
+
+ /** Complete a delayed message. */
+ void complete(const broker::QueuedMessage&);
+
+ /** Complete all delayed messages. */
+ void cancel();
+
+ void attach(ReplicatingSubscription&);
+
+ /**
+ * Return the queue range at the time the QueueGuard was created. The
+ * QueueGuard is created before the queue becomes active: either when a
+ * backup is promoted, or when a new queue is created on the primary.
+ *
+ * NOTE: The first position protected by the guard is getRange().getBack()+1
+ */
+ const QueueRange& getRange() const { return range; } // range is immutable, no lock needed.
+
+ /** Inform the guard of the stating position for the attached subscription.
+ * Complete messages that will not be seen by the subscriptino.
+ *@return true if the subscription has already advanced to a guarded position.
+ */
+ bool subscriptionStart(framing::SequenceNumber position);
+
+ private:
+ class QueueObserver;
+
+ sys::Mutex lock;
+ std::string logPrefix;
+ broker::Queue& queue;
+ framing::SequenceSet delayed;
+ ReplicatingSubscription* subscription;
+ boost::shared_ptr<QueueObserver> observer;
+ const QueueRange range;
+};
+}} // namespace qpid::ha
+
+#endif /*!QPID_HA_QUEUEGUARD_H*/
diff --git a/qpid/cpp/src/qpid/ha/QueueRange.h b/qpid/cpp/src/qpid/ha/QueueRange.h
new file mode 100644
index 0000000000..3e83ff795e
--- /dev/null
+++ b/qpid/cpp/src/qpid/ha/QueueRange.h
@@ -0,0 +1,71 @@
+#ifndef QPID_HA_QUEUERANGE_H
+#define QPID_HA_QUEUERANGE_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "ReplicatingSubscription.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/framing/SequenceNumber.h"
+#include <iostream>
+
+namespace qpid {
+namespace ha {
+
+/**
+ * Get the front/back range of a queue or from a ReplicatingSubscription arguments table.
+ */
+struct QueueRange {
+ public:
+ framing::SequenceNumber front, back;
+
+ QueueRange() { }
+
+ QueueRange(broker::Queue& q) {
+ back = q.getPosition();
+ front = back+1; // assume empty
+ ReplicatingSubscription::getFront(q, front);
+ assert(front <= back + 1);
+ }
+
+ QueueRange(const framing::FieldTable& args) {
+ back = args.getAsInt(ReplicatingSubscription::QPID_BACK);
+ front = back+1;
+ if (args.isSet(ReplicatingSubscription::QPID_FRONT))
+ front = args.getAsInt(ReplicatingSubscription::QPID_FRONT);
+ if (back+1 < front)
+ throw Exception(QPID_MSG("Invalid range [" << front << "," << back <<"]"));
+ }
+
+ bool empty() const { return front == back+1; }
+};
+
+
+inline std::ostream& operator<<(std::ostream& o, const QueueRange& qr) {
+ if (qr.front > qr.back) return o << "[-," << qr.back << "]";
+ else return o << "[" << qr.front << "," << qr.back << "]";
+}
+
+
+}} // namespace qpid::ha
+
+#endif /*!QPID_HA_QUEUERANGE_H*/
diff --git a/qpid/cpp/src/qpid/ha/QueueReplicator.cpp b/qpid/cpp/src/qpid/ha/QueueReplicator.cpp
index 6aff4879e3..dbed7e1537 100644
--- a/qpid/cpp/src/qpid/ha/QueueReplicator.cpp
+++ b/qpid/cpp/src/qpid/ha/QueueReplicator.cpp
@@ -19,6 +19,7 @@
*
*/
+#include "Counter.h"
#include "QueueReplicator.h"
#include "ReplicatingSubscription.h"
#include "qpid/broker/Bridge.h"
@@ -30,6 +31,7 @@
#include "qpid/framing/SequenceSet.h"
#include "qpid/framing/FieldTable.h"
#include "qpid/log/Statement.h"
+#include "qpid/Msg.h"
#include <boost/shared_ptr.hpp>
namespace {
@@ -43,25 +45,38 @@ namespace ha {
using namespace broker;
using namespace framing;
-const std::string QueueReplicator::DEQUEUE_EVENT_KEY("qpid.dequeue-event");
-const std::string QueueReplicator::POSITION_EVENT_KEY("qpid.position-event");
+const std::string QPID_HA_EVENT_PREFIX("qpid.ha-event:");
+const std::string QueueReplicator::DEQUEUE_EVENT_KEY(QPID_HA_EVENT_PREFIX+"dequeue");
+const std::string QueueReplicator::POSITION_EVENT_KEY(QPID_HA_EVENT_PREFIX+"position");
std::string QueueReplicator::replicatorName(const std::string& queueName) {
return QPID_REPLICATOR_ + queueName;
}
-QueueReplicator::QueueReplicator(boost::shared_ptr<Queue> q, boost::shared_ptr<Link> l)
- : Exchange(replicatorName(q->getName()), 0, q->getBroker()), queue(q), link(l)
+bool QueueReplicator::isEventKey(const std::string key) {
+ const std::string& prefix = QPID_HA_EVENT_PREFIX;
+ bool ret = key.size() > prefix.size() && key.compare(0, prefix.size(), prefix) == 0;
+ return ret;
+}
+
+QueueReplicator::QueueReplicator(const BrokerInfo& info,
+ boost::shared_ptr<Queue> q,
+ boost::shared_ptr<Link> l)
+ : Exchange(replicatorName(q->getName()), 0, q->getBroker()),
+ logPrefix("Backup queue "+q->getName()+": "),
+ queue(q), link(l), brokerInfo(info)
{
- logPrefix = "HA: Backup " + queue->getName() + ": ";
- QPID_LOG(info, logPrefix << "Created, settings: " << q->getSettings());
+ Uuid uuid(true);
+ bridgeName = replicatorName(q->getName()) + std::string(".") + uuid.str();
}
// This must be separate from the constructor so we can call shared_from_this.
void QueueReplicator::activate() {
- // Note this may create a new bridge or use an existing one.
+ sys::Mutex::ScopedLock l(lock);
+ std::pair<Bridge::shared_ptr, bool> result =
queue->getBroker()->getLinks().declare(
- link->getHost(), link->getPort(),
+ bridgeName,
+ *link,
false, // durable
queue->getName(), // src
getName(), // dest
@@ -74,47 +89,49 @@ void QueueReplicator::activate() {
0, // sync?
// Include shared_ptr to self to ensure we are not deleted
// before initializeBridge is called.
- boost::bind(&QueueReplicator::initializeBridge, this, _1, _2, shared_from_this())
+ boost::bind(&QueueReplicator::initializeBridge, shared_from_this(), _1, _2)
);
+ bridge = result.first;
}
QueueReplicator::~QueueReplicator() {}
void QueueReplicator::deactivate() {
+ // destroy the route
sys::Mutex::ScopedLock l(lock);
- queue->getBroker()->getLinks().destroy(
- link->getHost(), link->getPort(), queue->getName(), getName(), string());
- QPID_LOG(debug, logPrefix << "Deactivated bridge " << bridgeName);
+ if (bridge) {
+ bridge->close();
+ bridge.reset();
+ QPID_LOG(debug, logPrefix << "Deactivated bridge " << bridgeName);
+ }
}
// Called in a broker connection thread when the bridge is created.
-// shared_ptr to self ensures we are not deleted before initializeBridge is called.
-void QueueReplicator::initializeBridge(Bridge& bridge, SessionHandler& sessionHandler,
- boost::shared_ptr<QueueReplicator> /*self*/) {
+void QueueReplicator::initializeBridge(Bridge& bridge, SessionHandler& sessionHandler) {
sys::Mutex::ScopedLock l(lock);
- bridgeName = bridge.getName();
- framing::AMQP_ServerProxy peer(sessionHandler.out);
+ AMQP_ServerProxy peer(sessionHandler.out);
const qmf::org::apache::qpid::broker::ArgsLinkBridge& args(bridge.getArgs());
- framing::FieldTable settings;
-
- // FIXME aconway 2011-12-09: Failover optimization removed.
- // There was code here to re-use messages already on the backup
- // during fail-over. This optimization was removed to simplify
- // the logic till we get the basic replication stable, it
- // can be re-introduced later. Last revision with the optimization:
- // r1213258 | QPID-3603: Fix QueueReplicator subscription parameters.
-
- // Clear out any old messages, reset the queue to start replicating fresh.
- queue->purge();
- queue->setPosition(0);
-
+ FieldTable settings;
settings.setInt(ReplicatingSubscription::QPID_REPLICATING_SUBSCRIPTION, 1);
- // TODO aconway 2011-12-19: optimize.
- settings.setInt(QPID_SYNC_FREQUENCY, 1);
- peer.getMessage().subscribe(args.i_src, args.i_dest, 0/*accept-explicit*/, 1/*not-acquired*/, false/*exclusive*/, "", 0, settings);
+ settings.setInt(QPID_SYNC_FREQUENCY, 1); // FIXME aconway 2012-05-22: optimize?
+ settings.setInt(ReplicatingSubscription::QPID_BACK,
+ queue->getPosition());
+ settings.setTable(ReplicatingSubscription::QPID_BROKER_INFO,
+ brokerInfo.asFieldTable());
+ SequenceNumber front;
+ if (ReplicatingSubscription::getFront(*queue, front))
+ settings.setInt(ReplicatingSubscription::QPID_FRONT, front);
+ peer.getMessage().subscribe(
+ args.i_src, args.i_dest, 0/*accept-explicit*/, 1/*not-acquired*/,
+ false/*exclusive*/, "", 0, settings);
+ // FIXME aconway 2012-05-22: use a finite credit window
peer.getMessage().flow(getName(), 0, 0xFFFFFFFF);
peer.getMessage().flow(getName(), 1, 0xFFFFFFFF);
- QPID_LOG(debug, logPrefix << "Activated bridge " << bridgeName);
+
+ qpid::Address primary;
+ link->getRemoteAddress(primary);
+ QPID_LOG(info, logPrefix << "Connected to " << primary << "(" << bridgeName << ")");
+ QPID_LOG(trace, logPrefix << "Subscription settings: " << settings);
}
namespace {
@@ -128,35 +145,46 @@ template <class T> T decodeContent(Message& m) {
}
}
-void QueueReplicator::dequeue(SequenceNumber n, const sys::Mutex::ScopedLock&) {
+void QueueReplicator::dequeue(SequenceNumber n, sys::Mutex::ScopedLock&) {
// Thread safe: only calls thread safe Queue functions.
- if (queue->getPosition() >= n) { // Ignore messages we haven't reached yet
- QueuedMessage message;
- if (queue->acquireMessageAt(n, message))
- queue->dequeue(0, message);
- }
+ QueuedMessage message;
+ if (queue->acquireMessageAt(n, message))
+ queue->dequeue(0, message);
}
// Called in connection thread of the queues bridge to primary.
void QueueReplicator::route(Deliverable& msg)
{
- const std::string& key = msg.getMessage().getRoutingKey();
- sys::Mutex::ScopedLock l(lock);
- if (key == DEQUEUE_EVENT_KEY) {
- SequenceSet dequeues = decodeContent<SequenceSet>(msg.getMessage());
- QPID_LOG(trace, logPrefix << "Dequeue: " << dequeues);
- //TODO: should be able to optimise the following
- for (SequenceSet::iterator i = dequeues.begin(); i != dequeues.end(); i++)
- dequeue(*i, l);
- } else if (key == POSITION_EVENT_KEY) {
- SequenceNumber position = decodeContent<SequenceNumber>(msg.getMessage());
- QPID_LOG(trace, logPrefix << "Position moved from " << queue->getPosition()
- << " to " << position);
- assert(queue->getPosition() <= position);
- queue->setPosition(position);
- } else {
- msg.deliverTo(queue);
- QPID_LOG(trace, logPrefix << "Enqueued message " << queue->getPosition());
+ try {
+ const std::string& key = msg.getMessage().getRoutingKey();
+ sys::Mutex::ScopedLock l(lock);
+ if (!isEventKey(key)) {
+ msg.deliverTo(queue);
+ // We are on a backup so the queue is not modified except via this.
+ QPID_LOG(trace, logPrefix << "Enqueued message " << queue->getPosition());
+ }
+ else if (key == DEQUEUE_EVENT_KEY) {
+ SequenceSet dequeues = decodeContent<SequenceSet>(msg.getMessage());
+ QPID_LOG(trace, logPrefix << "Dequeue: " << dequeues);
+ //TODO: should be able to optimise the following
+ for (SequenceSet::iterator i = dequeues.begin(); i != dequeues.end(); i++)
+ dequeue(*i, l);
+ }
+ else if (key == POSITION_EVENT_KEY) {
+ SequenceNumber position = decodeContent<SequenceNumber>(msg.getMessage());
+ QPID_LOG(trace, logPrefix << "Position moved from " << queue->getPosition()
+ << " to " << position);
+ // Verify that there are no messages after the new position in the queue.
+ SequenceNumber next;
+ if (ReplicatingSubscription::getNext(*queue, position, next))
+ throw Exception("Invalid position move, preceeds messages");
+ queue->setPosition(position);
+ }
+ // Ignore unknown event keys, may be introduced in later versions.
+ }
+ catch (const std::exception& e) {
+ QPID_LOG(critical, logPrefix << "Replication failed: " << e.what());
+ throw;
}
}
diff --git a/qpid/cpp/src/qpid/ha/QueueReplicator.h b/qpid/cpp/src/qpid/ha/QueueReplicator.h
index a1ebbd788a..2f55e6cc85 100644
--- a/qpid/cpp/src/qpid/ha/QueueReplicator.h
+++ b/qpid/cpp/src/qpid/ha/QueueReplicator.h
@@ -21,6 +21,8 @@
* under the License.
*
*/
+
+#include "BrokerInfo.h"
#include "qpid/broker/Exchange.h"
#include "qpid/framing/SequenceSet.h"
#include <boost/enable_shared_from_this.hpp>
@@ -39,6 +41,8 @@ class Deliverable;
namespace ha {
+class Counter;
+
/**
* Exchange created on a backup broker to replicate a queue on the primary.
*
@@ -55,8 +59,13 @@ class QueueReplicator : public broker::Exchange,
static const std::string DEQUEUE_EVENT_KEY;
static const std::string POSITION_EVENT_KEY;
static std::string replicatorName(const std::string& queueName);
+ /** Test if a string is an event key */
+ static bool isEventKey(const std::string key);
+
+ QueueReplicator(const BrokerInfo&,
+ boost::shared_ptr<broker::Queue> q,
+ boost::shared_ptr<broker::Link> l);
- QueueReplicator(boost::shared_ptr<broker::Queue> q, boost::shared_ptr<broker::Link> l);
~QueueReplicator();
void activate(); // Call after ctor
@@ -70,15 +79,16 @@ class QueueReplicator : public broker::Exchange,
bool isBound(boost::shared_ptr<broker::Queue>, const std::string* const, const framing::FieldTable* const);
private:
- void initializeBridge(broker::Bridge& bridge, broker::SessionHandler& sessionHandler,
- boost::shared_ptr<QueueReplicator> self);
- void dequeue(framing::SequenceNumber, const sys::Mutex::ScopedLock&);
+ void initializeBridge(broker::Bridge& bridge, broker::SessionHandler& sessionHandler);
+ void dequeue(framing::SequenceNumber, sys::Mutex::ScopedLock&);
std::string logPrefix;
std::string bridgeName;
sys::Mutex lock;
boost::shared_ptr<broker::Queue> queue;
boost::shared_ptr<broker::Link> link;
+ boost::shared_ptr<broker::Bridge> bridge;
+ BrokerInfo brokerInfo;
};
}} // namespace qpid::ha
diff --git a/qpid/cpp/src/qpid/ha/RemoteBackup.cpp b/qpid/cpp/src/qpid/ha/RemoteBackup.cpp
new file mode 100644
index 0000000000..7e65b287b0
--- /dev/null
+++ b/qpid/cpp/src/qpid/ha/RemoteBackup.cpp
@@ -0,0 +1,106 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "RemoteBackup.h"
+#include "QueueGuard.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/QueueRegistry.h"
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace ha {
+
+using sys::Mutex;
+
+RemoteBackup::RemoteBackup(
+ const BrokerInfo& info, broker::Broker& broker, ReplicationTest rt, bool cg) :
+ logPrefix("Primary remote backup "+info.getLogId()+": "), brokerInfo(info), replicationTest(rt),
+ createGuards(cg)
+{
+ QPID_LOG(debug, logPrefix << "Guarding queues for backup broker.");
+ broker.getQueues().eachQueue(boost::bind(&RemoteBackup::initialQueue, this, _1));
+}
+
+RemoteBackup::~RemoteBackup() {
+ for (GuardMap::iterator i = guards.begin(); i != guards.end(); ++i)
+ i->second->cancel();
+}
+
+bool RemoteBackup::isReady() {
+ return initialQueues.empty();
+}
+
+void RemoteBackup::initialQueue(const QueuePtr& q) {
+ if (replicationTest.isReplicated(ALL, *q)) {
+ initialQueues.insert(q);
+ queueCreate(q);
+ }
+}
+
+RemoteBackup::GuardPtr RemoteBackup::guard(const QueuePtr& q) {
+ if (!createGuards) return RemoteBackup::GuardPtr();
+ GuardMap::iterator i = guards.find(q);
+ if (i == guards.end()) {
+ assert(0);
+ throw Exception(logPrefix+": Cannot find queue guard: "+q->getName());
+ }
+ GuardPtr guard = i->second;
+ guards.erase(i);
+ return guard;
+}
+
+namespace {
+typedef std::set<boost::shared_ptr<broker::Queue> > QS;
+struct QueueSetPrinter {
+ const QS& qs;
+ QueueSetPrinter(const QS& q) : qs(q) {}
+};
+std::ostream& operator<<(std::ostream& o, const QueueSetPrinter& qp) {
+ for (QS::const_iterator i = qp.qs.begin(); i != qp.qs.end(); ++i)
+ o << (*i)->getName() << " ";
+ return o;
+}
+}
+
+void RemoteBackup::ready(const QueuePtr& q) {
+ initialQueues.erase(q);
+ QPID_LOG(debug, logPrefix << "Queue ready: " << q->getName()
+ << " remaining unready: " << QueueSetPrinter(initialQueues));
+ if (isReady()) QPID_LOG(debug, logPrefix << "All queues ready");
+}
+
+// Called via ConfigurationObserver and from initialQueue
+void RemoteBackup::queueCreate(const QueuePtr& q) {
+ if (createGuards && replicationTest.isReplicated(ALL, *q))
+ guards[q].reset(new QueueGuard(*q, brokerInfo));
+}
+
+// Called via ConfigurationObserver
+void RemoteBackup::queueDestroy(const QueuePtr& q) {
+ initialQueues.erase(q);
+ GuardMap::iterator i = guards.find(q);
+ if (i != guards.end()) {
+ i->second->cancel();
+ guards.erase(i);
+ }
+}
+
+}} // namespace qpid::ha
diff --git a/qpid/cpp/src/qpid/ha/RemoteBackup.h b/qpid/cpp/src/qpid/ha/RemoteBackup.h
new file mode 100644
index 0000000000..f2e46c8042
--- /dev/null
+++ b/qpid/cpp/src/qpid/ha/RemoteBackup.h
@@ -0,0 +1,92 @@
+#ifndef QPID_HA_REMOTEBACKUP_H
+#define QPID_HA_REMOTEBACKUP_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "ReplicationTest.h"
+#include "BrokerInfo.h"
+#include "types.h"
+#include <set>
+#include <map>
+
+namespace qpid {
+
+namespace broker {
+class Broker;
+class Queue;
+}
+
+namespace ha {
+class QueueGuard;
+
+/**
+ * Track readiness for a remote broker.
+ * Creates queue guards on behalf of the remote broker to keep
+ * queues safe till the ReplicatingSubscription is ready.
+ *
+ * THREAD UNSAFE: Caller must serialize.
+ */
+class RemoteBackup
+{
+ public:
+ typedef boost::shared_ptr<QueueGuard> GuardPtr;
+ typedef boost::shared_ptr<broker::Queue> QueuePtr;
+
+ /** Note: isReady() can be true after construction */
+ RemoteBackup(const BrokerInfo& info, broker::Broker&, ReplicationTest rt, bool createGuards);
+ ~RemoteBackup();
+
+ /** Return guard associated with a queue. Used to create ReplicatingSubscription. */
+ GuardPtr guard(const QueuePtr&);
+
+ /** ReplicatingSubscription associated with queue is ready.
+ * Note: may set isReady()
+ */
+ void ready(const QueuePtr& queue);
+
+ /** Called via ConfigurationObserver */
+ void queueCreate(const QueuePtr&);
+
+ /** Called via ConfigurationObserver. Note: may set isReady() */
+ void queueDestroy(const QueuePtr&);
+
+ /**@return true when all initial queues for this backup are ready. */
+ bool isReady();
+
+ BrokerInfo getBrokerInfo() const { return brokerInfo; }
+ private:
+ typedef std::map<QueuePtr, GuardPtr> GuardMap;
+ typedef std::set<QueuePtr> QueueSet;
+
+ std::string logPrefix;
+ BrokerInfo brokerInfo;
+ ReplicationTest replicationTest;
+ GuardMap guards;
+ QueueSet initialQueues;
+ bool createGuards;
+
+ void initialQueue(const QueuePtr&);
+};
+
+}} // namespace qpid::ha
+
+#endif /*!QPID_HA_REMOTEBACKUP_H*/
diff --git a/qpid/cpp/src/qpid/ha/ReplicatingSubscription.cpp b/qpid/cpp/src/qpid/ha/ReplicatingSubscription.cpp
index af6180305d..629014b215 100644
--- a/qpid/cpp/src/qpid/ha/ReplicatingSubscription.cpp
+++ b/qpid/cpp/src/qpid/ha/ReplicatingSubscription.cpp
@@ -19,13 +19,18 @@
*
*/
+#include "QueueGuard.h"
+#include "QueueRange.h"
+#include "QueueReplicator.h"
#include "ReplicatingSubscription.h"
+#include "Primary.h"
#include "qpid/broker/Queue.h"
#include "qpid/broker/SessionContext.h"
#include "qpid/broker/ConnectionState.h"
#include "qpid/framing/AMQFrame.h"
#include "qpid/framing/MessageTransferBody.h"
#include "qpid/log/Statement.h"
+#include "qpid/types/Uuid.h"
#include <sstream>
namespace qpid {
@@ -34,19 +39,90 @@ namespace ha {
using namespace framing;
using namespace broker;
using namespace std;
+using sys::Mutex;
-const string ReplicatingSubscription::QPID_REPLICATING_SUBSCRIPTION("qpid.replicating-subscription");
+const string ReplicatingSubscription::QPID_REPLICATING_SUBSCRIPTION("qpid.ha-replicating-subscription");
+const string ReplicatingSubscription::QPID_BACK("qpid.ha-back");
+const string ReplicatingSubscription::QPID_FRONT("qpid.ha-front");
+const string ReplicatingSubscription::QPID_BROKER_INFO("qpid.ha-broker-info");
namespace {
const string DOLLAR("$");
const string INTERNAL("-internal");
} // namespace
+// Scan the queue for gaps and add them to the subscriptions dequed set.
+class DequeueScanner
+{
+ public:
+ DequeueScanner(
+ ReplicatingSubscription& rs,
+ SequenceNumber front_,
+ SequenceNumber back_ // Inclusive
+ ) : subscription(rs), front(front_), back(back_)
+ {
+ assert(front <= back);
+ // INVARIANT deques have been added for positions <= at.
+ at = front - 1;
+ }
+
+ void operator()(const QueuedMessage& qm) {
+ if (qm.position >= front && qm.position <= back) {
+ if (qm.position > at+1) subscription.dequeued(at+1, qm.position-1);
+ at = qm.position;
+ }
+ }
+
+ // Must call after scanning the queue.
+ void finish() {
+ if (at < back) subscription.dequeued(at+1, back);
+ }
+
+ private:
+ ReplicatingSubscription& subscription;
+ SequenceNumber front;
+ SequenceNumber back;
+ SequenceNumber at;
+};
+
string mask(const string& in)
{
return DOLLAR + in + INTERNAL;
}
+
+/** Dummy consumer used to get the front position on the queue */
+class GetPositionConsumer : public Consumer
+{
+ public:
+ GetPositionConsumer() :
+ Consumer("ha.GetPositionConsumer."+types::Uuid(true).str(), false) {}
+ bool deliver(broker::QueuedMessage& ) { return true; }
+ void notify() {}
+ bool filter(boost::intrusive_ptr<broker::Message>) { return true; }
+ bool accept(boost::intrusive_ptr<broker::Message>) { return true; }
+ void cancel() {}
+ void acknowledged(const broker::QueuedMessage&) {}
+ bool browseAcquired() const { return true; }
+ broker::OwnershipToken* getSession() { return 0; }
+};
+
+
+bool ReplicatingSubscription::getNext(
+ broker::Queue& q, SequenceNumber from, SequenceNumber& result)
+{
+ boost::shared_ptr<Consumer> c(new GetPositionConsumer);
+ c->setPosition(from);
+ if (!q.dispatch(c)) return false;
+ result = c->getPosition();
+ return true;
+}
+
+bool ReplicatingSubscription::getFront(broker::Queue& q, SequenceNumber& front) {
+ // FIXME aconway 2012-05-23: won't wrap, assumes 0 is < all messages in queue.
+ return getNext(q, 0, front);
+}
+
/* Called by SemanticState::consume to create a consumer */
boost::shared_ptr<broker::SemanticState::ConsumerImpl>
ReplicatingSubscription::Factory::create(
@@ -66,7 +142,7 @@ ReplicatingSubscription::Factory::create(
rs.reset(new ReplicatingSubscription(
parent, name, queue, ack, acquire, exclusive, tag,
resumeId, resumeTtl, arguments));
- queue->addObserver(rs);
+ rs->initialize();
}
return rs;
}
@@ -84,162 +160,211 @@ ReplicatingSubscription::ReplicatingSubscription(
const framing::FieldTable& arguments
) : ConsumerImpl(parent, name, queue, ack, acquire, exclusive, tag,
resumeId, resumeTtl, arguments),
- events(new Queue(mask(name))),
- consumer(new DelegatingConsumer(*this))
+ dummy(new Queue(mask(name))),
+ ready(false)
{
- stringstream ss;
- ss << "HA: Primary: " << getQueue()->getName() << " at "
- << parent->getSession().getConnection().getUrl() << ": ";
- logPrefix = ss.str();
-
- // FIXME aconway 2011-12-09: Failover optimization removed.
- // There was code here to re-use messages already on the backup
- // during fail-over. This optimization was removed to simplify
- // the logic till we get the basic replication stable, it
- // can be re-introduced later. Last revision with the optimization:
- // r1213258 | QPID-3603: Fix QueueReplicator subscription parameters.
-
- QPID_LOG(debug, logPrefix << "Created backup subscription " << getName());
-
- // FIXME aconway 2011-12-15: ConsumerImpl::position is left at 0
- // so we will start consuming from the lowest numbered message.
- // This is incorrect if the sequence number wraps around, but
- // this is what all consumers currently do.
-}
+ try {
+ FieldTable ft;
+ if (!arguments.getTable(ReplicatingSubscription::QPID_BROKER_INFO, ft))
+ throw Exception("Replicating subscription does not have broker info: " + tag);
+ info.assign(ft);
-// Message is delivered in the subscription's connection thread.
-bool ReplicatingSubscription::deliver(QueuedMessage& m) {
- // Add position events for the subscribed queue, not for the internal event queue.
- if (m.queue && m.queue == getQueue().get()) {
- sys::Mutex::ScopedLock l(lock);
- assert(position == m.position);
- // m.position is the position of the newly enqueued m on the local queue.
- // backupPosition is latest position on the backup queue (before enqueueing m.)
- assert(m.position > backupPosition);
- if (m.position - backupPosition > 1) {
- // Position has advanced because of messages dequeued ahead of us.
- SequenceNumber send(m.position);
- --send; // Send the position before m was enqueued.
- sendPositionEvent(send, l);
- }
- backupPosition = m.position;
- QPID_LOG(trace, logPrefix << "Replicating message " << m.position);
- }
- return ConsumerImpl::deliver(m);
-}
+ // Set a log prefix message that identifies the remote broker.
+ ostringstream os;
+ os << "Primary " << queue->getName() << "@" << info.getLogId() << ": ";
+ logPrefix = os.str();
-ReplicatingSubscription::~ReplicatingSubscription() {}
+ // NOTE: Once the guard is attached we can have concurrent
+ // calls to dequeued so we need to lock use of this->deques.
+ //
+ // However we must attach the guard _before_ we scan for
+ // initial dequeues to be sure we don't miss any dequeues
+ // between the scan and attaching the guard.
+ if (Primary::get()) guard = Primary::get()->getGuard(queue, info);
+ if (!guard) guard.reset(new QueueGuard(*queue, getBrokerInfo()));
+ guard->attach(*this);
+ QueueRange backup(arguments); // The remote backup state.
+ QueueRange primary(guard->getRange()); // The local state at the time the guard was set.
+ backupPosition = backup.back;
-// INVARIANT: delayed contains msg <=> we have outstanding startCompletion on msg
+ // Sync backup and primary queues, don't send messages already on the backup
-// Mark a message completed. May be called by acknowledge or dequeued
-void ReplicatingSubscription::complete(
- const QueuedMessage& qm, const sys::Mutex::ScopedLock&)
-{
- // Handle completions for the subscribed queue, not the internal event queue.
- if (qm.queue && qm.queue == getQueue().get()) {
- QPID_LOG(trace, logPrefix << "Completed message " << qm.position);
- Delayed::iterator i= delayed.find(qm.position);
- // The same message can be completed twice, by acknowledged and
- // dequeued, remove it from the set so it only gets completed
- // once.
- if (i != delayed.end()) {
- assert(i->second.payload == qm.payload);
- qm.payload->getIngressCompletion().finishCompleter();
- delayed.erase(i);
+ if (backup.back < primary.front || backup.front > primary.back
+ || primary.empty() || backup.empty())
+ {
+ // No overlap - erase backup and start from the beginning
+ if (!backup.empty()) dequeued(backup.front, backup.back);
+ position = primary.front-1;
+ }
+ else { // backup and primary do overlap.
+ // Remove messages from backup that are not in primary.
+ if (primary.back < backup.back) {
+ dequeued(primary.back+1, backup.back); // Trim excess messages at back
+ backup.back = primary.back;
+ }
+ if (backup.front < primary.front) {
+ dequeued(backup.front, primary.front-1); // Trim excess messages at front
+ backup.front = primary.front;
+ }
+ DequeueScanner scan(*this, backup.front, backup.back);
+ // FIXME aconway 2012-06-15: Optimize queue traversal, only in range.
+ queue->eachMessage(boost::ref(scan)); // Remove missing messages in between.
+ scan.finish();
+ position = backup.back;
}
+
+ QPID_LOG(debug, logPrefix << "Subscribed:"
+ << " backup:" << backup
+ << " primary:" << primary
+ << " catch-up: " << position << "-" << primary.back
+ << "(" << primary.back-position << ")");
+
+ // Check if we are ready yet.
+ if (guard->subscriptionStart(position)) setReady();
+ }
+ catch (const std::exception& e) {
+ throw InvalidArgumentException(QPID_MSG(logPrefix << e.what()
+ << ": arguments=" << arguments));
}
}
-// Called before we get notified of the message being available and
-// under the message lock in the queue. Called in arbitrary connection thread.
-void ReplicatingSubscription::enqueued(const QueuedMessage& qm) {
- sys::Mutex::ScopedLock l(lock);
- // Delay completion
- QPID_LOG(trace, logPrefix << "Delaying completion of message " << qm.position);
- qm.payload->getIngressCompletion().startCompleter();
- assert(delayed.find(qm.position) == delayed.end());
- delayed[qm.position] = qm;
+ReplicatingSubscription::~ReplicatingSubscription() {
+ QPID_LOG(debug, logPrefix << "Detroyed replicating subscription");
}
+// Called in subscription's connection thread when the subscription is created.
+// Called separate from ctor because sending events requires
+// shared_from_this
+//
+void ReplicatingSubscription::initialize() {
+ Mutex::ScopedLock l(lock); // Note dequeued() can be called concurrently.
-// Function to complete a delayed message, called by cancel()
-void ReplicatingSubscription::cancelComplete(
- const Delayed::value_type& v, const sys::Mutex::ScopedLock&)
-{
- QPID_LOG(trace, logPrefix << "Cancel completed message " << v.second.position);
- v.second.payload->getIngressCompletion().finishCompleter();
+ // Send initial dequeues and position to the backup.
+ // There must be a shared_ptr(this) when sending.
+ sendDequeueEvent(l);
+ sendPositionEvent(position, l);
+ backupPosition = position;
+}
+
+// Message is delivered in the subscription's connection thread.
+bool ReplicatingSubscription::deliver(QueuedMessage& qm) {
+ try {
+ // Add position events for the subscribed queue, not the internal event queue.
+ if (qm.queue == getQueue().get()) {
+ QPID_LOG(trace, logPrefix << "Replicating " << qm);
+ {
+ Mutex::ScopedLock l(lock);
+ assert(position == qm.position);
+ // qm.position is the position of the newly enqueued qm on local queue.
+ // backupPosition is latest position on backup queue before enqueueing
+ if (qm.position <= backupPosition)
+ throw Exception(
+ QPID_MSG("Expected position > " << backupPosition
+ << " but got " << qm.position));
+ if (qm.position - backupPosition > 1) {
+ // Position has advanced because of messages dequeued ahead of us.
+ // Send the position before qm was enqueued.
+ sendPositionEvent(qm.position-1, l);
+ }
+ // Backup will automatically advance by 1 on delivery of message.
+ backupPosition = qm.position;
+ }
+ }
+ return ConsumerImpl::deliver(qm);
+ } catch (const std::exception& e) {
+ QPID_LOG(critical, logPrefix << "Error replicating " << qm
+ << ": " << e.what());
+ throw;
+ }
+}
+
+void ReplicatingSubscription::setReady() {
+ {
+ Mutex::ScopedLock l(lock);
+ if (ready) return;
+ ready = true;
+ }
+ // Notify Primary that a subscription is ready.
+ QPID_LOG(debug, logPrefix << "Caught up");
+ if (Primary::get()) Primary::get()->readyReplica(*this);
}
// Called in the subscription's connection thread.
void ReplicatingSubscription::cancel()
{
- getQueue()->removeObserver(
- boost::dynamic_pointer_cast<QueueObserver>(shared_from_this()));
- {
- sys::Mutex::ScopedLock l(lock);
- QPID_LOG(debug, logPrefix <<"Cancelled backup subscription " << getName());
- for_each(delayed.begin(), delayed.end(),
- boost::bind(&ReplicatingSubscription::cancelComplete, this, _1, boost::ref(l)));
- delayed.clear();
- }
+ guard->cancel();
ConsumerImpl::cancel();
}
-// Called on primary in the backups IO thread.
-void ReplicatingSubscription::acknowledged(const QueuedMessage& msg) {
- sys::Mutex::ScopedLock l(lock);
- // Finish completion of message, it has been acknowledged by the backup.
- complete(msg, l);
+// Consumer override, called on primary in the backup's IO thread.
+void ReplicatingSubscription::acknowledged(const QueuedMessage& qm) {
+ if (qm.queue == getQueue().get()) { // Don't complete messages on the internal queue
+ // Finish completion of message, it has been acknowledged by the backup.
+ QPID_LOG(trace, logPrefix << "Acknowledged " << qm);
+ guard->complete(qm);
+ // If next message is protected by the guard then we are ready
+ if (qm.position >= guard->getRange().back) setReady();
+ }
+ ConsumerImpl::acknowledged(qm);
}
-// Hide the "queue deleted" error for a ReplicatingSubscription when a
-// queue is deleted, this is normal and not an error.
-bool ReplicatingSubscription::hideDeletedError() { return true; }
-
// Called with lock held. Called in subscription's connection thread.
-void ReplicatingSubscription::sendDequeueEvent(const sys::Mutex::ScopedLock& l)
+void ReplicatingSubscription::sendDequeueEvent(Mutex::ScopedLock&)
{
+ if (dequeues.empty()) return;
QPID_LOG(trace, logPrefix << "Sending dequeues " << dequeues);
string buf(dequeues.encodedSize(),'\0');
framing::Buffer buffer(&buf[0], buf.size());
dequeues.encode(buffer);
dequeues.clear();
buffer.reset();
- sendEvent(QueueReplicator::DEQUEUE_EVENT_KEY, buffer, l);
+ {
+ Mutex::ScopedUnlock u(lock);
+ sendEvent(QueueReplicator::DEQUEUE_EVENT_KEY, buffer);
+ }
}
-// Called after the message has been removed from the deque and under
-// the messageLock in the queue. Called in arbitrary connection threads.
+// Called via QueueObserver::dequeued override on guard.
+// Called after the message has been removed
+// from the deque and under the messageLock in the queue. Called in
+// arbitrary connection threads.
void ReplicatingSubscription::dequeued(const QueuedMessage& qm)
{
+ assert (qm.queue == getQueue().get());
+ QPID_LOG(trace, logPrefix << "Dequeued " << qm);
{
- sys::Mutex::ScopedLock l(lock);
- QPID_LOG(trace, logPrefix << "Dequeued message " << qm.position);
+ Mutex::ScopedLock l(lock);
dequeues.add(qm.position);
- // If we have not yet sent this message to the backup, then
- // complete it now as it will never be accepted.
- if (qm.position > position) complete(qm, l);
}
notify(); // Ensure a call to doDispatch
}
+// Called during construction while scanning for initial dequeues.
+void ReplicatingSubscription::dequeued(SequenceNumber first, SequenceNumber last) {
+ QPID_LOG(trace, logPrefix << "Initial dequeue [" << first << ", " << last << "]");
+ {
+ Mutex::ScopedLock l(lock);
+ dequeues.add(first,last);
+ }
+}
+
// Called with lock held. Called in subscription's connection thread.
-void ReplicatingSubscription::sendPositionEvent(
- SequenceNumber position, const sys::Mutex::ScopedLock&l )
+void ReplicatingSubscription::sendPositionEvent(SequenceNumber pos, Mutex::ScopedLock&)
{
- QPID_LOG(trace, logPrefix << "Sending position " << position
- << ", was " << backupPosition);
- string buf(backupPosition.encodedSize(),'\0');
+ if (pos == backupPosition) return; // No need to send.
+ QPID_LOG(trace, logPrefix << "Sending position " << pos << ", was " << backupPosition);
+ string buf(pos.encodedSize(),'\0');
framing::Buffer buffer(&buf[0], buf.size());
- position.encode(buffer);
+ pos.encode(buffer);
buffer.reset();
- sendEvent(QueueReplicator::POSITION_EVENT_KEY, buffer, l);
+ {
+ Mutex::ScopedUnlock u(lock);
+ sendEvent(QueueReplicator::POSITION_EVENT_KEY, buffer);
+ }
}
-void ReplicatingSubscription::sendEvent(const std::string& key, framing::Buffer& buffer,
- const sys::Mutex::ScopedLock&)
+void ReplicatingSubscription::sendEvent(const std::string& key, framing::Buffer& buffer)
{
//generate event message
boost::intrusive_ptr<Message> event = new Message();
@@ -259,15 +384,14 @@ void ReplicatingSubscription::sendEvent(const std::string& key, framing::Buffer&
event->getFrames().append(header);
event->getFrames().append(content);
- DeliveryProperties* props = event->getFrames().getHeaders()->get<DeliveryProperties>(true);
+ DeliveryProperties* props =
+ event->getFrames().getHeaders()->get<DeliveryProperties>(true);
props->setRoutingKey(key);
- // Send the event using the events queue. Consumer is a
- // DelegatingConsumer that delegates to *this for everything but
- // has an independnet position. We put an event on events and
- // dispatch it through ourselves to send it in line with the
- // normal browsing messages.
- events->deliver(event);
- events->dispatch(consumer);
+ // Send the event directly to the base consumer implementation.
+ // We don't really need a queue here but we pass a dummy queue
+ // to conform to the consumer API.
+ QueuedMessage qm(dummy.get(), event);
+ ConsumerImpl::deliver(qm);
}
@@ -275,19 +399,10 @@ void ReplicatingSubscription::sendEvent(const std::string& key, framing::Buffer&
bool ReplicatingSubscription::doDispatch()
{
{
- sys::Mutex::ScopedLock l(lock);
+ Mutex::ScopedLock l(lock);
if (!dequeues.empty()) sendDequeueEvent(l);
}
return ConsumerImpl::doDispatch();
}
-ReplicatingSubscription::DelegatingConsumer::DelegatingConsumer(ReplicatingSubscription& c) : Consumer(c.getName(), true), delegate(c) {}
-ReplicatingSubscription::DelegatingConsumer::~DelegatingConsumer() {}
-bool ReplicatingSubscription::DelegatingConsumer::deliver(QueuedMessage& m) { return delegate.deliver(m); }
-void ReplicatingSubscription::DelegatingConsumer::notify() { delegate.notify(); }
-bool ReplicatingSubscription::DelegatingConsumer::filter(boost::intrusive_ptr<Message> msg) { return delegate.filter(msg); }
-bool ReplicatingSubscription::DelegatingConsumer::accept(boost::intrusive_ptr<Message> msg) { return delegate.accept(msg); }
-bool ReplicatingSubscription::DelegatingConsumer::browseAcquired() const { return delegate.browseAcquired(); }
-OwnershipToken* ReplicatingSubscription::DelegatingConsumer::getSession() { return delegate.getSession(); }
-
}} // namespace qpid::ha
diff --git a/qpid/cpp/src/qpid/ha/ReplicatingSubscription.h b/qpid/cpp/src/qpid/ha/ReplicatingSubscription.h
index cbcb230bc1..afa503d8cc 100644
--- a/qpid/cpp/src/qpid/ha/ReplicatingSubscription.h
+++ b/qpid/cpp/src/qpid/ha/ReplicatingSubscription.h
@@ -22,10 +22,11 @@
*
*/
-#include "QueueReplicator.h" // For DEQUEUE_EVENT_KEY
+#include "BrokerInfo.h"
#include "qpid/broker/SemanticState.h"
#include "qpid/broker/QueueObserver.h"
#include "qpid/broker/ConsumerFactory.h"
+#include "qpid/types/Uuid.h"
#include <iosfwd>
namespace qpid {
@@ -42,18 +43,27 @@ class Buffer;
}
namespace ha {
+class QueueGuard;
/**
- * A susbcription that represents a backup replicating a queue.
+ * A susbcription that replicates to a remote backup.
*
- * Runs on the primary. Delays completion of messages till the backup
- * has acknowledged, informs backup of locally dequeued messages.
+ * Runs on the primary. In conjunction with a QueueGuard, delays completion of
+ * messages till the backup has acknowledged, informs backup of locally dequeued
+ * messages.
+ *
+ * A ReplicatingSubscription is "ready" when all the messages on the queue have
+ * either been acknowledged by the backup, or are protected by the queue guard.
+ * On a primary broker the ReplicatingSubscription calls Primary::readyReplica
+ * when it is ready.
+ *
+ * THREAD SAFE: Called in subscription's connection thread but also in arbitrary
+ * connection threads via dequeued.
+ *
+ * Lifecycle: broker::Queue holds shared_ptrs to this as a consumer.
*
- * THREAD SAFE: Used as a consumer in subscription's connection
- * thread, and as a QueueObserver in arbitrary connection threads.
*/
-class ReplicatingSubscription : public broker::SemanticState::ConsumerImpl,
- public broker::QueueObserver
+class ReplicatingSubscription : public broker::SemanticState::ConsumerImpl
{
public:
struct Factory : public broker::ConsumerFactory {
@@ -67,6 +77,20 @@ class ReplicatingSubscription : public broker::SemanticState::ConsumerImpl,
// Argument names for consume command.
static const std::string QPID_REPLICATING_SUBSCRIPTION;
+ static const std::string QPID_BACK;
+ static const std::string QPID_FRONT;
+ static const std::string QPID_BROKER_INFO;
+
+ // TODO aconway 2012-05-23: these don't belong on ReplicatingSubscription
+ /** Get position of front message on queue.
+ *@return false if queue is empty.
+ */
+ static bool getFront(broker::Queue&, framing::SequenceNumber& result);
+ /** Get next message after from in queue.
+ *@return false if none found.
+ */
+ static bool getNext(broker::Queue&, framing::SequenceNumber from,
+ framing::SequenceNumber& result);
ReplicatingSubscription(broker::SemanticState* parent,
const std::string& name, boost::shared_ptr<broker::Queue> ,
@@ -76,56 +100,46 @@ class ReplicatingSubscription : public broker::SemanticState::ConsumerImpl,
~ReplicatingSubscription();
- // QueueObserver overrides.
- bool deliver(broker::QueuedMessage& msg);
- void enqueued(const broker::QueuedMessage&);
- void dequeued(const broker::QueuedMessage&);
- void acquired(const broker::QueuedMessage&) {}
- void requeued(const broker::QueuedMessage&) {}
+ // Called via QueueGuard::dequeued.
+ //@return true if the message requires completion.
+ void dequeued(const broker::QueuedMessage& qm);
+
+ // Called during initial scan for dequeues.
+ void dequeued(framing::SequenceNumber first, framing::SequenceNumber last);
// Consumer overrides.
+ bool deliver(broker::QueuedMessage& msg);
void cancel();
void acknowledged(const broker::QueuedMessage&);
bool browseAcquired() const { return true; }
+ // Hide the "queue deleted" error for a ReplicatingSubscription when a
+ // queue is deleted, this is normal and not an error.
+ bool hideDeletedError() { return true; }
+
+ /** Initialization that must be done separately from construction
+ * because it requires a shared_ptr to this to exist.
+ */
+ void initialize();
- bool hideDeletedError();
+ BrokerInfo getBrokerInfo() const { return info; }
protected:
bool doDispatch();
+
private:
- typedef std::map<framing::SequenceNumber, broker::QueuedMessage> Delayed;
std::string logPrefix;
- boost::shared_ptr<broker::Queue> events;
- boost::shared_ptr<broker::Consumer> consumer;
- Delayed delayed;
+ boost::shared_ptr<broker::Queue> dummy; // Used to send event messages
framing::SequenceSet dequeues;
framing::SequenceNumber backupPosition;
-
- void complete(const broker::QueuedMessage&, const sys::Mutex::ScopedLock&);
- void cancelComplete(const Delayed::value_type& v, const sys::Mutex::ScopedLock&);
- void sendDequeueEvent(const sys::Mutex::ScopedLock&);
- void sendPositionEvent(framing::SequenceNumber, const sys::Mutex::ScopedLock&);
- void sendEvent(const std::string& key, framing::Buffer&,
- const sys::Mutex::ScopedLock&);
-
- class DelegatingConsumer : public Consumer
- {
- public:
- DelegatingConsumer(ReplicatingSubscription&);
- ~DelegatingConsumer();
- bool deliver(broker::QueuedMessage& msg);
- void notify();
- bool filter(boost::intrusive_ptr<broker::Message>);
- bool accept(boost::intrusive_ptr<broker::Message>);
- void cancel() {}
- void acknowledged(const broker::QueuedMessage&) {}
- bool browseAcquired() const;
-
- broker::OwnershipToken* getSession();
-
- private:
- ReplicatingSubscription& delegate;
- };
+ bool ready;
+ BrokerInfo info;
+ boost::shared_ptr<QueueGuard> guard;
+
+ void sendDequeueEvent(sys::Mutex::ScopedLock&);
+ void sendPositionEvent(framing::SequenceNumber, sys::Mutex::ScopedLock&);
+ void setReady();
+ void sendEvent(const std::string& key, framing::Buffer&);
+ friend struct Factory;
};
diff --git a/qpid/cpp/src/qpid/ha/ReplicationTest.cpp b/qpid/cpp/src/qpid/ha/ReplicationTest.cpp
new file mode 100644
index 0000000000..18e0953930
--- /dev/null
+++ b/qpid/cpp/src/qpid/ha/ReplicationTest.cpp
@@ -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.
+ *
+ */
+#include "ReplicationTest.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/framing/FieldTable.h"
+
+namespace qpid {
+namespace ha {
+
+using types::Variant;
+
+ReplicateLevel ReplicationTest::replicateLevel(const std::string& str) {
+ Enum<ReplicateLevel> rl(replicateDefault);
+ if (!str.empty()) rl.parse(str);
+ return rl.get();
+}
+
+ReplicateLevel ReplicationTest::replicateLevel(const framing::FieldTable& f) {
+ if (f.isSet(QPID_REPLICATE))
+ return replicateLevel(f.getAsString(QPID_REPLICATE));
+ else
+ return replicateDefault;
+}
+
+ReplicateLevel ReplicationTest::replicateLevel(const Variant::Map& m) {
+ Variant::Map::const_iterator i = m.find(QPID_REPLICATE);
+ if (i != m.end())
+ return replicateLevel(i->second.asString());
+ else
+ return replicateDefault;
+}
+
+namespace {
+const std::string AUTO_DELETE_TIMEOUT("qpid.auto_delete_timeout");
+}
+
+bool ReplicationTest::isReplicated(
+ ReplicateLevel level, const Variant::Map& args, bool autodelete, bool exclusive)
+{
+ bool ignore = autodelete && exclusive && args.find(AUTO_DELETE_TIMEOUT) == args.end();
+ return !ignore && replicateLevel(args) >= level;
+}
+
+bool ReplicationTest::isReplicated(
+ ReplicateLevel level, const framing::FieldTable& args, bool autodelete, bool exclusive)
+{
+ bool ignore = autodelete && exclusive && !args.isSet(AUTO_DELETE_TIMEOUT);
+ return !ignore && replicateLevel(args) >= level;
+}
+
+bool ReplicationTest::isReplicated(ReplicateLevel level, const broker::Queue& q)
+{
+ return isReplicated(level, q.getSettings(), q.isAutoDelete(), q.hasExclusiveOwner());
+}
+
+
+}} // namespace qpid::ha
diff --git a/qpid/cpp/src/qpid/ha/ReplicationTest.h b/qpid/cpp/src/qpid/ha/ReplicationTest.h
new file mode 100644
index 0000000000..9f6976a8e4
--- /dev/null
+++ b/qpid/cpp/src/qpid/ha/ReplicationTest.h
@@ -0,0 +1,67 @@
+#ifndef QPID_HA_REPLICATIONTEST_H
+#define QPID_HA_REPLICATIONTEST_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "types.h"
+#include "qpid/types/Variant.h"
+#include <string>
+
+namespace qpid {
+
+namespace broker {
+class Queue;
+}
+
+namespace framing {
+class FieldTable;
+}
+
+namespace ha {
+/**
+ * Test whether something is replicated, taking into account the
+ * default replication level.
+ */
+class ReplicationTest
+{
+ public:
+ ReplicationTest(ReplicateLevel replicateDefault_) :
+ replicateDefault(replicateDefault_) {}
+
+ // Return the simple replication level, accounting for defaults.
+ ReplicateLevel replicateLevel(const std::string& str);
+ ReplicateLevel replicateLevel(const framing::FieldTable& f);
+ ReplicateLevel replicateLevel(const types::Variant::Map& m);
+
+ // Return true if replication for a queue is enabled at level or higher,
+ // taking account of default level and queue settings.
+ bool isReplicated(ReplicateLevel level,
+ const types::Variant::Map& args, bool autodelete, bool exclusive);
+ bool isReplicated(ReplicateLevel level,
+ const framing::FieldTable& args, bool autodelete, bool exclusive);
+ bool isReplicated(ReplicateLevel level, const broker::Queue&);
+ private:
+ ReplicateLevel replicateDefault;
+};
+}} // namespace qpid::ha
+
+#endif /*!QPID_HA_REPLICATIONTEST_H*/
diff --git a/qpid/cpp/src/qpid/ha/Settings.h b/qpid/cpp/src/qpid/ha/Settings.h
index 7df18b4ef4..213a5f64d5 100644
--- a/qpid/cpp/src/qpid/ha/Settings.h
+++ b/qpid/cpp/src/qpid/ha/Settings.h
@@ -22,6 +22,7 @@
*
*/
+#include "types.h"
#include <string>
namespace qpid {
@@ -33,11 +34,13 @@ namespace ha {
class Settings
{
public:
- Settings() : cluster(false), expectedBackups(0) {}
+ Settings() : cluster(false), replicateDefault(NONE)
+ {}
+
bool cluster; // True if we are a cluster member.
std::string clientUrl;
std::string brokerUrl;
- size_t expectedBackups;
+ Enum<ReplicateLevel> replicateDefault;
std::string username, password, mechanism;
private:
};
diff --git a/qpid/cpp/src/qpid/ha/management-schema.xml b/qpid/cpp/src/qpid/ha/management-schema.xml
index 9a815b346c..3da482e732 100644
--- a/qpid/cpp/src/qpid/ha/management-schema.xml
+++ b/qpid/cpp/src/qpid/ha/management-schema.xml
@@ -25,33 +25,39 @@
<property name="status" type="sstr" desc="HA status: primary or backup"/>
- <property name="brokers" type="sstr"
- desc="Multiple-address URL used by HA brokers to connect to each other."/>
+ <property name="brokersUrl" type="sstr"
+ desc="URL with address of each broker in the cluster."/>
- <property name="publicBrokers" type="sstr"
- desc="Multiple-address URL used by clients to connect to the HA brokers."/>
+ <property name="publicUrl" type="sstr"
+ desc="URL advertized to clients to connect to the cluster."/>
- <property name="expectedBackups" type="uint16"
- desc="Number of HA backup brokers expected."/>>
+ <property name="replicateDefault" type="sstr"
+ desc="Replication for queues/exchanges with no qpid.replicate argument"/>
+
+ <property name="members" type="list" desc="List of brokers in the cluster"/>
+
+ <property name="systemId" type="uuid" desc="Identifies the system."/>
<method name="promote" desc="Promote a backup broker to primary."/>
- <method name="setBrokers" desc="Set URL for HA brokers to connect to each other.">
+ <method name="setBrokersUrl" desc="URL listing each broker in the cluster.">
<arg name="url" type="sstr" dir="I"/>
</method>
- <method name="setPublicBrokers" desc="Set URL for clients to connect to HA brokers">
+ <method name="setPublicUrl" desc="URL advertized to clients.">
<arg name="url" type="sstr" dir="I"/>
</method>
- <method name="setExpectedBackups" desc="Set number of backups expected">
- <arg name="expectedBackups" type="uint16" dir="I"/>
- </method>
-
- <method name="replicate" desc="Replicate from a remote queue to the local broker.">
+ <method name="replicate" desc="Replicate individual queue from remote broker.">
<arg name="broker" type="sstr" dir="I"/>
<arg name="queue" type="sstr" dir="I"/>
</method>
</class>
+ <eventArguments>
+ <arg name="members" type="list" desc="List of broker information maps"/>
+ </eventArguments>
+
+ <event name="membersUpdate" sev="inform" args="members"/>
+
</schema>
diff --git a/qpid/cpp/src/qpid/ha/types.cpp b/qpid/cpp/src/qpid/ha/types.cpp
new file mode 100644
index 0000000000..53e2056213
--- /dev/null
+++ b/qpid/cpp/src/qpid/ha/types.cpp
@@ -0,0 +1,80 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "types.h"
+#include "qpid/Msg.h"
+#include "qpid/Exception.h"
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <assert.h>
+
+namespace qpid {
+namespace ha {
+
+using namespace std;
+
+const string QPID_REPLICATE("qpid.replicate");
+
+string EnumBase::str() const {
+ assert(value < count);
+ return names[value];
+}
+
+void EnumBase::parse(const string& s) {
+ if (!parseNoThrow(s))
+ throw Exception(QPID_MSG("Invalid " << name << " value: " << s));
+}
+
+bool EnumBase::parseNoThrow(const string& s) {
+ const char** i = find(names, names+count, s);
+ value = i - names;
+ return value < count;
+}
+
+template <> const char* Enum<ReplicateLevel>::NAME = "replication";
+template <> const char* Enum<ReplicateLevel>::NAMES[] = { "none", "configuration", "all" };
+template <> const size_t Enum<ReplicateLevel>::N = 3;
+
+template <> const char* Enum<BrokerStatus>::NAME = "HA broker status";
+template <> const char* Enum<BrokerStatus>::NAMES[] = {
+ "joining", "catchup", "ready", "recovering", "active", "standalone"
+};
+template <> const size_t Enum<BrokerStatus>::N = 6;
+
+ostream& operator<<(ostream& o, EnumBase e) {
+ return o << e.str();
+}
+
+istream& operator>>(istream& i, EnumBase& e) {
+ string s;
+ i >> s;
+ e.parse(s);
+ return i;
+}
+
+ostream& operator<<(ostream& o, const IdSet& ids) {
+ ostream_iterator<qpid::types::Uuid> out(o, " ");
+ copy(ids.begin(), ids.end(), out);
+ return o;
+}
+
+}} // namespace qpid::ha
diff --git a/qpid/cpp/src/qpid/ha/types.h b/qpid/cpp/src/qpid/ha/types.h
new file mode 100644
index 0000000000..35faf9f624
--- /dev/null
+++ b/qpid/cpp/src/qpid/ha/types.h
@@ -0,0 +1,109 @@
+#ifndef QPID_HA_ENUM_H
+#define QPID_HA_ENUM_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/types/Variant.h"
+#include "qpid/types/Uuid.h"
+#include <string>
+#include <set>
+#include <iosfwd>
+
+namespace qpid {
+
+namespace framing {
+class FieldTable;
+}
+
+namespace ha {
+
+/** Base class for enums with string conversion */
+class EnumBase {
+ public:
+ EnumBase(const char* name_, const char* names_[], size_t count_, unsigned value)
+ : name(name_), names(names_), count(count_), value(value) {}
+
+ /** Convert to string */
+ std::string str() const;
+ /** Parse from string, throw if unsuccessful */
+ void parse(const std::string&);
+ /** Parse from string, return false if unsuccessful. */
+ bool parseNoThrow(const std::string&);
+
+ protected:
+ const char* name;
+ const char** names;
+ size_t count;
+ unsigned value;
+};
+
+std::ostream& operator<<(std::ostream&, EnumBase);
+std::istream& operator>>(std::istream&, EnumBase&);
+
+/** Wrapper template for enums with string conversion */
+template <class T> class Enum : public EnumBase {
+ public:
+ Enum(T x=T()) : EnumBase(NAME, NAMES, N, x) {}
+ T get() const { return T(value); }
+ void operator=(T x) { value = x; }
+
+ private:
+ static const size_t N; // Number of enum values.
+ static const char* NAMES[]; // Names of enum values.
+ static const char* NAME; // Descriptive name for the enum type.
+};
+
+/** To print an enum x: o << printable(x) */
+template <class T> Enum<T> printable(T x) { return Enum<T>(x); }
+
+enum ReplicateLevel {
+ NONE, ///< Nothing is replicated
+ CONFIGURATION, ///< Wiring is replicated but not messages
+ ALL ///< Everything is replicated
+};
+
+/** State of a broker: see HaBroker::setStatus for state diagram */
+enum BrokerStatus {
+ JOINING, ///< New broker, looking for primary
+ CATCHUP, ///< Backup: Connected to primary, catching up on state.
+ READY, ///< Backup: Caught up, ready to take over.
+ RECOVERING, ///< Primary: waiting for backups to connect & sync
+ ACTIVE, ///< Primary: actively serving clients.
+ STANDALONE ///< Not part of a cluster.
+};
+
+inline bool isPrimary(BrokerStatus s) {
+ return s == RECOVERING || s == ACTIVE || s == STANDALONE;
+}
+
+inline bool isBackup(BrokerStatus s) { return !isPrimary(s); }
+
+// String constants.
+extern const std::string QPID_REPLICATE;
+
+/** Define IdSet type, not a typedef so we can overload operator << */
+class IdSet : public std::set<types::Uuid> {};
+
+std::ostream& operator<<(std::ostream& o, const IdSet& ids);
+
+}} // qpid::ha
+#endif /*!QPID_HA_ENUM_H*/
diff --git a/qpid/cpp/src/qpid/log/Logger.cpp b/qpid/cpp/src/qpid/log/Logger.cpp
index 800881c077..7c2e807ca9 100644
--- a/qpid/cpp/src/qpid/log/Logger.cpp
+++ b/qpid/cpp/src/qpid/log/Logger.cpp
@@ -47,7 +47,7 @@ using namespace std;
typedef sys::Mutex::ScopedLock ScopedLock;
inline void Logger::enable_unlocked(Statement* s) {
- s->enabled=selector.isEnabled(s->level, s->function);
+ s->enabled=selector.isEnabled(s->level, s->function, s->category);
}
Logger& Logger::instance() {
@@ -95,6 +95,8 @@ void Logger::log(const Statement& s, const std::string& msg) {
else
qpid::sys::outputFormattedNow(os);
}
+ if (flags&CATEGORY)
+ os << "[" << CategoryTraits::name(s.category) << "] ";
if (flags&LEVEL)
os << LevelTraits::name(s.level) << " ";
if (flags&THREAD)
@@ -144,7 +146,8 @@ int Logger::format(const Options& opts) {
bitIf(opts.source, (FILE|LINE)) |
bitIf(opts.function, FUNCTION) |
bitIf(opts.thread, THREAD) |
- bitIf(opts.hiresTs, HIRES);
+ bitIf(opts.hiresTs, HIRES) |
+ bitIf(opts.category, CATEGORY);
format(flags);
return flags;
}
diff --git a/qpid/cpp/src/qpid/log/Options.cpp b/qpid/cpp/src/qpid/log/Options.cpp
index 1259244297..10422bbb1e 100644
--- a/qpid/cpp/src/qpid/log/Options.cpp
+++ b/qpid/cpp/src/qpid/log/Options.cpp
@@ -39,6 +39,7 @@ Options::Options(const std::string& argv0_, const std::string& name_) :
source(false),
function(false),
hiresTs(false),
+ category(true),
trace(false),
sinkOptions (SinkOptions::create(argv0_))
{
@@ -49,15 +50,23 @@ Options::Options(const std::string& argv0_, const std::string& name_) :
for (int i = 1; i < LevelTraits::COUNT; ++i)
levels << " " << LevelTraits::name(Level(i));
+ ostringstream categories;
+ categories << CategoryTraits::name(Category(0));
+ for (int i = 1; i < CategoryTraits::COUNT; ++i)
+ categories << " " << CategoryTraits::name(Category(i));
+
addOptions()
("trace,t", optValue(trace), "Enables all logging" )
("log-enable", optValue(selectors, "RULE"),
- ("Enables logging for selected levels and components. "
+ ("Enables logging for selected levels and components. "
"RULE is in the form 'LEVEL[+][:PATTERN]' "
- "Levels are one of: \n\t "+levels.str()+"\n"
+ "LEVEL is one of: \n\t "+levels.str()+"\n"
+ "PATTERN is a function name or a catogory: \n\t "+categories.str()+"\n"
"For example:\n"
"\t'--log-enable warning+' "
"logs all warning, error and critical messages.\n"
+ "\t'--log-enable trace+:Broker' "
+ "logs all category 'Broker' messages.\n"
"\t'--log-enable debug:framing' "
"logs debug messages from the framing namespace. "
"This option can be used multiple times").c_str())
@@ -67,6 +76,7 @@ Options::Options(const std::string& argv0_, const std::string& name_) :
("log-thread", optValue(thread,"yes|no"), "Include thread ID in log messages")
("log-function", optValue(function,"yes|no"), "Include function signature in log messages")
("log-hires-timestamp", optValue(hiresTs,"yes|no"), "Use hi-resolution timestamps in log messages")
+ ("log-category", optValue(category,"yes|no"), "Include category in log messages")
("log-prefix", optValue(prefix,"STRING"), "Prefix to append to all log messages")
;
add(*sinkOptions);
@@ -83,6 +93,7 @@ Options::Options(const Options &o) :
source(o.source),
function(o.function),
hiresTs(o.hiresTs),
+ category(o.category),
trace(o.trace),
prefix(o.prefix),
sinkOptions (SinkOptions::create(o.argv0))
@@ -101,11 +112,12 @@ Options& Options::operator=(const Options& x) {
source = x.source;
function = x.function;
hiresTs = x.hiresTs;
+ category = x.category;
trace = x.trace;
prefix = x.prefix;
*sinkOptions = *x.sinkOptions;
}
return *this;
}
-
+
}} // namespace qpid::log
diff --git a/qpid/cpp/src/qpid/log/Selector.cpp b/qpid/cpp/src/qpid/log/Selector.cpp
index a4bc580470..8757486d88 100644
--- a/qpid/cpp/src/qpid/log/Selector.cpp
+++ b/qpid/cpp/src/qpid/log/Selector.cpp
@@ -37,18 +37,29 @@ void Selector::enable(const string& enableStr) {
level=enableStr.substr(0,c);
pattern=enableStr.substr(c+1);
}
+ bool isCat = CategoryTraits::isCategory(pattern);
if (!level.empty() && level[level.size()-1]=='+') {
for (int i = LevelTraits::level(level.substr(0,level.size()-1));
i < LevelTraits::COUNT;
- ++i)
- enable(Level(i), pattern);
+ ++i) {
+ if (isCat) {
+ enable(Level(i), CategoryTraits::category(pattern));
+ } else {
+ enable(Level(i), pattern);
+ }
+ }
}
else {
- enable(LevelTraits::level(level), pattern);
+ if (isCat) {
+ enable(LevelTraits::level(level), CategoryTraits::category(pattern));
+ } else {
+ enable(LevelTraits::level(level), pattern);
+ }
}
}
Selector::Selector(const Options& opt){
+ reset();
for_each(opt.selectors.begin(), opt.selectors.end(),
boost::bind(&Selector::enable, this, _1));
}
@@ -58,11 +69,17 @@ bool Selector::isEnabled(Level level, const char* function) {
for (std::vector<std::string>::iterator i=substrings[level].begin();
i != substrings[level].end();
++i)
- {
- if (std::search(function, functionEnd, i->begin(), i->end()) != functionEnd)
- return true;
- }
+ {
+ if (std::search(function, functionEnd, i->begin(), i->end()) != functionEnd)
+ return true;
+ }
return false;
}
+bool Selector::isEnabled(Level level, const char* function, Category category) {
+ if (catFlags[level][category])
+ return true;
+ return isEnabled(level, function);
+}
+
}} // namespace qpid::log
diff --git a/qpid/cpp/src/qpid/log/Statement.cpp b/qpid/cpp/src/qpid/log/Statement.cpp
index 79f7a28100..b0a58fabe7 100644
--- a/qpid/cpp/src/qpid/log/Statement.cpp
+++ b/qpid/cpp/src/qpid/log/Statement.cpp
@@ -36,7 +36,7 @@ std::string quote(const std::string& str) {
size_t n = std::count_if(str.begin(), str.end(), nonPrint);
if (n==0) return str;
std::string ret;
- ret.reserve(str.size()+2*n); // Avoid extra allocations.
+ ret.reserve(str.size()+3*n); // Avoid extra allocations.
for (std::string::const_iterator i = str.begin(); i != str.end(); ++i) {
if (nonPrint(*i)) {
ret.push_back('\\');
@@ -50,10 +50,42 @@ std::string quote(const std::string& str) {
}
}
+//
+// Instance of name hints
+//
+static CategoryFileNameHints filenameHints;
+
+
+Category CategoryFileNameHints::categoryOf(const char* const fName) {
+ for (std::list<std::pair<const char* const, Category> >::iterator
+ it = filenameHints.hintList.begin();
+ it != filenameHints.hintList.end();
+ ++it) {
+ if (strstr(fName, (const char* const)it->first) != 0) {
+ return it->second;
+ }
+ }
+ return unspecified;
+}
+
+
+void Statement::categorize(Statement& s) {
+ // given a statement and it's category
+ // if the category is Unspecified then try to find a
+ // better category based on the path and file name.
+ if (s.category == log::unspecified) {
+ s.category = CategoryFileNameHints::categoryOf(s.file);
+ } else {
+ // already has a category so leave it alone
+ }
+}
+
+
void Statement::log(const std::string& message) {
Logger::instance().log(*this, quote(message));
}
+
Statement::Initializer::Initializer(Statement& s) : statement(s) {
// QPID-3891
// From the given BOOST_CURRENT_FUNCTION name extract only the
@@ -99,16 +131,22 @@ Statement::Initializer::Initializer(Statement& s) : statement(s) {
// no function-name pointer to process
}
+ Statement::categorize(s);
Logger::instance().add(s);
}
+
namespace {
const char* names[LevelTraits::COUNT] = {
"trace", "debug", "info", "notice", "warning", "error", "critical"
};
-} // namespace
+const char* catNames[CategoryTraits::COUNT] = {
+ "Security", "Broker", "Management", "Protocol", "System", "HA", "Messaging",
+ "Store", "Network", "Test", "Client", "Unspecified"
+};
+} // namespace
Level LevelTraits::level(const char* name) {
for (int i =0; i < LevelTraits::COUNT; ++i) {
if (strcmp(names[i], name)==0)
@@ -121,4 +159,23 @@ const char* LevelTraits::name(Level l) {
return names[l];
}
+bool CategoryTraits::isCategory(const std::string& name) {
+ for (int i =0; i < CategoryTraits::COUNT; ++i) {
+ if (strcmp(catNames[i], name.c_str())==0)
+ return true;
+ }
+ return false;
+}
+
+Category CategoryTraits::category(const char* name) {
+ for (int i =0; i < CategoryTraits::COUNT; ++i) {
+ if (strcmp(catNames[i], name)==0)
+ return Category(i);
+ }
+ throw std::runtime_error(std::string("Invalid log category name: ")+name);
+}
+
+const char* CategoryTraits::name(Category c) {
+ return catNames[c];
+}
}} // namespace qpid::log
diff --git a/qpid/cpp/src/qpid/log/posix/SinkOptions.cpp b/qpid/cpp/src/qpid/log/posix/SinkOptions.cpp
index ffa7633e3b..8459938e5c 100644
--- a/qpid/cpp/src/qpid/log/posix/SinkOptions.cpp
+++ b/qpid/cpp/src/qpid/log/posix/SinkOptions.cpp
@@ -30,6 +30,10 @@
using std::string;
using qpid::Exception;
+namespace qpid {
+namespace log {
+namespace posix {
+
namespace {
// SyslogFacilities maps from syslog values to the text equivalents.
@@ -110,10 +114,6 @@ std::string basename(const std::string path) {
} // namespace
-namespace qpid {
-namespace log {
-namespace posix {
-
std::ostream& operator<<(std::ostream& o, const SyslogFacility& f) {
return o << SyslogFacilities().name(f.value);
}
diff --git a/qpid/cpp/src/qpid/management/Buffer.cpp b/qpid/cpp/src/qpid/management/Buffer.cpp
index 7556b2a243..0ad6e9a851 100644
--- a/qpid/cpp/src/qpid/management/Buffer.cpp
+++ b/qpid/cpp/src/qpid/management/Buffer.cpp
@@ -29,12 +29,11 @@ namespace management {
Buffer::Buffer(char* data, uint32_t size) : impl(new framing::Buffer(data, size)) {}
Buffer::~Buffer() { delete impl; }
-void Buffer::record() { impl->record(); }
-void Buffer::restore(bool reRecord) { impl->restore(reRecord); }
void Buffer::reset() { impl->reset(); }
uint32_t Buffer::available() { return impl->available(); }
uint32_t Buffer::getSize() { return impl->getSize(); }
uint32_t Buffer::getPosition() { return impl->getPosition(); }
+void Buffer::setPosition(uint32_t p) { impl->setPosition(p); }
char* Buffer::getPointer() { return impl->getPointer(); }
void Buffer::putOctet(uint8_t i) { impl->putOctet(i); }
void Buffer::putShort(uint16_t i) { impl->putShort(i); }
diff --git a/qpid/cpp/src/qpid/management/ManagementAgent.cpp b/qpid/cpp/src/qpid/management/ManagementAgent.cpp
index 8c2cb95faa..7d90ed99d0 100644
--- a/qpid/cpp/src/qpid/management/ManagementAgent.cpp
+++ b/qpid/cpp/src/qpid/management/ManagementAgent.cpp
@@ -30,6 +30,7 @@
#include "qpid/log/Statement.h"
#include <qpid/broker/Message.h>
#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/FieldValue.h"
#include "qpid/sys/Time.h"
#include "qpid/sys/Thread.h"
#include "qpid/broker/ConnectionState.h"
@@ -44,14 +45,15 @@
#include <sstream>
#include <typeinfo>
+namespace qpid {
+namespace management {
+
using boost::intrusive_ptr;
using qpid::framing::Uuid;
using qpid::types::Variant;
using qpid::amqp_0_10::MapCodec;
using qpid::amqp_0_10::ListCodec;
-using qpid::sys::Mutex;
using namespace qpid::framing;
-using namespace qpid::management;
using namespace qpid::broker;
using namespace qpid;
using namespace std;
@@ -1342,18 +1344,19 @@ void ManagementAgent::handleMethodRequestLH(Buffer& inBuffer, const string& repl
outBuffer.putLong (Manageable::STATUS_PARAMETER_INVALID);
outBuffer.putMediumString(Manageable::StatusText (Manageable::STATUS_PARAMETER_INVALID));
}
- else
+ else {
+ uint32_t pos = outBuffer.getPosition();
try {
- outBuffer.record();
sys::Mutex::ScopedUnlock u(userLock);
string outBuf;
iter->second->doMethod(methodName, inArgs, outBuf, userId);
outBuffer.putRawData(outBuf);
} catch(exception& e) {
- outBuffer.restore();
+ outBuffer.setPosition(pos);;
outBuffer.putLong(Manageable::STATUS_EXCEPTION);
outBuffer.putMediumString(e.what());
}
+ }
}
outLen = MA_BUFFER_SIZE - outBuffer.available();
@@ -1660,11 +1663,11 @@ void ManagementAgent::handleSchemaResponseLH(Buffer& inBuffer, const string& /*r
string packageName;
SchemaClassKey key;
- inBuffer.record();
+ uint32_t pos = inBuffer.getPosition();
inBuffer.getOctet();
inBuffer.getShortString(packageName);
key.decode(inBuffer);
- inBuffer.restore();
+ inBuffer.setPosition(pos);;
QPID_LOG(debug, "RECV SchemaResponse class=" << packageName << ":" << key.name << "(" << Uuid(key.hash) << ")" << " seq=" << sequence);
@@ -2424,7 +2427,6 @@ size_t ManagementAgent::validateTableSchema(Buffer& inBuffer)
uint8_t hash[16];
try {
- inBuffer.record();
uint8_t kind = inBuffer.getOctet();
if (kind != ManagementItem::CLASS_KIND_TABLE)
return 0;
@@ -2466,7 +2468,7 @@ size_t ManagementAgent::validateTableSchema(Buffer& inBuffer)
}
end = inBuffer.getPosition();
- inBuffer.restore(); // restore original position
+ inBuffer.setPosition(start); // restore original position
return end - start;
}
@@ -2478,7 +2480,6 @@ size_t ManagementAgent::validateEventSchema(Buffer& inBuffer)
uint8_t hash[16];
try {
- inBuffer.record();
uint8_t kind = inBuffer.getOctet();
if (kind != ManagementItem::CLASS_KIND_EVENT)
return 0;
@@ -2505,7 +2506,7 @@ size_t ManagementAgent::validateEventSchema(Buffer& inBuffer)
}
end = inBuffer.getPosition();
- inBuffer.restore(); // restore original position
+ inBuffer.setPosition(start); // restore original position
return end - start;
}
@@ -2961,9 +2962,6 @@ bool ManagementAgent::moveDeletedObjectsLH() {
return !deleteList.empty();
}
-namespace qpid {
-namespace management {
-
namespace {
QPID_TSS const qpid::broker::ConnectionState* executionContext = 0;
}
@@ -2978,3 +2976,4 @@ const qpid::broker::ConnectionState* getManagementExecutionContext()
}
}}
+
diff --git a/qpid/cpp/src/qpid/management/ManagementAgent.h b/qpid/cpp/src/qpid/management/ManagementAgent.h
index f01c66b4af..c7e830dcf5 100644
--- a/qpid/cpp/src/qpid/management/ManagementAgent.h
+++ b/qpid/cpp/src/qpid/management/ManagementAgent.h
@@ -36,7 +36,6 @@
#include "qpid/sys/MemStat.h"
#include "qpid/types/Variant.h"
#include <qpid/framing/AMQFrame.h>
-#include <qpid/framing/FieldValue.h>
#include <qpid/framing/ResizableBuffer.h>
#include <memory>
#include <string>
diff --git a/qpid/cpp/src/qpid/management/ManagementDirectExchange.cpp b/qpid/cpp/src/qpid/management/ManagementDirectExchange.cpp
index 312eacf462..9432a21b3a 100644
--- a/qpid/cpp/src/qpid/management/ManagementDirectExchange.cpp
+++ b/qpid/cpp/src/qpid/management/ManagementDirectExchange.cpp
@@ -28,7 +28,7 @@ using namespace qpid::broker;
using namespace qpid::framing;
using namespace qpid::sys;
-ManagementDirectExchange::ManagementDirectExchange(const string& _name, Manageable* _parent, Broker* b) :
+ManagementDirectExchange::ManagementDirectExchange(const std::string& _name, Manageable* _parent, Broker* b) :
Exchange (_name, _parent, b),
DirectExchange(_name, _parent, b),
managementAgent(0) {}
@@ -43,7 +43,7 @@ ManagementDirectExchange::ManagementDirectExchange(const std::string& _name,
void ManagementDirectExchange::route(Deliverable& msg)
{
bool routeIt = true;
- const string& routingKey = msg.getMessage().getRoutingKey();
+ const std::string& routingKey = msg.getMessage().getRoutingKey();
const FieldTable* args = msg.getMessage().getApplicationHeaders();
if (managementAgent)
diff --git a/qpid/cpp/src/qpid/management/ManagementTopicExchange.cpp b/qpid/cpp/src/qpid/management/ManagementTopicExchange.cpp
index 587cc660df..e5b659f217 100644
--- a/qpid/cpp/src/qpid/management/ManagementTopicExchange.cpp
+++ b/qpid/cpp/src/qpid/management/ManagementTopicExchange.cpp
@@ -27,7 +27,7 @@ using namespace qpid::broker;
using namespace qpid::framing;
using namespace qpid::sys;
-ManagementTopicExchange::ManagementTopicExchange(const string& _name, Manageable* _parent, Broker* b) :
+ManagementTopicExchange::ManagementTopicExchange(const std::string& _name, Manageable* _parent, Broker* b) :
Exchange (_name, _parent, b),
TopicExchange(_name, _parent, b),
managementAgent(0) {}
@@ -42,7 +42,7 @@ ManagementTopicExchange::ManagementTopicExchange(const std::string& _name,
void ManagementTopicExchange::route(Deliverable& msg)
{
bool routeIt = true;
- const string& routingKey = msg.getMessage().getRoutingKey();
+ const std::string& routingKey = msg.getMessage().getRoutingKey();
const FieldTable* args = msg.getMessage().getApplicationHeaders();
// Intercept management agent commands
@@ -54,7 +54,7 @@ void ManagementTopicExchange::route(Deliverable& msg)
}
bool ManagementTopicExchange::bind(Queue::shared_ptr queue,
- const string& routingKey,
+ const std::string& routingKey,
const qpid::framing::FieldTable* args)
{
if (qmfVersion == 1)
diff --git a/qpid/cpp/src/qpid/messaging/PrivateImplRef.h b/qpid/cpp/src/qpid/messaging/PrivateImplRef.h
index e77c58d071..a60f4eeadf 100644
--- a/qpid/cpp/src/qpid/messaging/PrivateImplRef.h
+++ b/qpid/cpp/src/qpid/messaging/PrivateImplRef.h
@@ -77,15 +77,15 @@ template <class T> class PrivateImplRef {
/** Set the implementation pointer in a handle */
static void set(T& t, const intrusive_ptr& p) {
if (t.impl == p) return;
- if (t.impl) boost::intrusive_ptr_release(t.impl);
+ if (t.impl) intrusive_ptr_release(t.impl);
t.impl = p.get();
- if (t.impl) boost::intrusive_ptr_add_ref(t.impl);
+ if (t.impl) intrusive_ptr_add_ref(t.impl);
}
// Helper functions to implement the ctor, dtor, copy, assign
- static void ctor(T& t, Impl* p) { t.impl = p; if (p) boost::intrusive_ptr_add_ref(p); }
+ static void ctor(T& t, Impl* p) { t.impl = p; if (p) intrusive_ptr_add_ref(p); }
static void copy(T& t, const T& x) { if (&t == &x) return; t.impl = 0; assign(t, x); }
- static void dtor(T& t) { if(t.impl) boost::intrusive_ptr_release(t.impl); }
+ static void dtor(T& t) { if(t.impl) intrusive_ptr_release(t.impl); }
static T& assign(T& t, const T& x) { set(t, get(x)); return t;}
};
diff --git a/qpid/cpp/src/qpid/replication/ReplicatingEventListener.cpp b/qpid/cpp/src/qpid/replication/ReplicatingEventListener.cpp
index 3d84a1ce3c..9284bda388 100644
--- a/qpid/cpp/src/qpid/replication/ReplicatingEventListener.cpp
+++ b/qpid/cpp/src/qpid/replication/ReplicatingEventListener.cpp
@@ -131,7 +131,7 @@ boost::intrusive_ptr<Message> ReplicatingEventListener::cloneMessage(Queue& queu
//cloned body:
AMQFrame header(*original->getFrames().getHeaders());
header.setBof(false);
- header.setEof(!original->getFrames().getContentSize());//if there is any content then the header is not the end of the frameset
+ header.setEof(!original->getFrames().hasContent());//if there are any content frames then the header is not the end of the frameset
header.setBos(true);
header.setEos(true);
handler.handle(header);
diff --git a/qpid/cpp/src/qpid/replication/ReplicationExchange.cpp b/qpid/cpp/src/qpid/replication/ReplicationExchange.cpp
index dce0d750a4..bcb7c7f293 100644
--- a/qpid/cpp/src/qpid/replication/ReplicationExchange.cpp
+++ b/qpid/cpp/src/qpid/replication/ReplicationExchange.cpp
@@ -60,7 +60,13 @@ void ReplicationExchange::route(Deliverable& msg)
if (args) {
int eventType = args->getAsInt(REPLICATION_EVENT_TYPE);
if (eventType) {
- if (isDuplicate(args)) return;
+ if (isDuplicate(args)) {
+ if (mgmtExchange != 0) {
+ mgmtExchange->inc_msgDrops();
+ mgmtExchange->inc_byteDrops(msg.contentSize());
+ }
+ return;
+ }
switch (eventType) {
case ENQUEUE:
handleEnqueueEvent(args, msg);
@@ -178,7 +184,7 @@ bool ReplicationExchange::unbind(Queue::shared_ptr /*queue*/, const std::string&
throw NotImplementedException("Replication exchange does not support unbind operation");
}
-bool ReplicationExchange::isBound(Queue::shared_ptr /*queue*/, const string* const /*routingKey*/, const FieldTable* const /*args*/)
+bool ReplicationExchange::isBound(Queue::shared_ptr /*queue*/, const std::string* const /*routingKey*/, const FieldTable* const /*args*/)
{
return false;
}
diff --git a/qpid/cpp/src/qpid/store/MessageStorePlugin.cpp b/qpid/cpp/src/qpid/store/MessageStorePlugin.cpp
index 2a8d971987..20231bf910 100644
--- a/qpid/cpp/src/qpid/store/MessageStorePlugin.cpp
+++ b/qpid/cpp/src/qpid/store/MessageStorePlugin.cpp
@@ -28,6 +28,9 @@
#include "qpid/DataDir.h"
#include "qpid/log/Statement.h"
+namespace qpid {
+namespace store {
+
/*
* The MessageStore pointer given to the Broker points to static storage.
* Thus, it cannot be deleted, especially by the broker. To prevent deletion,
@@ -42,9 +45,6 @@ namespace {
};
}
-namespace qpid {
-namespace store {
-
static MessageStorePlugin static_instance_registers_plugin;
diff --git a/qpid/cpp/src/qpid/store/ms-clfs/MessageLog.cpp b/qpid/cpp/src/qpid/store/ms-clfs/MessageLog.cpp
index 14d63a4cd4..849a0a44e8 100644
--- a/qpid/cpp/src/qpid/store/ms-clfs/MessageLog.cpp
+++ b/qpid/cpp/src/qpid/store/ms-clfs/MessageLog.cpp
@@ -32,6 +32,10 @@
#include "MessageLog.h"
#include "Lsn.h"
+namespace qpid {
+namespace store {
+namespace ms_clfs {
+
namespace {
// Structures that hold log records. Each has a type field at the start.
@@ -97,10 +101,6 @@ struct MessageDequeue {
} // namespace
-namespace qpid {
-namespace store {
-namespace ms_clfs {
-
void
MessageLog::initialize()
{
diff --git a/qpid/cpp/src/qpid/store/ms-clfs/Transaction.h b/qpid/cpp/src/qpid/store/ms-clfs/Transaction.h
index fd07f2fb2e..499b01d503 100644
--- a/qpid/cpp/src/qpid/store/ms-clfs/Transaction.h
+++ b/qpid/cpp/src/qpid/store/ms-clfs/Transaction.h
@@ -27,6 +27,7 @@
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <string>
+#include <vector>
#include "TransactionLog.h"
diff --git a/qpid/cpp/src/qpid/store/ms-clfs/TransactionLog.cpp b/qpid/cpp/src/qpid/store/ms-clfs/TransactionLog.cpp
index 04780e83e8..0ef046d7c8 100644
--- a/qpid/cpp/src/qpid/store/ms-clfs/TransactionLog.cpp
+++ b/qpid/cpp/src/qpid/store/ms-clfs/TransactionLog.cpp
@@ -33,6 +33,10 @@
#include "Transaction.h"
#include "Lsn.h"
+namespace qpid {
+namespace store {
+namespace ms_clfs {
+
namespace {
// Structures that hold log records. Each has a type field at the start.
@@ -95,10 +99,6 @@ struct TransactionDelete {
} // namespace
-namespace qpid {
-namespace store {
-namespace ms_clfs {
-
void
TransactionLog::initialize()
{
diff --git a/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp b/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp
index 5233002850..8e83c9b32d 100644
--- a/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp
+++ b/qpid/cpp/src/qpid/sys/AsynchIOHandler.cpp
@@ -23,6 +23,7 @@
#include "qpid/sys/AsynchIO.h"
#include "qpid/sys/Socket.h"
#include "qpid/sys/SecuritySettings.h"
+#include "qpid/sys/Timer.h"
#include "qpid/framing/AMQP_HighestVersion.h"
#include "qpid/framing/ProtocolInitiation.h"
#include "qpid/log/Statement.h"
@@ -41,7 +42,25 @@ struct Buff : public AsynchIO::BufferBase {
{ delete [] bytes;}
};
-AsynchIOHandler::AsynchIOHandler(std::string id, ConnectionCodec::Factory* f) :
+struct ProtocolTimeoutTask : public sys::TimerTask {
+ AsynchIOHandler& handler;
+ std::string id;
+
+ ProtocolTimeoutTask(const std::string& i, const Duration& timeout, AsynchIOHandler& h) :
+ TimerTask(timeout, "ProtocolTimeout"),
+ handler(h),
+ id(i)
+ {}
+
+ void fire() {
+ // If this fires it means that we didn't negotiate the connection in the timeout period
+ // Schedule closing the connection for the io thread
+ QPID_LOG(error, "Connection " << id << " No protocol received closing");
+ handler.abort();
+ }
+};
+
+AsynchIOHandler::AsynchIOHandler(const std::string& id, ConnectionCodec::Factory* f) :
identifier(id),
aio(0),
factory(f),
@@ -54,12 +73,18 @@ AsynchIOHandler::AsynchIOHandler(std::string id, ConnectionCodec::Factory* f) :
AsynchIOHandler::~AsynchIOHandler() {
if (codec)
codec->closed();
+ if (timeoutTimerTask)
+ timeoutTimerTask->cancel();
delete codec;
}
-void AsynchIOHandler::init(AsynchIO* a, int numBuffs) {
+void AsynchIOHandler::init(qpid::sys::AsynchIO* a, qpid::sys::Timer& timer, uint32_t maxTime, int numBuffs) {
aio = a;
+ // Start timer for this connection
+ timeoutTimerTask = new ProtocolTimeoutTask(identifier, maxTime*TIME_MSEC, *this);
+ timer.add(timeoutTimerTask);
+
// Give connection some buffers to use
for (int i = 0; i < numBuffs; i++) {
aio->queueReadBuffer(new Buff);
@@ -143,6 +168,9 @@ void AsynchIOHandler::readbuff(AsynchIO& , AsynchIO::BufferBase* buff) {
framing::ProtocolInitiation protocolInit;
if (protocolInit.decode(in)) {
decoded = in.getPosition();
+ // We've just got the protocol negotiation so we can cancel the timeout for that
+ timeoutTimerTask->cancel();
+
QPID_LOG(debug, "RECV [" << identifier << "]: INIT(" << protocolInit << ")");
try {
codec = factory->create(protocolInit.getVersion(), *this, identifier, SecuritySettings());
@@ -202,6 +230,10 @@ void AsynchIOHandler::idle(AsynchIO&){
if (isClient && codec == 0) {
codec = factory->create(*this, identifier, SecuritySettings());
write(framing::ProtocolInitiation(codec->getVersion()));
+ // We've just sent the protocol negotiation so we can cancel the timeout for that
+ // This is not ideal, because we've not received anything yet, but heartbeats will
+ // be active soon
+ timeoutTimerTask->cancel();
return;
}
if (codec == 0) return;
diff --git a/qpid/cpp/src/qpid/sys/AsynchIOHandler.h b/qpid/cpp/src/qpid/sys/AsynchIOHandler.h
index b9867606c4..2ddd5c9a90 100644
--- a/qpid/cpp/src/qpid/sys/AsynchIOHandler.h
+++ b/qpid/cpp/src/qpid/sys/AsynchIOHandler.h
@@ -27,6 +27,8 @@
#include "qpid/sys/Mutex.h"
#include "qpid/CommonImportExport.h"
+#include <boost/intrusive_ptr.hpp>
+
namespace qpid {
namespace framing {
@@ -38,6 +40,8 @@ namespace sys {
class AsynchIO;
struct AsynchIOBufferBase;
class Socket;
+class Timer;
+class TimerTask;
class AsynchIOHandler : public OutputControl {
std::string identifier;
@@ -49,13 +53,14 @@ class AsynchIOHandler : public OutputControl {
AtomicValue<int32_t> readCredit;
static const int32_t InfiniteCredit = -1;
Mutex creditLock;
+ boost::intrusive_ptr<sys::TimerTask> timeoutTimerTask;
void write(const framing::ProtocolInitiation&);
public:
- QPID_COMMON_EXTERN AsynchIOHandler(std::string id, ConnectionCodec::Factory* f);
+ QPID_COMMON_EXTERN AsynchIOHandler(const std::string& id, qpid::sys::ConnectionCodec::Factory* f );
QPID_COMMON_EXTERN ~AsynchIOHandler();
- QPID_COMMON_EXTERN void init(AsynchIO* a, int numBuffs);
+ QPID_COMMON_EXTERN void init(AsynchIO* a, Timer& timer, uint32_t maxTime, int numBuffs);
QPID_COMMON_INLINE_EXTERN void setClient() { isClient = true; }
diff --git a/qpid/cpp/src/qpid/sys/windows/MemStat.cpp b/qpid/cpp/src/qpid/sys/MemStat.cpp
index b1d25c5fc5..c71fba785c 100644
--- a/qpid/cpp/src/qpid/sys/windows/MemStat.cpp
+++ b/qpid/cpp/src/qpid/sys/MemStat.cpp
@@ -21,9 +21,11 @@
#include "qpid/sys/MemStat.h"
+// Null memory stats provider:
+// This is for platforms that do not have a way to get allocated
+// memory status
void qpid::sys::MemStat::loadMemInfo(qmf::org::apache::qpid::broker::Memory*)
{
- // TODO: Add Windows-specific memory stats to the object and load them here.
}
diff --git a/qpid/cpp/src/qpid/sys/SslPlugin.cpp b/qpid/cpp/src/qpid/sys/SslPlugin.cpp
index 48baef9042..3b50527c0a 100644
--- a/qpid/cpp/src/qpid/sys/SslPlugin.cpp
+++ b/qpid/cpp/src/qpid/sys/SslPlugin.cpp
@@ -39,6 +39,8 @@
namespace qpid {
namespace sys {
+class Timer;
+
using namespace qpid::sys::ssl;
struct SslServerOptions : ssl::SslOptions
@@ -68,6 +70,8 @@ class SslProtocolFactoryTmpl : public ProtocolFactory {
typedef SslAcceptorTmpl<T> SslAcceptor;
+ Timer& brokerTimer;
+ uint32_t maxNegotiateTime;
const bool tcpNoDelay;
T listener;
const uint16_t listeningPort;
@@ -75,7 +79,7 @@ class SslProtocolFactoryTmpl : public ProtocolFactory {
bool nodict;
public:
- SslProtocolFactoryTmpl(const SslServerOptions&, int backlog, bool nodelay);
+ SslProtocolFactoryTmpl(const SslServerOptions&, int backlog, bool nodelay, Timer& timer, uint32_t maxTime);
void accept(Poller::shared_ptr, ConnectionCodec::Factory*);
void connect(Poller::shared_ptr, const std::string& host, const std::string& port,
ConnectionCodec::Factory*,
@@ -132,16 +136,18 @@ static struct SslPlugin : public Plugin {
try {
ssl::initNSS(options, true);
nssInitialized = true;
-
+
const broker::Broker::Options& opts = broker->getOptions();
ProtocolFactory::shared_ptr protocol(options.multiplex ?
static_cast<ProtocolFactory*>(new SslMuxProtocolFactory(options,
opts.connectionBacklog,
- opts.tcpNoDelay)) :
+ opts.tcpNoDelay,
+ broker->getTimer(), opts.maxNegotiateTime)) :
static_cast<ProtocolFactory*>(new SslProtocolFactory(options,
opts.connectionBacklog,
- opts.tcpNoDelay)));
+ opts.tcpNoDelay,
+ broker->getTimer(), opts.maxNegotiateTime)));
QPID_LOG(notice, "Listening for " <<
(options.multiplex ? "SSL or TCP" : "SSL") <<
" connections on TCP port " <<
@@ -156,14 +162,16 @@ static struct SslPlugin : public Plugin {
} sslPlugin;
template <class T>
-SslProtocolFactoryTmpl<T>::SslProtocolFactoryTmpl(const SslServerOptions& options, int backlog, bool nodelay) :
+SslProtocolFactoryTmpl<T>::SslProtocolFactoryTmpl(const SslServerOptions& options, int backlog, bool nodelay, Timer& timer, uint32_t maxTime) :
+ brokerTimer(timer),
+ maxNegotiateTime(maxTime),
tcpNoDelay(nodelay), listeningPort(listener.listen(options.port, backlog, options.certName, options.clientAuth)),
nodict(options.nodict)
{}
void SslEstablished(Poller::shared_ptr poller, const qpid::sys::SslSocket& s,
ConnectionCodec::Factory* f, bool isClient,
- bool tcpNoDelay, bool nodict) {
+ Timer& timer, uint32_t maxTime, bool tcpNoDelay, bool nodict) {
qpid::sys::ssl::SslHandler* async = new qpid::sys::ssl::SslHandler(s.getFullAddress(), f, nodict);
if (tcpNoDelay) {
@@ -183,7 +191,7 @@ void SslEstablished(Poller::shared_ptr poller, const qpid::sys::SslSocket& s,
boost::bind(&qpid::sys::ssl::SslHandler::nobuffs, async, _1),
boost::bind(&qpid::sys::ssl::SslHandler::idle, async, _1));
- async->init(aio, 4);
+ async->init(aio,timer, maxTime, 4);
aio->start(poller);
}
@@ -192,7 +200,7 @@ void SslProtocolFactory::established(Poller::shared_ptr poller, const Socket& s,
ConnectionCodec::Factory* f, bool isClient) {
const SslSocket *sslSock = dynamic_cast<const SslSocket*>(&s);
- SslEstablished(poller, *sslSock, f, isClient, tcpNoDelay, nodict);
+ SslEstablished(poller, *sslSock, f, isClient, brokerTimer, maxNegotiateTime, tcpNoDelay, nodict);
}
template <class T>
@@ -216,7 +224,7 @@ void SslMuxProtocolFactory::established(Poller::shared_ptr poller, const Socket&
const SslSocket *sslSock = dynamic_cast<const SslSocket*>(&s);
if (sslSock) {
- SslEstablished(poller, *sslSock, f, isClient, tcpNoDelay, nodict);
+ SslEstablished(poller, *sslSock, f, isClient, brokerTimer, maxNegotiateTime, tcpNoDelay, nodict);
return;
}
@@ -239,7 +247,7 @@ void SslMuxProtocolFactory::established(Poller::shared_ptr poller, const Socket&
boost::bind(&AsynchIOHandler::nobuffs, async, _1),
boost::bind(&AsynchIOHandler::idle, async, _1));
- async->init(aio, 4);
+ async->init(aio, brokerTimer, maxNegotiateTime, 4);
aio->start(poller);
}
diff --git a/qpid/cpp/src/qpid/sys/TCPIOPlugin.cpp b/qpid/cpp/src/qpid/sys/TCPIOPlugin.cpp
index bd10a5555a..551440f954 100644
--- a/qpid/cpp/src/qpid/sys/TCPIOPlugin.cpp
+++ b/qpid/cpp/src/qpid/sys/TCPIOPlugin.cpp
@@ -36,14 +36,21 @@
namespace qpid {
namespace sys {
+class Timer;
+
class AsynchIOProtocolFactory : public ProtocolFactory {
- const bool tcpNoDelay;
boost::ptr_vector<Socket> listeners;
boost::ptr_vector<AsynchAcceptor> acceptors;
+ Timer& brokerTimer;
+ uint32_t maxNegotiateTime;
uint16_t listeningPort;
+ const bool tcpNoDelay;
public:
- AsynchIOProtocolFactory(const std::string& host, const std::string& port, int backlog, bool nodelay, bool shouldListen);
+ AsynchIOProtocolFactory(const std::string& host, const std::string& port,
+ int backlog, bool nodelay,
+ Timer& timer, uint32_t maxTime,
+ bool shouldListen);
void accept(Poller::shared_ptr, ConnectionCodec::Factory*);
void connect(Poller::shared_ptr, const std::string& host, const std::string& port,
ConnectionCodec::Factory*,
@@ -90,6 +97,7 @@ static class TCPIOPlugin : public Plugin {
"", boost::lexical_cast<std::string>(opts.port),
opts.connectionBacklog,
opts.tcpNoDelay,
+ broker->getTimer(), opts.maxNegotiateTime,
shouldListen));
if (shouldListen) {
@@ -101,7 +109,12 @@ static class TCPIOPlugin : public Plugin {
}
} tcpPlugin;
-AsynchIOProtocolFactory::AsynchIOProtocolFactory(const std::string& host, const std::string& port, int backlog, bool nodelay, bool shouldListen) :
+AsynchIOProtocolFactory::AsynchIOProtocolFactory(const std::string& host, const std::string& port,
+ int backlog, bool nodelay,
+ Timer& timer, uint32_t maxTime,
+ bool shouldListen) :
+ brokerTimer(timer),
+ maxNegotiateTime(maxTime),
tcpNoDelay(nodelay)
{
if (!shouldListen) {
@@ -153,7 +166,7 @@ void AsynchIOProtocolFactory::established(Poller::shared_ptr poller, const Socke
boost::bind(&AsynchIOHandler::nobuffs, async, _1),
boost::bind(&AsynchIOHandler::idle, async, _1));
- async->init(aio, 4);
+ async->init(aio, brokerTimer, maxNegotiateTime, 4);
aio->start(poller);
}
diff --git a/qpid/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp b/qpid/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp
index 249b769051..29b91f3e7a 100644
--- a/qpid/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp
+++ b/qpid/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp
@@ -18,6 +18,7 @@
* under the License.
*
*/
+#include <unistd.h>
#include "qpid/sys/cyrus/CyrusSecurityLayer.h"
#include <algorithm>
#include "qpid/framing/reply_exceptions.h"
diff --git a/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp b/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp
index c25159985e..01ff8b6bfa 100644
--- a/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp
+++ b/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp
@@ -41,7 +41,9 @@
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
-using namespace qpid::sys;
+namespace qpid {
+namespace sys {
+namespace posix {
namespace {
@@ -71,10 +73,6 @@ __thread int64_t threadMaxIoTimeNs = 2 * 1000000; // start at 2ms
/*
* Asynch Acceptor
*/
-namespace qpid {
-namespace sys {
-namespace posix {
-
class AsynchAcceptor : public qpid::sys::AsynchAcceptor {
public:
AsynchAcceptor(const Socket& s, AsynchAcceptor::Callback callback);
diff --git a/qpid/cpp/src/qpid/sys/posix/LockFile.cpp b/qpid/cpp/src/qpid/sys/posix/LockFile.cpp
index c1f1c37b47..9fdf83f1bd 100755
--- a/qpid/cpp/src/qpid/sys/posix/LockFile.cpp
+++ b/qpid/cpp/src/qpid/sys/posix/LockFile.cpp
@@ -46,7 +46,7 @@ LockFile::LockFile(const std::string& path_, bool create)
errno = 0;
int flags=create ? O_WRONLY|O_CREAT|O_NOFOLLOW : O_RDWR;
int fd = ::open(path.c_str(), flags, 0644);
- if (fd < 0) throw ErrnoException("Cannot open " + path, errno);
+ if (fd < 0) throw ErrnoException("Cannot open lock file " + path, errno);
if (::lockf(fd, F_TLOCK, 0) < 0) {
::close(fd);
throw ErrnoException("Cannot lock " + path, errno);
diff --git a/qpid/cpp/src/qpid/sys/posix/MemStat.cpp b/qpid/cpp/src/qpid/sys/posix/MemStat.cpp
index 72c53e5886..2fbf119cab 100644
--- a/qpid/cpp/src/qpid/sys/posix/MemStat.cpp
+++ b/qpid/cpp/src/qpid/sys/posix/MemStat.cpp
@@ -20,6 +20,7 @@
*/
#include "qpid/sys/MemStat.h"
+
#include <malloc.h>
void qpid::sys::MemStat::loadMemInfo(qmf::org::apache::qpid::broker::Memory* object)
@@ -35,4 +36,3 @@ void qpid::sys::MemStat::loadMemInfo(qmf::org::apache::qpid::broker::Memory* obj
object->set_malloc_keepcost(info.keepcost);
}
-
diff --git a/qpid/cpp/src/qpid/sys/posix/PollableCondition.cpp b/qpid/cpp/src/qpid/sys/posix/PollableCondition.cpp
index b22a615a54..abff8a5be8 100644
--- a/qpid/cpp/src/qpid/sys/posix/PollableCondition.cpp
+++ b/qpid/cpp/src/qpid/sys/posix/PollableCondition.cpp
@@ -1,6 +1,3 @@
-#ifndef QPID_SYS_LINUX_POLLABLECONDITION_CPP
-#define QPID_SYS_LINUX_POLLABLECONDITION_CPP
-
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -120,5 +117,3 @@ void PollableCondition::set() { impl->set(); }
void PollableCondition::clear() { impl->clear(); }
}} // namespace qpid::sys
-
-#endif /*!QPID_SYS_LINUX_POLLABLECONDITION_CPP*/
diff --git a/qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp b/qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp
index a7049c1851..344bd28669 100644
--- a/qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp
+++ b/qpid/cpp/src/qpid/sys/posix/SocketAddress.cpp
@@ -35,14 +35,16 @@ namespace sys {
SocketAddress::SocketAddress(const std::string& host0, const std::string& port0) :
host(host0),
port(port0),
- addrInfo(0)
+ addrInfo(0),
+ currentAddrInfo(0)
{
}
SocketAddress::SocketAddress(const SocketAddress& sa) :
host(sa.host),
port(sa.port),
- addrInfo(0)
+ addrInfo(0),
+ currentAddrInfo(0)
{
}
diff --git a/qpid/cpp/src/qpid/sys/posix/SystemInfo.cpp b/qpid/cpp/src/qpid/sys/posix/SystemInfo.cpp
index 540cc8bc91..007d0773fc 100755
--- a/qpid/cpp/src/qpid/sys/posix/SystemInfo.cpp
+++ b/qpid/cpp/src/qpid/sys/posix/SystemInfo.cpp
@@ -18,10 +18,11 @@
*
*/
+#include "qpid/log/Statement.h"
#include "qpid/sys/SystemInfo.h"
-
#include "qpid/sys/posix/check.h"
-
+#include <set>
+#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/utsname.h>
#include <sys/types.h> // For FreeBSD
@@ -59,48 +60,97 @@ bool SystemInfo::getLocalHostname (Address &address) {
return true;
}
-static const string LOCALHOST("127.0.0.1");
+static const string LOOPBACK("127.0.0.1");
static const string TCP("tcp");
+// Test IPv4 address for loopback
+inline bool IN_IS_ADDR_LOOPBACK(const ::in_addr* a) {
+ return ((ntohl(a->s_addr) & 0xff000000) == 0x7f000000);
+}
+
+inline bool isLoopback(const ::sockaddr* addr) {
+ switch (addr->sa_family) {
+ case AF_INET: return IN_IS_ADDR_LOOPBACK(&((const ::sockaddr_in*)(const void*)addr)->sin_addr);
+ case AF_INET6: return IN6_IS_ADDR_LOOPBACK(&((const ::sockaddr_in6*)(const void*)addr)->sin6_addr);
+ default: return false;
+ }
+}
+
void SystemInfo::getLocalIpAddresses (uint16_t port,
std::vector<Address> &addrList) {
::ifaddrs* ifaddr = 0;
QPID_POSIX_CHECK(::getifaddrs(&ifaddr));
for (::ifaddrs* ifap = ifaddr; ifap != 0; ifap = ifap->ifa_next) {
if (ifap->ifa_addr == 0) continue;
-
+ if (isLoopback(ifap->ifa_addr)) continue;
int family = ifap->ifa_addr->sa_family;
switch (family) {
- case AF_INET: {
- char dispName[NI_MAXHOST];
- int rc = ::getnameinfo(
- ifap->ifa_addr,
- (family == AF_INET)
- ? sizeof(struct sockaddr_in)
- : sizeof(struct sockaddr_in6),
- dispName, sizeof(dispName),
- 0, 0, NI_NUMERICHOST);
- if (rc != 0) {
- throw QPID_POSIX_ERROR(rc);
+ case AF_INET6: {
+ // Ignore link local addresses as:
+ // * The scope id is illegal in URL syntax
+ // * Clients won't be able to use a link local address
+ // without adding their own (potentially different) scope id
+ sockaddr_in6* sa6 = (sockaddr_in6*)(ifap->ifa_addr);
+ if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) break;
+ // Fallthrough
}
- string addr(dispName);
- if (addr != LOCALHOST) {
- addrList.push_back(Address(TCP, addr, port));
- }
- break;
- }
- // TODO: Url parsing currently can't cope with IPv6 addresses so don't return them
- // when it can cope move this line to above "case AF_INET:"
- case AF_INET6:
- default:
+ case AF_INET: {
+ char dispName[NI_MAXHOST];
+ int rc = ::getnameinfo(
+ ifap->ifa_addr,
+ (family == AF_INET)
+ ? sizeof(struct sockaddr_in)
+ : sizeof(struct sockaddr_in6),
+ dispName, sizeof(dispName),
+ 0, 0, NI_NUMERICHOST);
+ if (rc != 0) {
+ throw QPID_POSIX_ERROR(rc);
+ }
+ string addr(dispName);
+ addrList.push_back(Address(TCP, addr, port));
+ break;
+ }
+ default:
continue;
}
}
- freeifaddrs(ifaddr);
+ ::freeifaddrs(ifaddr);
if (addrList.empty()) {
- addrList.push_back(Address(TCP, LOCALHOST, port));
+ addrList.push_back(Address(TCP, LOOPBACK, port));
+ }
+}
+
+namespace {
+struct AddrInfo {
+ struct addrinfo* ptr;
+ AddrInfo(const std::string& host) : ptr(0) {
+ if (::getaddrinfo(host.c_str(), NULL, NULL, &ptr) != 0)
+ ptr = 0;
+ }
+ ~AddrInfo() { if (ptr) ::freeaddrinfo(ptr); }
+};
+}
+
+bool SystemInfo::isLocalHost(const std::string& host) {
+ std::vector<Address> myAddrs;
+ getLocalIpAddresses(0, myAddrs);
+ std::set<string> localHosts;
+ for (std::vector<Address>::const_iterator i = myAddrs.begin(); i != myAddrs.end(); ++i)
+ localHosts.insert(i->host);
+ // Resolve host
+ AddrInfo ai(host);
+ if (!ai.ptr) return false;
+ for (struct addrinfo *res = ai.ptr; res != NULL; res = res->ai_next) {
+ if (isLoopback(res->ai_addr)) return true;
+ // Get string form of IP addr
+ char addr[NI_MAXHOST] = "";
+ int error = ::getnameinfo(res->ai_addr, res->ai_addrlen, addr, NI_MAXHOST, NULL, 0,
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ if (error) return false;
+ if (localHosts.find(addr) != localHosts.end()) return true;
}
+ return false;
}
void SystemInfo::getSystemId (std::string &osName,
diff --git a/qpid/cpp/src/qpid/sys/ssl/SslHandler.cpp b/qpid/cpp/src/qpid/sys/ssl/SslHandler.cpp
index 67bf4ea893..8613059f28 100644
--- a/qpid/cpp/src/qpid/sys/ssl/SslHandler.cpp
+++ b/qpid/cpp/src/qpid/sys/ssl/SslHandler.cpp
@@ -19,9 +19,9 @@
*
*/
#include "qpid/sys/ssl/SslHandler.h"
-
#include "qpid/sys/ssl/SslIo.h"
#include "qpid/sys/ssl/SslSocket.h"
+#include "qpid/sys/Timer.h"
#include "qpid/framing/AMQP_HighestVersion.h"
#include "qpid/framing/ProtocolInitiation.h"
#include "qpid/log/Statement.h"
@@ -42,6 +42,24 @@ struct Buff : public SslIO::BufferBase {
{ delete [] bytes;}
};
+struct ProtocolTimeoutTask : public sys::TimerTask {
+ SslHandler& handler;
+ std::string id;
+
+ ProtocolTimeoutTask(const std::string& i, const Duration& timeout, SslHandler& h) :
+ TimerTask(timeout, "ProtocolTimeout"),
+ handler(h),
+ id(i)
+ {}
+
+ void fire() {
+ // If this fires it means that we didn't negotiate the connection in the timeout period
+ // Schedule closing the connection for the io thread
+ QPID_LOG(error, "Connection " << id << " No protocol received closing");
+ handler.abort();
+ }
+};
+
SslHandler::SslHandler(std::string id, ConnectionCodec::Factory* f, bool _nodict) :
identifier(id),
aio(0),
@@ -55,12 +73,18 @@ SslHandler::SslHandler(std::string id, ConnectionCodec::Factory* f, bool _nodict
SslHandler::~SslHandler() {
if (codec)
codec->closed();
+ if (timeoutTimerTask)
+ timeoutTimerTask->cancel();
delete codec;
}
-void SslHandler::init(SslIO* a, int numBuffs) {
+void SslHandler::init(SslIO* a, Timer& timer, uint32_t maxTime, int numBuffs) {
aio = a;
+ // Start timer for this connection
+ timeoutTimerTask = new ProtocolTimeoutTask(identifier, maxTime*TIME_MSEC, *this);
+ timer.add(timeoutTimerTask);
+
// Give connection some buffers to use
for (int i = 0; i < numBuffs; i++) {
aio->queueReadBuffer(new Buff);
@@ -80,8 +104,10 @@ void SslHandler::write(const framing::ProtocolInitiation& data)
}
void SslHandler::abort() {
- // TODO: can't implement currently as underlying functionality not implemented
- // aio->requestCallback(boost::bind(&SslHandler::eof, this, _1));
+ // Don't disconnect if we're already disconnecting
+ if (!readError) {
+ aio->requestCallback(boost::bind(&SslHandler::eof, this, _1));
+ }
}
void SslHandler::activateOutput() {
aio->notifyPendingWrite();
@@ -109,6 +135,9 @@ void SslHandler::readbuff(SslIO& , SslIO::BufferBase* buff) {
framing::Buffer in(buff->bytes+buff->dataStart, buff->dataCount);
framing::ProtocolInitiation protocolInit;
if (protocolInit.decode(in)) {
+ // We've just got the protocol negotiation so we can cancel the timeout for that
+ timeoutTimerTask->cancel();
+
decoded = in.getPosition();
QPID_LOG(debug, "RECV [" << identifier << "]: INIT(" << protocolInit << ")");
try {
@@ -169,6 +198,10 @@ void SslHandler::idle(SslIO&){
if (isClient && codec == 0) {
codec = factory->create(*this, identifier, getSecuritySettings(aio));
write(framing::ProtocolInitiation(codec->getVersion()));
+ // We've just sent the protocol negotiation so we can cancel the timeout for that
+ // This is not ideal, because we've not received anything yet, but heartbeats will
+ // be active soon
+ timeoutTimerTask->cancel();
return;
}
if (codec == 0) return;
diff --git a/qpid/cpp/src/qpid/sys/ssl/SslHandler.h b/qpid/cpp/src/qpid/sys/ssl/SslHandler.h
index 400fa317fd..74df2b7fb0 100644
--- a/qpid/cpp/src/qpid/sys/ssl/SslHandler.h
+++ b/qpid/cpp/src/qpid/sys/ssl/SslHandler.h
@@ -25,6 +25,8 @@
#include "qpid/sys/ConnectionCodec.h"
#include "qpid/sys/OutputControl.h"
+#include <boost/intrusive_ptr.hpp>
+
namespace qpid {
namespace framing {
@@ -32,6 +34,10 @@ namespace framing {
}
namespace sys {
+
+class Timer;
+class TimerTask;
+
namespace ssl {
class SslIO;
@@ -46,6 +52,7 @@ class SslHandler : public OutputControl {
bool readError;
bool isClient;
bool nodict;
+ boost::intrusive_ptr<sys::TimerTask> timeoutTimerTask;
void write(const framing::ProtocolInitiation&);
qpid::sys::SecuritySettings getSecuritySettings(SslIO* aio);
@@ -53,7 +60,7 @@ class SslHandler : public OutputControl {
public:
SslHandler(std::string id, ConnectionCodec::Factory* f, bool nodict);
~SslHandler();
- void init(SslIO* a, int numBuffs);
+ void init(SslIO* a, Timer& timer, uint32_t maxTime, int numBuffs);
void setClient() { isClient = true; }
diff --git a/qpid/cpp/src/qpid/sys/ssl/SslIo.cpp b/qpid/cpp/src/qpid/sys/ssl/SslIo.cpp
index 73f15617dc..789c205ead 100644
--- a/qpid/cpp/src/qpid/sys/ssl/SslIo.cpp
+++ b/qpid/cpp/src/qpid/sys/ssl/SslIo.cpp
@@ -37,8 +37,9 @@
#include <boost/bind.hpp>
-using namespace qpid::sys;
-using namespace qpid::sys::ssl;
+namespace qpid {
+namespace sys {
+namespace ssl {
namespace {
@@ -256,6 +257,18 @@ void SslIO::queueWriteClose() {
DispatchHandle::rewatchWrite();
}
+void SslIO::requestCallback(RequestCallback callback) {
+ // TODO creating a function object every time isn't all that
+ // efficient - if this becomes heavily used do something better (what?)
+ assert(callback);
+ DispatchHandle::call(boost::bind(&SslIO::requestedCall, this, callback));
+}
+
+void SslIO::requestedCall(RequestCallback callback) {
+ assert(callback);
+ callback(*this);
+}
+
/** Return a queued buffer if there are enough
* to spare
*/
@@ -448,3 +461,5 @@ SecuritySettings SslIO::getSecuritySettings() {
settings.authid = socket.getClientAuthId();
return settings;
}
+
+}}}
diff --git a/qpid/cpp/src/qpid/sys/ssl/SslIo.h b/qpid/cpp/src/qpid/sys/ssl/SslIo.h
index c980d73831..b795594cd9 100644
--- a/qpid/cpp/src/qpid/sys/ssl/SslIo.h
+++ b/qpid/cpp/src/qpid/sys/ssl/SslIo.h
@@ -125,6 +125,7 @@ public:
typedef boost::function2<void, SslIO&, const SslSocket&> ClosedCallback;
typedef boost::function1<void, SslIO&> BuffersEmptyCallback;
typedef boost::function1<void, SslIO&> IdleCallback;
+ typedef boost::function1<void, SslIO&> RequestCallback;
private:
@@ -159,6 +160,7 @@ public:
void notifyPendingWrite();
void queueWriteClose();
bool writeQueueEmpty() { return writeQueue.empty(); }
+ void requestCallback(RequestCallback);
BufferBase* getQueuedBuffer();
qpid::sys::SecuritySettings getSecuritySettings();
@@ -168,6 +170,7 @@ private:
void readable(qpid::sys::DispatchHandle& handle);
void writeable(qpid::sys::DispatchHandle& handle);
void disconnected(qpid::sys::DispatchHandle& handle);
+ void requestedCall(RequestCallback);
void close(qpid::sys::DispatchHandle& handle);
};
diff --git a/qpid/cpp/src/qpid/sys/unordered_map.h b/qpid/cpp/src/qpid/sys/unordered_map.h
index 5f7f9567c5..7ae71c3daa 100644
--- a/qpid/cpp/src/qpid/sys/unordered_map.h
+++ b/qpid/cpp/src/qpid/sys/unordered_map.h
@@ -23,6 +23,8 @@
#ifdef _MSC_VER
# include <unordered_map>
+#elif defined(__SUNPRO_CC)
+# include <boost/tr1/unordered_map.hpp>
#else
# include <tr1/unordered_map>
#endif /* _MSC_VER */
diff --git a/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp b/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp
index 30378d4c5f..ae53414e52 100644
--- a/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp
+++ b/qpid/cpp/src/qpid/sys/windows/AsynchIO.cpp
@@ -291,6 +291,8 @@ private:
volatile LONG opsInProgress;
// Is there a write in progress?
volatile bool writeInProgress;
+ // Or a read?
+ volatile bool readInProgress;
// Deletion requested, but there are callbacks in progress.
volatile bool queuedDelete;
// Socket close requested, but there are operations in progress.
@@ -344,6 +346,11 @@ private:
* Called when there's a completion to process.
*/
void completion(AsynchIoResult *result);
+
+ /**
+ * Helper function to facilitate the close operation
+ */
+ void cancelRead();
};
// This is used to encapsulate pure callbacks into a handle
@@ -372,6 +379,7 @@ AsynchIO::AsynchIO(const Socket& s,
socket(s),
opsInProgress(0),
writeInProgress(false),
+ readInProgress(false),
queuedDelete(false),
queuedClose(false),
working(false) {
@@ -389,21 +397,24 @@ AsynchIO::~AsynchIO() {
}
void AsynchIO::queueForDeletion() {
- queuedDelete = true;
- if (opsInProgress > 0) {
- QPID_LOG(info, "Delete AsynchIO queued; ops in progress");
- // AsynchIOHandler calls this then deletes itself; don't do any more
- // callbacks.
- readCallback = 0;
- eofCallback = 0;
- disCallback = 0;
- closedCallback = 0;
- emptyCallback = 0;
- idleCallback = 0;
- }
- else {
- delete this;
+ {
+ ScopedLock<Mutex> l(completionLock);
+ assert(!queuedDelete);
+ queuedDelete = true;
+ if (working || opsInProgress > 0) {
+ QPID_LOG(info, "Delete AsynchIO queued; ops in progress");
+ // AsynchIOHandler calls this then deletes itself; don't do any more
+ // callbacks.
+ readCallback = 0;
+ eofCallback = 0;
+ disCallback = 0;
+ closedCallback = 0;
+ emptyCallback = 0;
+ idleCallback = 0;
+ return;
+ }
}
+ delete this;
}
void AsynchIO::start(Poller::shared_ptr poller0) {
@@ -451,9 +462,14 @@ void AsynchIO::notifyPendingWrite() {
}
void AsynchIO::queueWriteClose() {
- queuedClose = true;
- if (!writeInProgress)
- notifyPendingWrite();
+ {
+ ScopedLock<Mutex> l(completionLock);
+ queuedClose = true;
+ if (working || writeInProgress)
+ // no need to summon an IO thread
+ return;
+ }
+ notifyPendingWrite();
}
bool AsynchIO::writeQueueEmpty() {
@@ -466,7 +482,7 @@ bool AsynchIO::writeQueueEmpty() {
* called when the read is complete and data is available.
*/
void AsynchIO::startReading() {
- if (queuedDelete)
+ if (queuedDelete || queuedClose)
return;
// (Try to) get a buffer; look on the front since there may be an
@@ -489,6 +505,7 @@ void AsynchIO::startReading() {
readCount);
DWORD bytesReceived = 0, flags = 0;
InterlockedIncrement(&opsInProgress);
+ readInProgress = true;
int status = WSARecv(toSocketHandle(socket),
const_cast<LPWSABUF>(result->getWSABUF()), 1,
&bytesReceived,
@@ -616,17 +633,19 @@ void AsynchIO::close(void) {
void AsynchIO::readComplete(AsynchReadResult *result) {
int status = result->getStatus();
size_t bytes = result->getTransferred();
+ readInProgress = false;
if (status == 0 && bytes > 0) {
- bool restartRead = true; // May not if receiver doesn't want more
if (readCallback)
readCallback(*this, result->getBuff());
- if (restartRead)
- startReading();
+ startReading();
}
else {
// No data read, so put the buffer back. It may be partially filled,
// so "unread" it back to the front of the queue.
unread(result->getBuff());
+ if (queuedClose) {
+ return; // Expected from cancelRead()
+ }
notifyEof();
if (status != 0)
{
@@ -682,6 +701,8 @@ void AsynchIO::writeComplete(AsynchWriteResult *result) {
}
void AsynchIO::completion(AsynchIoResult *result) {
+ bool closing = false;
+ bool deleting = false;
{
ScopedLock<Mutex> l(completionLock);
if (working) {
@@ -713,6 +734,8 @@ void AsynchIO::completion(AsynchIoResult *result) {
delete result;
result = 0;
InterlockedDecrement(&opsInProgress);
+ if (queuedClose && opsInProgress == 1 && readInProgress)
+ cancelRead();
}
// Lock is held again.
if (completionQueue.empty())
@@ -721,17 +744,40 @@ void AsynchIO::completion(AsynchIoResult *result) {
completionQueue.pop();
}
working = false;
+ if (opsInProgress == 0) {
+ closing = queuedClose;
+ deleting = queuedDelete;
+ }
}
// Lock released; ok to close if ops are done and close requested.
// Layer above will call back to queueForDeletion() if it hasn't
// already been done. If it already has, go ahead and delete.
- if (opsInProgress == 0) {
- if (queuedClose)
- // close() may cause a delete; don't trust 'this' on return
- close();
- else if (queuedDelete)
- delete this;
+ if (deleting)
+ delete this;
+ else if (closing)
+ // close() may cause a delete; don't trust 'this' on return
+ close();
+}
+
+/*
+ * NOTE - this method must be called in the same context as other completions,
+ * so that the resulting readComplete, and final AsynchIO::close() is serialized
+ * after this method returns.
+ */
+void AsynchIO::cancelRead() {
+ if (queuedDelete)
+ return; // socket already deleted
+ else {
+ ScopedLock<Mutex> l(completionLock);;
+ if (!completionQueue.empty())
+ return; // process it; come back later if necessary
}
+ // Cancel outstanding read and force to completion. Otherwise, on a faulty
+ // physical link, the pending read can remain uncompleted indefinitely.
+ // Draining the pending read will result in the official close (and
+ // notifyClosed). CancelIoEX() is the natural choice, but not available in
+ // XP, so we make do with closesocket().
+ socket.close();
}
} // namespace windows
diff --git a/qpid/cpp/src/qpid/sys/windows/IocpPoller.cpp b/qpid/cpp/src/qpid/sys/windows/IocpPoller.cpp
index 1805dd2cd8..c81cef87b0 100755
--- a/qpid/cpp/src/qpid/sys/windows/IocpPoller.cpp
+++ b/qpid/cpp/src/qpid/sys/windows/IocpPoller.cpp
@@ -96,6 +96,7 @@ void Poller::shutdown() {
// Allow sloppy code to shut us down more than once.
if (impl->isShutdown)
return;
+ impl->isShutdown = true;
ULONG_PTR key = 1; // Tell wait() it's a shutdown, not I/O
PostQueuedCompletionStatus(impl->iocp, 0, key, 0);
}
@@ -110,7 +111,7 @@ bool Poller::interrupt(PollerHandle&) {
}
void Poller::run() {
- do {
+ while (!impl->isShutdown) {
Poller::Event event = this->wait();
// Handle shutdown
@@ -124,7 +125,7 @@ void Poller::run() {
// This should be impossible
assert(false);
}
- } while (true);
+ }
}
void Poller::monitorHandle(PollerHandle& handle, Direction dir) {
diff --git a/qpid/cpp/src/qpid/sys/windows/PollableCondition.cpp b/qpid/cpp/src/qpid/sys/windows/PollableCondition.cpp
index 6a1d9045b4..bb637be0a6 100644
--- a/qpid/cpp/src/qpid/sys/windows/PollableCondition.cpp
+++ b/qpid/cpp/src/qpid/sys/windows/PollableCondition.cpp
@@ -1,6 +1,3 @@
-#ifndef QPID_SYS_WINDOWS_POLLABLECONDITION_CPP
-#define QPID_SYS_WINDOWS_POLLABLECONDITION_CPP
-
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -110,5 +107,3 @@ void PollableCondition::clear() {
}
}} // namespace qpid::sys
-
-#endif /*!QPID_SYS_WINDOWS_POLLABLECONDITION_CPP*/
diff --git a/qpid/cpp/src/qpid/sys/windows/Socket.cpp b/qpid/cpp/src/qpid/sys/windows/Socket.cpp
index 1fa4768329..a4374260cc 100644
--- a/qpid/cpp/src/qpid/sys/windows/Socket.cpp
+++ b/qpid/cpp/src/qpid/sys/windows/Socket.cpp
@@ -32,6 +32,9 @@
#include <winsock2.h>
+namespace qpid {
+namespace sys {
+
// Need to initialize WinSock. Ideally, this would be a singleton or embedded
// in some one-time initialization function. I tried boost singleton and could
// not get it to compile (and others located in google had the same problem).
@@ -76,13 +79,6 @@ protected:
static WinSockSetup setup;
-} /* namespace */
-
-namespace qpid {
-namespace sys {
-
-namespace {
-
std::string getName(SOCKET fd, bool local)
{
::sockaddr_storage name_s; // big enough for any socket address
@@ -270,14 +266,17 @@ int Socket::getError() const
void Socket::setTcpNoDelay() const
{
- int flag = 1;
- int result = setsockopt(impl->fd,
- IPPROTO_TCP,
- TCP_NODELAY,
- (char *)&flag,
- sizeof(flag));
- QPID_WINSOCK_CHECK(result);
+ SOCKET& socket = impl->fd;
nodelay = true;
+ if (socket != INVALID_SOCKET) {
+ int flag = 1;
+ int result = setsockopt(impl->fd,
+ IPPROTO_TCP,
+ TCP_NODELAY,
+ (char *)&flag,
+ sizeof(flag));
+ QPID_WINSOCK_CHECK(result);
+ }
}
inline IOHandlePrivate* IOHandlePrivate::getImpl(const qpid::sys::IOHandle &h)
diff --git a/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.cpp b/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.cpp
index 11a3389e45..25cc94b290 100644
--- a/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.cpp
+++ b/qpid/cpp/src/qpid/sys/windows/SslAsynchIO.cpp
@@ -38,6 +38,10 @@
#include <queue>
#include <boost/bind.hpp>
+namespace qpid {
+namespace sys {
+namespace windows {
+
namespace {
/*
@@ -66,10 +70,6 @@ namespace {
};
}
-namespace qpid {
-namespace sys {
-namespace windows {
-
SslAsynchIO::SslAsynchIO(const qpid::sys::Socket& s,
CredHandle hCred,
ReadCallback rCb,
diff --git a/qpid/cpp/src/qpid/sys/windows/SystemInfo.cpp b/qpid/cpp/src/qpid/sys/windows/SystemInfo.cpp
index 4da440bdd4..cef78dcc60 100755
--- a/qpid/cpp/src/qpid/sys/windows/SystemInfo.cpp
+++ b/qpid/cpp/src/qpid/sys/windows/SystemInfo.cpp
@@ -23,9 +23,11 @@
# define _WIN32_WINNT 0x0501
#endif
-#include "qpid/sys/IntegerTypes.h"
#include "qpid/sys/SystemInfo.h"
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/Exception.h"
+#include <assert.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
@@ -93,6 +95,12 @@ void SystemInfo::getLocalIpAddresses (uint16_t port,
}
}
+bool SystemInfo::isLocalHost(const std::string& candidateHost) {
+ // FIXME aconway 2012-05-03: not implemented.
+ assert(0);
+ throw Exception("Not implemented: isLocalHost");
+}
+
void SystemInfo::getSystemId (std::string &osName,
std::string &nodeName,
std::string &release,
diff --git a/qpid/cpp/src/qpid/xml/XmlExchange.cpp b/qpid/cpp/src/qpid/xml/XmlExchange.cpp
index 01770e22a6..3fb11394d0 100644
--- a/qpid/cpp/src/qpid/xml/XmlExchange.cpp
+++ b/qpid/cpp/src/qpid/xml/XmlExchange.cpp
@@ -101,7 +101,7 @@ XmlBinding::XmlBinding(const std::string& key, const Queue::shared_ptr queue, co
}
-XmlExchange::XmlExchange(const string& _name, Manageable* _parent, Broker* b) : Exchange(_name, _parent, b)
+XmlExchange::XmlExchange(const std::string& _name, Manageable* _parent, Broker* b) : Exchange(_name, _parent, b)
{
if (mgmtExchange != 0)
mgmtExchange->set_type (typeName);
@@ -115,7 +115,7 @@ XmlExchange::XmlExchange(const std::string& _name, bool _durable,
mgmtExchange->set_type (typeName);
}
-bool XmlExchange::bind(Queue::shared_ptr queue, const string& bindingKey, const FieldTable* args)
+bool XmlExchange::bind(Queue::shared_ptr queue, const std::string& bindingKey, const FieldTable* args)
{
// Federation uses bind for unbind and reorigin comands as well as for binds.
@@ -123,9 +123,9 @@ bool XmlExchange::bind(Queue::shared_ptr queue, const string& bindingKey, const
// Both federated and local binds are done in this method. Other
// federated requests are done by calling the relevent methods.
- string fedOp;
- string fedTags;
- string fedOrigin;
+ std::string fedOp;
+ std::string fedTags;
+ std::string fedOrigin;
if (args)
fedOp = args->getAsString(qpidFedOp);
@@ -146,7 +146,7 @@ bool XmlExchange::bind(Queue::shared_ptr queue, const string& bindingKey, const
else if (fedOp.empty() || fedOp == fedOpBind) {
- string queryText = args->getAsString("xquery");
+ std::string queryText = args->getAsString("xquery");
RWlock::ScopedWlock l(lock);
@@ -175,7 +175,7 @@ bool XmlExchange::bind(Queue::shared_ptr queue, const string& bindingKey, const
return true;
}
-bool XmlExchange::unbind(Queue::shared_ptr queue, const string& bindingKey, const FieldTable* args)
+bool XmlExchange::unbind(Queue::shared_ptr queue, const std::string& bindingKey, const FieldTable* args)
{
/*
* When called directly, no qpidFedOrigin argument will be
@@ -184,7 +184,7 @@ bool XmlExchange::unbind(Queue::shared_ptr queue, const string& bindingKey, cons
* This is a bit of a hack - the binding needs the origin, but
* this interface, as originally defined, would not supply one.
*/
- string fedOrigin;
+ std::string fedOrigin;
if (args) fedOrigin = args->getAsString(qpidFedOrigin);
RWlock::ScopedWlock l(lock);
@@ -200,7 +200,7 @@ bool XmlExchange::unbind(Queue::shared_ptr queue, const string& bindingKey, cons
bool XmlExchange::matches(Query& query, Deliverable& msg, const qpid::framing::FieldTable* args, bool parse_message_content)
{
- string msgContent;
+ std::string msgContent;
try {
QPID_LOG(trace, "matches: query is [" << UTF8(query->getQueryText()) << "]");
@@ -285,7 +285,7 @@ bool XmlExchange::matches(Query& query, Deliverable& msg, const qpid::framing::F
void XmlExchange::route(Deliverable& msg)
{
- const string& routingKey = msg.getMessage().getRoutingKey();
+ const std::string& routingKey = msg.getMessage().getRoutingKey();
const FieldTable* args = msg.getMessage().getApplicationHeaders();
PreRoute pr(msg, this);
try {
@@ -309,7 +309,7 @@ void XmlExchange::route(Deliverable& msg)
}
-bool XmlExchange::isBound(Queue::shared_ptr queue, const string* const bindingKey, const FieldTable* const)
+bool XmlExchange::isBound(Queue::shared_ptr queue, const std::string* const bindingKey, const FieldTable* const)
{
RWlock::ScopedRlock l(lock);
if (bindingKey) {
@@ -345,7 +345,7 @@ void XmlExchange::propagateFedOp(const std::string& bindingKey, const std::strin
if (args) {
for (qpid::framing::FieldTable::ValueMap::const_iterator i=args->begin(); i != args->end(); ++i) {
- const string& name(i->first);
+ const std::string& name(i->first);
if (name != qpidFedOp &&
name != qpidFedTags &&
name != qpidFedOrigin) {
@@ -358,7 +358,7 @@ void XmlExchange::propagateFedOp(const std::string& bindingKey, const std::strin
Exchange::propagateFedOp(bindingKey, fedTags, fedOp, fedOrigin, propArgs);
}
-bool XmlExchange::fedUnbind(const string& fedOrigin, const string& fedTags, Queue::shared_ptr queue, const string& bindingKey, const FieldTable* args)
+bool XmlExchange::fedUnbind(const std::string& fedOrigin, const std::string& fedTags, Queue::shared_ptr queue, const std::string& bindingKey, const FieldTable* args)
{
RWlock::ScopedRlock l(lock);
@@ -376,19 +376,19 @@ void XmlExchange::fedReorigin()
RWlock::ScopedRlock l(lock);
for (XmlBindingsMap::iterator i = bindingsMap.begin(); i != bindingsMap.end(); ++i) {
XmlBinding::vector::ConstPtr p = i->second.snapshot();
- if (std::find_if(p->begin(), p->end(), MatchOrigin(string())) != p->end()) {
+ if (std::find_if(p->begin(), p->end(), MatchOrigin(std::string())) != p->end()) {
keys2prop.push_back(i->first);
}
}
} /* lock dropped */
for (std::vector<std::string>::const_iterator key = keys2prop.begin();
key != keys2prop.end(); key++) {
- propagateFedOp( *key, string(), fedOpBind, string());
+ propagateFedOp( *key, std::string(), fedOpBind, std::string());
}
}
-XmlExchange::MatchOrigin::MatchOrigin(const string& _origin) : origin(_origin) {}
+XmlExchange::MatchOrigin::MatchOrigin(const std::string& _origin) : origin(_origin) {}
bool XmlExchange::MatchOrigin::operator()(XmlBinding::shared_ptr b)
{
@@ -396,7 +396,7 @@ bool XmlExchange::MatchOrigin::operator()(XmlBinding::shared_ptr b)
}
-XmlExchange::MatchQueueAndOrigin::MatchQueueAndOrigin(Queue::shared_ptr _queue, const string& _origin) : queue(_queue), origin(_origin) {}
+XmlExchange::MatchQueueAndOrigin::MatchQueueAndOrigin(Queue::shared_ptr _queue, const std::string& _origin) : queue(_queue), origin(_origin) {}
bool XmlExchange::MatchQueueAndOrigin::operator()(XmlBinding::shared_ptr b)
{
diff --git a/qpid/cpp/src/qpid/xml/XmlExchange.h b/qpid/cpp/src/qpid/xml/XmlExchange.h
index 9ef389d9bf..1d4723f9c4 100644
--- a/qpid/cpp/src/qpid/xml/XmlExchange.h
+++ b/qpid/cpp/src/qpid/xml/XmlExchange.h
@@ -35,8 +35,6 @@
#include <vector>
#include <string>
-using namespace std;
-
namespace qpid {
namespace broker {
@@ -62,7 +60,7 @@ struct XmlBinding : public Exchange::Binding {
class XmlExchange : public virtual Exchange {
- typedef std::map<string, XmlBinding::vector> XmlBindingsMap;
+ typedef std::map<std::string, XmlBinding::vector> XmlBindingsMap;
XmlBindingsMap bindingsMap;
qpid::sys::RWlock lock;
diff --git a/qpid/cpp/src/qpidd.cpp b/qpid/cpp/src/qpidd.cpp
index a0e329ca9d..b5686c6ab8 100644
--- a/qpid/cpp/src/qpidd.cpp
+++ b/qpid/cpp/src/qpidd.cpp
@@ -29,6 +29,9 @@
#include <memory>
using namespace std;
+namespace qpid {
+namespace broker {
+
auto_ptr<QpiddOptions> options;
// Broker real entry; various system-invoked entrypoints call here.
@@ -87,3 +90,4 @@ int run_broker(int argc, char *argv[], bool hidden)
}
return 1;
}
+}}
diff --git a/qpid/cpp/src/qpidd.h b/qpid/cpp/src/qpidd.h
index a3150a2737..f7f84d11da 100644
--- a/qpid/cpp/src/qpidd.h
+++ b/qpid/cpp/src/qpidd.h
@@ -26,6 +26,9 @@
#include <memory>
+namespace qpid {
+namespace broker {
+
// BootstrapOptions is a minimal subset of options used for a pre-parse
// of the command line to discover which plugin modules need to be loaded.
// The pre-parse is necessary because plugin modules may supply their own
@@ -70,4 +73,5 @@ public:
// Broker real entry; various system-invoked entrypoints call here.
int run_broker(int argc, char *argv[], bool hidden = false);
+}}
#endif /*!QPID_H*/
diff --git a/qpid/cpp/src/tests/Array.cpp b/qpid/cpp/src/tests/Array.cpp
index 7622b89d15..8ce7615162 100644
--- a/qpid/cpp/src/tests/Array.cpp
+++ b/qpid/cpp/src/tests/Array.cpp
@@ -58,7 +58,7 @@ QPID_AUTO_TEST_CASE(testEncodeDecode)
BOOST_CHECK_EQUAL(a, b);
std::vector<std::string> data2;
- b.collect(data2);
+ std::transform(b.begin(), b.end(), std::back_inserter(data2), Array::get<std::string, Array::ValuePtr>);
//BOOST_CHECK_EQUAL(data, data2);
BOOST_CHECK(data == data2);
}
@@ -74,7 +74,7 @@ QPID_AUTO_TEST_CASE(testArrayAssignment)
BOOST_CHECK_EQUAL(a, b);
}
std::vector<std::string> data2;
- b.collect(data2);
+ std::transform(b.begin(), b.end(), std::back_inserter(data2), Array::get<std::string, Array::ValuePtr>);
//BOOST_CHECK_EQUAL(data, data2);
BOOST_CHECK(data == data2);
}
diff --git a/qpid/cpp/src/tests/CMakeLists.txt b/qpid/cpp/src/tests/CMakeLists.txt
index 79c82106cb..e9ad80080c 100644
--- a/qpid/cpp/src/tests/CMakeLists.txt
+++ b/qpid/cpp/src/tests/CMakeLists.txt
@@ -23,7 +23,11 @@ include (CTest)
# Make sure that everything get built before the tests
# Need to create a var with all the necessary top level targets
-add_definitions(-DBOOST_TEST_DYN_LINK)
+# If we're linking Boost for DLLs, turn that on for the unit test too.
+if (QPID_LINK_BOOST_DYNAMIC)
+ add_definitions(-DBOOST_TEST_DYN_LINK)
+endif (QPID_LINK_BOOST_DYNAMIC)
+
include_directories( ${CMAKE_CURRENT_SOURCE_DIR} )
include (FindPythonInterp)
@@ -145,19 +149,13 @@ set(unit_tests_to_build
PollableCondition
Variant
ClientMessage
+ SystemInfo
${xml_tests}
CACHE STRING "Which unit tests to build"
)
mark_as_advanced(unit_tests_to_build)
-# Disabled till we move to amqp_0_10 codec.
-# amqp_0_10/serialize.cpp allSegmentTypes.h \
-# amqp_0_10/ProxyTemplate.cpp \
-# amqp_0_10/apply.cpp \
-# amqp_0_10/Map.cpp \
-# amqp_0_10/handlers.cpp
-
add_executable (unit_test unit_test
${unit_tests_to_build} ${platform_test_additions})
target_link_libraries (unit_test
@@ -326,7 +324,7 @@ if (BUILD_MSSQL)
add_test (store_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_store_tests${test_script_suffix} MSSQL)
endif (BUILD_MSSQL)
if (BUILD_MSCLFS)
- add_test (store_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_store_tests${test_script_suffix} MSSQL-CLFS)
+ add_test (store_tests_clfs ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_store_tests${test_script_suffix} MSSQL-CLFS)
endif (BUILD_MSCLFS)
endif (PYTHON_EXECUTABLE)
diff --git a/qpid/cpp/src/tests/ExchangeTest.cpp b/qpid/cpp/src/tests/ExchangeTest.cpp
index 2fb284741a..66a16b9178 100644
--- a/qpid/cpp/src/tests/ExchangeTest.cpp
+++ b/qpid/cpp/src/tests/ExchangeTest.cpp
@@ -33,6 +33,8 @@
#include <iostream>
#include "MessageUtils.h"
+using std::string;
+
using boost::intrusive_ptr;
using namespace qpid::broker;
using namespace qpid::framing;
diff --git a/qpid/cpp/src/tests/FieldTable.cpp b/qpid/cpp/src/tests/FieldTable.cpp
index fe2a14ec03..8aeeb031b3 100644
--- a/qpid/cpp/src/tests/FieldTable.cpp
+++ b/qpid/cpp/src/tests/FieldTable.cpp
@@ -19,16 +19,19 @@
*
*/
#include <iostream>
+#include <algorithm>
+#include "qpid/sys/alloca.h"
#include "qpid/framing/Array.h"
#include "qpid/framing/FieldTable.h"
#include "qpid/framing/FieldValue.h"
#include "qpid/framing/List.h"
-#include "qpid/sys/alloca.h"
#include "unit_test.h"
using namespace qpid::framing;
+using std::string;
+
namespace qpid {
namespace tests {
@@ -73,11 +76,11 @@ QPID_AUTO_TEST_CASE(testAssignment)
FieldTable c;
c = a;
- char* buff = static_cast<char*>(::alloca(c.encodedSize()));
- Buffer wbuffer(buff, c.encodedSize());
+ std::vector<char> buff(c.encodedSize());
+ Buffer wbuffer(&buff[0], c.encodedSize());
wbuffer.put(c);
- Buffer rbuffer(buff, c.encodedSize());
+ Buffer rbuffer(&buff[0], c.encodedSize());
rbuffer.get(d);
BOOST_CHECK_EQUAL(c, d);
BOOST_CHECK(string("CCCC") == c.getAsString("A"));
@@ -127,7 +130,7 @@ QPID_AUTO_TEST_CASE(testNestedValues)
BOOST_CHECK(string("B") == b.getAsString("id"));
a.getArray("C", c);
std::vector<std::string> items;
- c.collect(items);
+ std::transform(c.begin(), c.end(), std::back_inserter(items), Array::get<std::string, Array::ValuePtr>);
BOOST_CHECK((uint) 2 == items.size());
BOOST_CHECK(string("one") == items[0]);
BOOST_CHECK(string("two") == items[1]);
diff --git a/qpid/cpp/src/tests/Frame.cpp b/qpid/cpp/src/tests/Frame.cpp
index 1270eabba3..cfcfde04a7 100644
--- a/qpid/cpp/src/tests/Frame.cpp
+++ b/qpid/cpp/src/tests/Frame.cpp
@@ -30,7 +30,6 @@ QPID_AUTO_TEST_SUITE(FrameTestSuite)
using namespace std;
using namespace qpid::framing;
-using namespace boost;
QPID_AUTO_TEST_CASE(testContentBody) {
Frame f(42, AMQContentBody("foobar"));
diff --git a/qpid/cpp/src/tests/FramingTest.cpp b/qpid/cpp/src/tests/FramingTest.cpp
index f8795316cc..2392b6fec4 100644
--- a/qpid/cpp/src/tests/FramingTest.cpp
+++ b/qpid/cpp/src/tests/FramingTest.cpp
@@ -25,6 +25,7 @@
#include "qpid/framing/all_method_bodies.h"
#include "qpid/framing/amqp_framing.h"
#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/FieldValue.h"
#include "unit_test.h"
#include <boost/bind.hpp>
diff --git a/qpid/cpp/src/tests/Makefile.am b/qpid/cpp/src/tests/Makefile.am
index a7f3bc1fbd..cdc7429f3b 100644
--- a/qpid/cpp/src/tests/Makefile.am
+++ b/qpid/cpp/src/tests/Makefile.am
@@ -67,7 +67,7 @@ tmodule_LTLIBRARIES=
TESTS+=unit_test
check_PROGRAMS+=unit_test
-unit_test_LDADD=-lboost_unit_test_framework \
+unit_test_LDADD=-lboost_unit_test_framework -lpthread \
$(lib_messaging) $(lib_broker) $(lib_console) $(lib_qmf2)
unit_test_SOURCES= unit_test.cpp unit_test.h \
@@ -124,7 +124,8 @@ unit_test_SOURCES= unit_test.cpp unit_test.h \
Address.cpp \
ClientMessage.cpp \
Qmf2.cpp \
- BrokerOptions.cpp
+ BrokerOptions.cpp \
+ SystemInfo.cpp
if HAVE_XML
unit_test_SOURCES+= XmlClientSessionTest.cpp
@@ -149,8 +150,8 @@ endif
# Test programs that are installed and therefore built as part of make, not make check
-qpidexectest_SCRIPTS += qpid-cpp-benchmark install_env.sh
-EXTRA_DIST += qpid-cpp-benchmark install_env.sh
+qpidexectest_SCRIPTS += qpid-cpp-benchmark qpid-cluster-benchmark install_env.sh
+EXTRA_DIST += qpid-cpp-benchmark qpid-cluster-benchmark install_env.sh
qpidexectest_PROGRAMS += receiver
receiver_SOURCES = \
@@ -305,7 +306,7 @@ TESTS_ENVIRONMENT = \
system_tests = qpid-client-test quick_perftest quick_topictest run_header_test quick_txtest \
run_msg_group_tests
TESTS += start_broker $(system_tests) python_tests stop_broker \
- ha_tests.py run_federation_tests run_federation_sys_tests \
+ run_ha_tests run_federation_tests run_federation_sys_tests \
run_acl_tests run_cli_tests replication_test dynamic_log_level_test \
run_queue_flow_limit_tests ipv6_test
@@ -320,7 +321,7 @@ EXTRA_DIST += \
header_test.py \
ssl_test \
config.null \
- ais_check \
+ cpg_check.sh.in \
run_federation_tests \
run_federation_sys_tests \
run_long_federation_sys_tests \
@@ -352,7 +353,9 @@ EXTRA_DIST += \
run_queue_flow_limit_tests \
run_msg_group_tests \
ipv6_test \
- ha_tests.py
+ run_ha_tests \
+ ha_tests.py \
+ test_env.ps1.in
check_LTLIBRARIES += libdlclose_noop.la
libdlclose_noop_la_LDFLAGS = -module -rpath $(abs_builddir)
@@ -367,9 +370,15 @@ LONG_TESTS+=start_broker \
fanout_perftest shared_perftest multiq_perftest topic_perftest run_ring_queue_test \
run_msg_group_tests_soak \
stop_broker \
- run_long_federation_sys_tests \
- run_failover_soak reliable_replication_test \
- federated_cluster_test_with_node_failure
+ run_long_federation_sys_tests
+
+if HAVE_LIBCPG
+
+LONG_TESTS+= federated_cluster_test_with_node_failure \
+ run_failover_soak \
+ reliable_replication_test
+
+endif HAVE_LIBCPG
EXTRA_DIST+= \
fanout_perftest \
@@ -380,7 +389,8 @@ EXTRA_DIST+= \
reliable_replication_test \
federated_cluster_test_with_node_failure \
sasl_test_setup.sh \
- run_msg_group_tests_soak
+ run_msg_group_tests_soak \
+ qpidd-empty.conf
check-long:
$(MAKE) check TESTS="$(LONG_TESTS)" VALGRIND=
diff --git a/qpid/cpp/src/tests/MessageBuilderTest.cpp b/qpid/cpp/src/tests/MessageBuilderTest.cpp
index c3d40ed88a..9adb133d40 100644
--- a/qpid/cpp/src/tests/MessageBuilderTest.cpp
+++ b/qpid/cpp/src/tests/MessageBuilderTest.cpp
@@ -40,7 +40,7 @@ class MockMessageStore : public NullMessageStore
uint64_t id;
boost::intrusive_ptr<PersistableMessage> expectedMsg;
- string expectedData;
+ std::string expectedData;
std::list<Op> ops;
void checkExpectation(Op actual)
@@ -58,7 +58,7 @@ class MockMessageStore : public NullMessageStore
ops.push_back(STAGE);
}
- void expectAppendContent(PersistableMessage& msg, const string& data)
+ void expectAppendContent(PersistableMessage& msg, const std::string& data)
{
expectedMsg = &msg;
expectedData = data;
@@ -73,7 +73,7 @@ class MockMessageStore : public NullMessageStore
}
void appendContent(const boost::intrusive_ptr<const PersistableMessage>& msg,
- const string& data)
+ const std::string& data)
{
checkExpectation(APPEND);
BOOST_CHECK_EQUAL(boost::static_pointer_cast<const PersistableMessage>(expectedMsg), msg);
diff --git a/qpid/cpp/src/tests/MessageTest.cpp b/qpid/cpp/src/tests/MessageTest.cpp
index 7d67c92b37..3a3ed061f9 100644
--- a/qpid/cpp/src/tests/MessageTest.cpp
+++ b/qpid/cpp/src/tests/MessageTest.cpp
@@ -24,7 +24,6 @@
#include "qpid/framing/MessageTransferBody.h"
#include "qpid/framing/FieldValue.h"
#include "qpid/framing/Uuid.h"
-#include "qpid/sys/alloca.h"
#include "unit_test.h"
@@ -33,6 +32,8 @@
using namespace qpid::broker;
using namespace qpid::framing;
+using std::string;
+
namespace qpid {
namespace tests {
@@ -69,11 +70,11 @@ QPID_AUTO_TEST_CASE(testEncodeDecode)
dProps->setDeliveryMode(PERSISTENT);
BOOST_CHECK(msg->isPersistent());
- char* buff = static_cast<char*>(::alloca(msg->encodedSize()));
- Buffer wbuffer(buff, msg->encodedSize());
+ std::vector<char> buff(msg->encodedSize());
+ Buffer wbuffer(&buff[0], msg->encodedSize());
msg->encode(wbuffer);
- Buffer rbuffer(buff, msg->encodedSize());
+ Buffer rbuffer(&buff[0], msg->encodedSize());
msg = new Message();
msg->decodeHeader(rbuffer);
msg->decodeContent(rbuffer);
diff --git a/qpid/cpp/src/tests/MessageUtils.h b/qpid/cpp/src/tests/MessageUtils.h
index a1b140d484..991e2a2714 100644
--- a/qpid/cpp/src/tests/MessageUtils.h
+++ b/qpid/cpp/src/tests/MessageUtils.h
@@ -33,7 +33,7 @@ namespace tests {
struct MessageUtils
{
- static boost::intrusive_ptr<Message> createMessage(const string& exchange="", const string& routingKey="",
+ static boost::intrusive_ptr<Message> createMessage(const std::string& exchange="", const std::string& routingKey="",
const bool durable = false, const Uuid& messageId=Uuid(true),
uint64_t contentSize = 0)
{
@@ -53,7 +53,7 @@ struct MessageUtils
return msg;
}
- static void addContent(boost::intrusive_ptr<Message> msg, const string& data)
+ static void addContent(boost::intrusive_ptr<Message> msg, const std::string& data)
{
AMQFrame content((AMQContentBody(data)));
msg->getFrames().append(content);
diff --git a/qpid/cpp/src/tests/MessagingSessionTests.cpp b/qpid/cpp/src/tests/MessagingSessionTests.cpp
index 313e8b3132..968d55fd45 100644
--- a/qpid/cpp/src/tests/MessagingSessionTests.cpp
+++ b/qpid/cpp/src/tests/MessagingSessionTests.cpp
@@ -1139,6 +1139,13 @@ QPID_AUTO_TEST_CASE(testHeadersExchange)
}
}
+QPID_AUTO_TEST_CASE(testLargeRoutingKey)
+{
+ MessagingFixture fix;
+ std::string address = "amq.direct/" + std::string(300, 'x');//routing/binding key can be at most 225 chars in 0-10
+ BOOST_CHECK_THROW(fix.session.createReceiver(address), qpid::messaging::MessagingException);
+}
+
QPID_AUTO_TEST_SUITE_END()
}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/QueueFlowLimitTest.cpp b/qpid/cpp/src/tests/QueueFlowLimitTest.cpp
index 8a6923fb09..bd868398f8 100644
--- a/qpid/cpp/src/tests/QueueFlowLimitTest.cpp
+++ b/qpid/cpp/src/tests/QueueFlowLimitTest.cpp
@@ -27,6 +27,7 @@
#include "qpid/broker/QueueFlowLimit.h"
#include "qpid/sys/Time.h"
#include "qpid/framing/reply_exceptions.h"
+#include "qpid/framing/FieldValue.h"
#include "MessageUtils.h"
#include "BrokerFixture.h"
diff --git a/qpid/cpp/src/tests/QueueTest.cpp b/qpid/cpp/src/tests/QueueTest.cpp
index 0058aa5133..aa7132828a 100644
--- a/qpid/cpp/src/tests/QueueTest.cpp
+++ b/qpid/cpp/src/tests/QueueTest.cpp
@@ -31,6 +31,8 @@
#include "qpid/broker/QueueRegistry.h"
#include "qpid/broker/NullMessageStore.h"
#include "qpid/broker/ExpiryPolicy.h"
+#include "qpid/framing/DeliveryProperties.h"
+#include "qpid/framing/FieldTable.h"
#include "qpid/framing/MessageTransferBody.h"
#include "qpid/client/QueueOptions.h"
#include "qpid/framing/AMQFrame.h"
@@ -40,8 +42,11 @@
#include "qpid/broker/QueueFlowLimit.h"
#include <iostream>
-#include "boost/format.hpp"
+#include <vector>
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+using namespace std;
using boost::intrusive_ptr;
using namespace qpid;
using namespace qpid::broker;
@@ -83,7 +88,7 @@ public:
Message& getMessage() { return *(msg.get()); }
};
-intrusive_ptr<Message> create_message(std::string exchange, std::string routingKey, uint64_t ttl = 0) {
+intrusive_ptr<Message> createMessage(std::string exchange, std::string routingKey, uint64_t ttl = 0) {
intrusive_ptr<Message> msg(new Message());
AMQFrame method((MessageTransferBody(ProtocolVersion(), exchange, 0, 0)));
AMQFrame header((AMQHeaderBody()));
@@ -94,6 +99,16 @@ intrusive_ptr<Message> create_message(std::string exchange, std::string routingK
return msg;
}
+intrusive_ptr<Message> contentMessage(string content) {
+ intrusive_ptr<Message> m(MessageUtils::createMessage());
+ MessageUtils::addContent(m, content);
+ return m;
+}
+
+string getContent(intrusive_ptr<Message> m) {
+ return m->getFrames().getContent();
+}
+
QPID_AUTO_TEST_SUITE(QueueTestSuite)
QPID_AUTO_TEST_CASE(testAsyncMessage) {
@@ -105,7 +120,7 @@ QPID_AUTO_TEST_CASE(testAsyncMessage) {
//Test basic delivery:
- intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg1 = createMessage("e", "A");
msg1->enqueueAsync(queue, 0);//this is done on enqueue which is not called from process
queue->process(msg1);
sleep(2);
@@ -120,7 +135,7 @@ QPID_AUTO_TEST_CASE(testAsyncMessage) {
QPID_AUTO_TEST_CASE(testAsyncMessageCount){
Queue::shared_ptr queue(new Queue("my_test_queue", true));
- intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg1 = createMessage("e", "A");
msg1->enqueueAsync(queue, 0);//this is done on enqueue which is not called from process
queue->process(msg1);
@@ -145,9 +160,9 @@ QPID_AUTO_TEST_CASE(testConsumers){
BOOST_CHECK_EQUAL(uint32_t(2), queue->getConsumerCount());
//Test basic delivery:
- intrusive_ptr<Message> msg1 = create_message("e", "A");
- intrusive_ptr<Message> msg2 = create_message("e", "B");
- intrusive_ptr<Message> msg3 = create_message("e", "C");
+ intrusive_ptr<Message> msg1 = createMessage("e", "A");
+ intrusive_ptr<Message> msg2 = createMessage("e", "B");
+ intrusive_ptr<Message> msg3 = createMessage("e", "C");
queue->deliver(msg1);
BOOST_CHECK(queue->dispatch(c1));
@@ -191,9 +206,9 @@ QPID_AUTO_TEST_CASE(testRegistry){
QPID_AUTO_TEST_CASE(testDequeue){
Queue::shared_ptr queue(new Queue("my_queue", true));
- intrusive_ptr<Message> msg1 = create_message("e", "A");
- intrusive_ptr<Message> msg2 = create_message("e", "B");
- intrusive_ptr<Message> msg3 = create_message("e", "C");
+ intrusive_ptr<Message> msg1 = createMessage("e", "A");
+ intrusive_ptr<Message> msg2 = createMessage("e", "B");
+ intrusive_ptr<Message> msg3 = createMessage("e", "C");
intrusive_ptr<Message> received;
queue->deliver(msg1);
@@ -265,9 +280,9 @@ QPID_AUTO_TEST_CASE(testPersistLastNodeStanding){
Queue::shared_ptr queue(new Queue("my-queue", true));
queue->configure(args);
- intrusive_ptr<Message> msg1 = create_message("e", "A");
- intrusive_ptr<Message> msg2 = create_message("e", "B");
- intrusive_ptr<Message> msg3 = create_message("e", "C");
+ intrusive_ptr<Message> msg1 = createMessage("e", "A");
+ intrusive_ptr<Message> msg2 = createMessage("e", "B");
+ intrusive_ptr<Message> msg3 = createMessage("e", "C");
//enqueue 2 messages
queue->deliver(msg1);
@@ -291,9 +306,9 @@ QPID_AUTO_TEST_CASE(testSeek){
Queue::shared_ptr queue(new Queue("my-queue", true));
- intrusive_ptr<Message> msg1 = create_message("e", "A");
- intrusive_ptr<Message> msg2 = create_message("e", "B");
- intrusive_ptr<Message> msg3 = create_message("e", "C");
+ intrusive_ptr<Message> msg1 = createMessage("e", "A");
+ intrusive_ptr<Message> msg2 = createMessage("e", "B");
+ intrusive_ptr<Message> msg3 = createMessage("e", "C");
//enqueue 2 messages
queue->deliver(msg1);
@@ -317,9 +332,9 @@ QPID_AUTO_TEST_CASE(testSearch){
Queue::shared_ptr queue(new Queue("my-queue", true));
- intrusive_ptr<Message> msg1 = create_message("e", "A");
- intrusive_ptr<Message> msg2 = create_message("e", "B");
- intrusive_ptr<Message> msg3 = create_message("e", "C");
+ intrusive_ptr<Message> msg1 = createMessage("e", "A");
+ intrusive_ptr<Message> msg2 = createMessage("e", "B");
+ intrusive_ptr<Message> msg3 = createMessage("e", "C");
//enqueue 2 messages
queue->deliver(msg1);
@@ -431,10 +446,10 @@ QPID_AUTO_TEST_CASE(testLVQOrdering){
Queue::shared_ptr queue(new Queue("my-queue", true ));
queue->configure(args);
- intrusive_ptr<Message> msg1 = create_message("e", "A");
- intrusive_ptr<Message> msg2 = create_message("e", "B");
- intrusive_ptr<Message> msg3 = create_message("e", "C");
- intrusive_ptr<Message> msg4 = create_message("e", "D");
+ intrusive_ptr<Message> msg1 = createMessage("e", "A");
+ intrusive_ptr<Message> msg2 = createMessage("e", "B");
+ intrusive_ptr<Message> msg3 = createMessage("e", "C");
+ intrusive_ptr<Message> msg4 = createMessage("e", "D");
intrusive_ptr<Message> received;
//set deliever match for LVQ a,b,c,a
@@ -466,9 +481,9 @@ QPID_AUTO_TEST_CASE(testLVQOrdering){
received = queue->get().payload;
BOOST_CHECK_EQUAL(msg3.get(), received.get());
- intrusive_ptr<Message> msg5 = create_message("e", "A");
- intrusive_ptr<Message> msg6 = create_message("e", "B");
- intrusive_ptr<Message> msg7 = create_message("e", "C");
+ intrusive_ptr<Message> msg5 = createMessage("e", "A");
+ intrusive_ptr<Message> msg6 = createMessage("e", "B");
+ intrusive_ptr<Message> msg7 = createMessage("e", "C");
msg5->insertCustomProperty(key,"a");
msg6->insertCustomProperty(key,"b");
msg7->insertCustomProperty(key,"c");
@@ -498,8 +513,8 @@ QPID_AUTO_TEST_CASE(testLVQEmptyKey){
Queue::shared_ptr queue(new Queue("my-queue", true ));
queue->configure(args);
- intrusive_ptr<Message> msg1 = create_message("e", "A");
- intrusive_ptr<Message> msg2 = create_message("e", "B");
+ intrusive_ptr<Message> msg1 = createMessage("e", "A");
+ intrusive_ptr<Message> msg2 = createMessage("e", "B");
string key;
args.getLVQKey(key);
@@ -524,12 +539,12 @@ QPID_AUTO_TEST_CASE(testLVQAcquire){
Queue::shared_ptr queue(new Queue("my-queue", true ));
queue->configure(args);
- intrusive_ptr<Message> msg1 = create_message("e", "A");
- intrusive_ptr<Message> msg2 = create_message("e", "B");
- intrusive_ptr<Message> msg3 = create_message("e", "C");
- intrusive_ptr<Message> msg4 = create_message("e", "D");
- intrusive_ptr<Message> msg5 = create_message("e", "F");
- intrusive_ptr<Message> msg6 = create_message("e", "G");
+ intrusive_ptr<Message> msg1 = createMessage("e", "A");
+ intrusive_ptr<Message> msg2 = createMessage("e", "B");
+ intrusive_ptr<Message> msg3 = createMessage("e", "C");
+ intrusive_ptr<Message> msg4 = createMessage("e", "D");
+ intrusive_ptr<Message> msg5 = createMessage("e", "F");
+ intrusive_ptr<Message> msg6 = createMessage("e", "G");
//set deliever match for LVQ a,b,c,a
@@ -601,8 +616,8 @@ QPID_AUTO_TEST_CASE(testLVQMultiQueue){
queue1->configure(args);
queue2->configure(args);
- intrusive_ptr<Message> msg1 = create_message("e", "A");
- intrusive_ptr<Message> msg2 = create_message("e", "A");
+ intrusive_ptr<Message> msg1 = createMessage("e", "A");
+ intrusive_ptr<Message> msg2 = createMessage("e", "A");
string key;
args.getLVQKey(key);
@@ -645,8 +660,8 @@ QPID_AUTO_TEST_CASE(testLVQRecover){
intrusive_ptr<Message> received;
queue1->create(args);
- intrusive_ptr<Message> msg1 = create_message("e", "A");
- intrusive_ptr<Message> msg2 = create_message("e", "A");
+ intrusive_ptr<Message> msg1 = createMessage("e", "A");
+ intrusive_ptr<Message> msg2 = createMessage("e", "A");
// 2
string key;
args.getLVQKey(key);
@@ -673,7 +688,7 @@ QPID_AUTO_TEST_CASE(testLVQRecover){
void addMessagesToQueue(uint count, Queue& queue, uint oddTtl = 200, uint evenTtl = 0)
{
for (uint i = 0; i < count; i++) {
- intrusive_ptr<Message> m = create_message("exchange", "key", i % 2 ? oddTtl : evenTtl);
+ intrusive_ptr<Message> m = createMessage("exchange", "key", i % 2 ? oddTtl : evenTtl);
m->computeExpiration(new broker::ExpiryPolicy);
queue.deliver(m);
}
@@ -736,7 +751,7 @@ QPID_AUTO_TEST_CASE(testGroupsMultiConsumer) {
std::string("b"), std::string("b"), std::string("b"),
std::string("c"), std::string("c"), std::string("c") };
for (int i = 0; i < 9; ++i) {
- intrusive_ptr<Message> msg = create_message("e", "A");
+ intrusive_ptr<Message> msg = createMessage("e", "A");
msg->insertCustomProperty("GROUP-ID", groups[i]);
msg->insertCustomProperty("MY-ID", i);
queue->deliver(msg);
@@ -883,7 +898,7 @@ QPID_AUTO_TEST_CASE(testGroupsMultiConsumer) {
// Queue = a-2,
// Owners= ^C3,
- intrusive_ptr<Message> msg = create_message("e", "A");
+ intrusive_ptr<Message> msg = createMessage("e", "A");
msg->insertCustomProperty("GROUP-ID", "a");
msg->insertCustomProperty("MY-ID", 9);
queue->deliver(msg);
@@ -894,7 +909,7 @@ QPID_AUTO_TEST_CASE(testGroupsMultiConsumer) {
gotOne = queue->dispatch(c2);
BOOST_CHECK( !gotOne );
- msg = create_message("e", "A");
+ msg = createMessage("e", "A");
msg->insertCustomProperty("GROUP-ID", "b");
msg->insertCustomProperty("MY-ID", 10);
queue->deliver(msg);
@@ -925,7 +940,7 @@ QPID_AUTO_TEST_CASE(testGroupsMultiConsumerDefaults) {
queue->configure(args);
for (int i = 0; i < 3; ++i) {
- intrusive_ptr<Message> msg = create_message("e", "A");
+ intrusive_ptr<Message> msg = createMessage("e", "A");
// no "GROUP-ID" header
msg->insertCustomProperty("MY-ID", i);
queue->deliver(msg);
@@ -988,7 +1003,7 @@ QPID_AUTO_TEST_CASE(testMultiQueueLastNode){
Queue::shared_ptr queue2(new Queue("queue2", true, &testStore ));
queue2->create(args);
- intrusive_ptr<Message> msg1 = create_message("e", "A");
+ intrusive_ptr<Message> msg1 = createMessage("e", "A");
queue1->deliver(msg1);
queue2->deliver(msg1);
@@ -1004,7 +1019,7 @@ QPID_AUTO_TEST_CASE(testMultiQueueLastNode){
queue2->setLastNodeFailure();
BOOST_CHECK_EQUAL(testStore.enqCnt, 2u);
- intrusive_ptr<Message> msg2 = create_message("e", "B");
+ intrusive_ptr<Message> msg2 = createMessage("e", "B");
queue1->deliver(msg2);
queue2->deliver(msg2);
@@ -1019,7 +1034,7 @@ QPID_AUTO_TEST_CASE(testMultiQueueLastNode){
queue1->clearLastNodeFailure();
queue2->clearLastNodeFailure();
- intrusive_ptr<Message> msg3 = create_message("e", "B");
+ intrusive_ptr<Message> msg3 = createMessage("e", "B");
queue1->deliver(msg3);
queue2->deliver(msg3);
BOOST_CHECK_EQUAL(testStore.enqCnt, 4u);
@@ -1033,8 +1048,8 @@ QPID_AUTO_TEST_CASE(testMultiQueueLastNode){
* internal details not part of the queue abstraction.
// check requeue 1
- intrusive_ptr<Message> msg4 = create_message("e", "C");
- intrusive_ptr<Message> msg5 = create_message("e", "D");
+ intrusive_ptr<Message> msg4 = createMessage("e", "C");
+ intrusive_ptr<Message> msg5 = createMessage("e", "D");
framing::SequenceNumber sequence(1);
QueuedMessage qmsg1(queue1.get(), msg4, sequence);
@@ -1081,8 +1096,8 @@ not requeued to the store.
queue1->create(args);
// check requeue 1
- intrusive_ptr<Message> msg1 = create_message("e", "C");
- intrusive_ptr<Message> msg2 = create_message("e", "D");
+ intrusive_ptr<Message> msg1 = createMessage("e", "C");
+ intrusive_ptr<Message> msg2 = createMessage("e", "D");
queue1->recover(msg1);
@@ -1114,7 +1129,7 @@ simulate store exception going into last node standing
queue1->configure(args);
// check requeue 1
- intrusive_ptr<Message> msg1 = create_message("e", "C");
+ intrusive_ptr<Message> msg1 = createMessage("e", "C");
queue1->deliver(msg1);
testStore.createError();
@@ -1401,6 +1416,133 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){
BOOST_CHECK_EQUAL(5u, tq9->getMessageCount());
}
+QPID_AUTO_TEST_CASE(testSetPositionFifo) {
+ Queue::shared_ptr q(new Queue("my-queue", true));
+ BOOST_CHECK_EQUAL(q->getPosition(), SequenceNumber(0));
+ for (int i = 0; i < 10; ++i)
+ q->deliver(contentMessage(boost::lexical_cast<string>(i+1)));
+
+ // Verify the front of the queue
+ TestConsumer::shared_ptr c(new TestConsumer("test", false)); // Don't acquire
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(1u, c->last.position); // Numbered from 1
+ BOOST_CHECK_EQUAL("1", getContent(c->last.payload));
+ // Verify the back of the queue
+ QueuedMessage qm;
+ BOOST_CHECK_EQUAL(10u, q->getPosition());
+ BOOST_CHECK(q->find(q->getPosition(), qm)); // Back of the queue
+ BOOST_CHECK_EQUAL("10", getContent(qm.payload));
+ BOOST_CHECK_EQUAL(10u, q->getMessageCount());
+
+ // Using setPosition to introduce a gap in sequence numbers.
+ q->setPosition(15);
+ BOOST_CHECK_EQUAL(10u, q->getMessageCount());
+ BOOST_CHECK_EQUAL(15u, q->getPosition());
+ BOOST_CHECK(q->find(10, qm)); // Back of the queue
+ BOOST_CHECK_EQUAL("10", getContent(qm.payload));
+ q->deliver(contentMessage("16"));
+ c->setPosition(9);
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(10u, c->last.position);
+ BOOST_CHECK_EQUAL("10", getContent(c->last.payload));
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(16u, c->last.position);
+ BOOST_CHECK_EQUAL("16", getContent(c->last.payload));
+
+ // Using setPosition to trunkcate the queue
+ q->setPosition(5);
+ BOOST_CHECK_EQUAL(5u, q->getMessageCount());
+ q->deliver(contentMessage("6a"));
+ c->setPosition(4);
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(5u, c->last.position);
+ BOOST_CHECK_EQUAL("5", getContent(c->last.payload));
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(6u, c->last.position);
+ BOOST_CHECK_EQUAL("6a", getContent(c->last.payload));
+ BOOST_CHECK(!q->dispatch(c)); // No more messages.
+}
+
+QPID_AUTO_TEST_CASE(testSetPositionLvq) {
+ Queue::shared_ptr q(new Queue("my-queue", true));
+ string key="key";
+ framing::FieldTable args;
+ args.setString("qpid.last_value_queue_key", "key");
+ q->configure(args);
+
+ const char* values[] = { "a", "b", "c", "a", "b", "c" };
+ for (size_t i = 0; i < sizeof(values)/sizeof(values[0]); ++i) {
+ intrusive_ptr<Message> m = contentMessage(boost::lexical_cast<string>(i+1));
+ m->insertCustomProperty(key, values[i]);
+ q->deliver(m);
+ }
+ BOOST_CHECK_EQUAL(3u, q->getMessageCount());
+ // Verify the front of the queue
+ TestConsumer::shared_ptr c(new TestConsumer("test", false)); // Don't acquire
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(4u, c->last.position); // Numbered from 1
+ BOOST_CHECK_EQUAL("4", getContent(c->last.payload));
+ // Verify the back of the queue
+ QueuedMessage qm;
+ BOOST_CHECK_EQUAL(6u, q->getPosition());
+ BOOST_CHECK(q->find(q->getPosition(), qm)); // Back of the queue
+ BOOST_CHECK_EQUAL("6", getContent(qm.payload));
+
+ q->setPosition(5);
+ c->setPosition(4);
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(5u, c->last.position); // Numbered from 1
+ BOOST_CHECK(!q->dispatch(c));
+}
+
+QPID_AUTO_TEST_CASE(testSetPositionPriority) {
+ Queue::shared_ptr q(new Queue("my-queue", true));
+ framing::FieldTable args;
+ args.setInt("qpid.priorities", 10);
+ q->configure(args);
+
+ const int priorities[] = { 1, 2, 3, 2, 1, 3 };
+ for (size_t i = 0; i < sizeof(priorities)/sizeof(priorities[0]); ++i) {
+ intrusive_ptr<Message> m = contentMessage(boost::lexical_cast<string>(i+1));
+ m->getFrames().getHeaders()->get<DeliveryProperties>(true)
+ ->setPriority(priorities[i]);
+ q->deliver(m);
+ }
+
+ // Truncation removes messages in fifo order, not priority order.
+ q->setPosition(3);
+ TestConsumer::shared_ptr c(new TestConsumer("test", false)); // Browse in FIFO order
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(1u, c->last.position);
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(2u, c->last.position);
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(3u, c->last.position);
+ BOOST_CHECK(!q->dispatch(c));
+
+ intrusive_ptr<Message> m = contentMessage("4a");
+ m->getFrames().getHeaders()->get<DeliveryProperties>(true)
+ ->setPriority(4);
+ q->deliver(m);
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(4u, c->last.position);
+ BOOST_CHECK_EQUAL("4a", getContent(c->last.payload));
+
+ // But consumers see priority order
+ c.reset(new TestConsumer("test", true));
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(4u, c->last.position);
+ BOOST_CHECK_EQUAL("4a", getContent(c->last.payload));
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(3u, c->last.position);
+ BOOST_CHECK_EQUAL("3", getContent(c->last.payload));
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(2u, c->last.position);
+ BOOST_CHECK_EQUAL("2", getContent(c->last.payload));
+ BOOST_CHECK(q->dispatch(c));
+ BOOST_CHECK_EQUAL(1u, c->last.position);
+ BOOST_CHECK_EQUAL("1", getContent(c->last.payload));
+}
QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/RangeSet.cpp b/qpid/cpp/src/tests/RangeSet.cpp
index db3a964086..285f432bf7 100644
--- a/qpid/cpp/src/tests/RangeSet.cpp
+++ b/qpid/cpp/src/tests/RangeSet.cpp
@@ -29,63 +29,71 @@ namespace tests {
QPID_AUTO_TEST_SUITE(RangeSetTestSuite)
-typedef qpid::Range<int> TestRange;
-typedef qpid::RangeSet<int> TestRangeSet;
+typedef qpid::Range<int> TR; // Test Range
+typedef RangeSet<int> TRSet;
QPID_AUTO_TEST_CASE(testEmptyRange) {
- TestRange r;
+ TR r;
+ BOOST_CHECK_EQUAL(r, TR(0,0));
BOOST_CHECK(r.empty());
BOOST_CHECK(!r.contains(0));
- // BOOST_CHECK(r.contiguous(0));
}
QPID_AUTO_TEST_CASE(testRangeSetAddPoint) {
- TestRangeSet r;
+ TRSet r;
BOOST_CHECK(r.empty());
r += 3;
BOOST_CHECK_MESSAGE(r.contains(3), r);
- BOOST_CHECK_MESSAGE(r.contains(TestRange(3,4)), r);
+ BOOST_CHECK_MESSAGE(r.contains(TR(3,4)), r);
BOOST_CHECK(!r.empty());
r += 5;
BOOST_CHECK_MESSAGE(r.contains(5), r);
- BOOST_CHECK_MESSAGE(r.contains(TestRange(5,6)), r);
- BOOST_CHECK_MESSAGE(!r.contains(TestRange(3,6)), r);
+ BOOST_CHECK_MESSAGE(r.contains(TR(5,6)), r);
+ BOOST_CHECK_MESSAGE(!r.contains(TR(3,6)), r);
r += 4;
- BOOST_CHECK_MESSAGE(r.contains(TestRange(3,6)), r);
+ BOOST_CHECK_MESSAGE(r.contains(TR(3,6)), r);
}
QPID_AUTO_TEST_CASE(testRangeSetAddRange) {
- TestRangeSet r;
- r += TestRange(0,3);
- BOOST_CHECK(r.contains(TestRange(0,3)));
- r += TestRange(4,6);
- BOOST_CHECK_MESSAGE(r.contains(TestRange(4,6)), r);
+ TRSet r;
+ r += TR(0,3);
+ BOOST_CHECK(r.contains(TR(0,3)));
+ BOOST_CHECK(r.contiguous());
+ r += TR(4,6);
+ BOOST_CHECK(!r.contiguous());
+ BOOST_CHECK_MESSAGE(r.contains(TR(4,6)), r);
r += 3;
- BOOST_CHECK_MESSAGE(r.contains(TestRange(0,6)), r);
+ BOOST_CHECK_MESSAGE(r.contains(TR(0,6)), r);
BOOST_CHECK(r.front() == 0);
BOOST_CHECK(r.back() == 6);
+
+ // Merging additions
+ r = TRSet(0,3)+TR(5,6);
+ TRSet e(0,6);
+ BOOST_CHECK_EQUAL(r + TR(3,5), e);
+ BOOST_CHECK(e.contiguous());
+ r = TRSet(0,5)+TR(10,15)+TR(20,25)+TR(30,35)+TR(40,45);
+ BOOST_CHECK_EQUAL(r + TR(11,37), TRSet(0,5)+TR(11,37)+TR(40,45));
}
QPID_AUTO_TEST_CASE(testRangeSetAddSet) {
- TestRangeSet r;
- TestRangeSet s = TestRangeSet(0,3)+TestRange(5,10);
+ TRSet r;
+ TRSet s = TRSet(0,3)+TR(5,10);
r += s;
BOOST_CHECK_EQUAL(r,s);
- r += TestRangeSet(3,5) + TestRange(7,12) + 15;
- BOOST_CHECK_EQUAL(r, TestRangeSet(0,12) + 15);
+ r += TRSet(3,5) + TR(7,12) + 15;
+ BOOST_CHECK_EQUAL(r, TRSet(0,12) + 15);
r.clear();
BOOST_CHECK(r.empty());
- r += TestRange::makeClosed(6,10);
- BOOST_CHECK_EQUAL(r, TestRangeSet(6,11));
- r += TestRangeSet(2,6)+8;
- BOOST_CHECK_EQUAL(r, TestRangeSet(2,11));
+ r += TR::makeClosed(6,10);
+ BOOST_CHECK_EQUAL(r, TRSet(6,11));
+ r += TRSet(2,6)+8;
+ BOOST_CHECK_EQUAL(r, TRSet(2,11));
}
QPID_AUTO_TEST_CASE(testRangeSetIterate) {
- TestRangeSet r;
- (((r += 1) += 10) += TestRange(4,7)) += 2;
- BOOST_MESSAGE(r);
+ TRSet r = TRSet(1,3)+TR(4,7)+TR(10,11);
std::vector<int> actual;
std::copy(r.begin(), r.end(), std::back_inserter(actual));
std::vector<int> expect = boost::assign::list_of(1)(2)(4)(5)(6)(10);
@@ -94,51 +102,51 @@ QPID_AUTO_TEST_CASE(testRangeSetIterate) {
QPID_AUTO_TEST_CASE(testRangeSetRemove) {
// points
- BOOST_CHECK_EQUAL(TestRangeSet(0,5)-3, TestRangeSet(0,3)+TestRange(4,5));
- BOOST_CHECK_EQUAL(TestRangeSet(1,5)-5, TestRangeSet(1,5));
- BOOST_CHECK_EQUAL(TestRangeSet(1,5)-0, TestRangeSet(1,5));
+ BOOST_CHECK_EQUAL(TRSet(0,5)-3, TRSet(0,3)+TR(4,5));
+ BOOST_CHECK_EQUAL(TRSet(1,5)-5, TRSet(1,5));
+ BOOST_CHECK_EQUAL(TRSet(1,5)-0, TRSet(1,5));
- TestRangeSet r(TestRangeSet(0,5)+TestRange(10,15)+TestRange(20,25));
+ TRSet r(TRSet(0,5)+TR(10,15)+TR(20,25));
- // TestRanges
- BOOST_CHECK_EQUAL(r-TestRange(0,5), TestRangeSet(10,15)+TestRange(20,25));
- BOOST_CHECK_EQUAL(r-TestRange(10,15), TestRangeSet(0,5)+TestRange(20,25));
- BOOST_CHECK_EQUAL(r-TestRange(20,25), TestRangeSet(0,5)+TestRange(10,15));
+ // TRs
+ BOOST_CHECK_EQUAL(r-TR(0,5), TRSet(10,15)+TR(20,25));
+ BOOST_CHECK_EQUAL(r-TR(10,15), TRSet(0,5)+TR(20,25));
+ BOOST_CHECK_EQUAL(r-TR(20,25), TRSet(0,5)+TR(10,15));
- BOOST_CHECK_EQUAL(r-TestRange(-5, 30), TestRangeSet());
+ BOOST_CHECK_EQUAL(r-TR(-5, 30), TRSet());
- BOOST_CHECK_EQUAL(r-TestRange(-5, 7), TestRangeSet(10,15)+TestRange(20,25));
- BOOST_CHECK_EQUAL(r-TestRange(8,19), TestRangeSet(0,5)+TestRange(20,25));
- BOOST_CHECK_EQUAL(r-TestRange(17,30), TestRangeSet(0,5)+TestRange(10,15));
- BOOST_CHECK_EQUAL(r-TestRange(17,30), TestRangeSet(0,5)+TestRange(10,15));
+ BOOST_CHECK_EQUAL(r-TR(-5, 7), TRSet(10,15)+TR(20,25));
+ BOOST_CHECK_EQUAL(r-TR(8,19), TRSet(0,5)+TR(20,25));
+ BOOST_CHECK_EQUAL(r-TR(17,30), TRSet(0,5)+TR(10,15));
- BOOST_CHECK_EQUAL(r-TestRange(-5, 5), TestRangeSet(10,15)+TestRange(20,25));
- BOOST_CHECK_EQUAL(r-TestRange(10,19), TestRangeSet(0,5)+TestRange(20,25));
- BOOST_CHECK_EQUAL(r-TestRange(18,25), TestRangeSet(0,5)+TestRange(10,15));
- BOOST_CHECK_EQUAL(r-TestRange(23,25), TestRangeSet(0,5)+TestRange(10,15)+TestRange(20,23));
+ BOOST_CHECK_EQUAL(r-TR(-5, 5), TRSet(10,15)+TR(20,25));
+ BOOST_CHECK_EQUAL(r-TR(10,19), TRSet(0,5)+TR(20,25));
+ BOOST_CHECK_EQUAL(r-TR(18,25), TRSet(0,5)+TR(10,15));
+ BOOST_CHECK_EQUAL(r-TR(23,25), TRSet(0,5)+TR(10,15)+TR(20,23));
- BOOST_CHECK_EQUAL(r-TestRange(-3, 3), TestRangeSet(3,5)+TestRange(10,15)+TestRange(20,25));
- BOOST_CHECK_EQUAL(r-TestRange(3, 7), TestRangeSet(0,2)+TestRange(10,15)+TestRange(20,25));
- BOOST_CHECK_EQUAL(r-TestRange(3, 12), TestRangeSet(0,3)+TestRange(12,15)+TestRange(20,25));
- BOOST_CHECK_EQUAL(r-TestRange(3, 22), TestRangeSet(12,15)+TestRange(22,25));
- BOOST_CHECK_EQUAL(r-TestRange(12, 22), TestRangeSet(0,5)+TestRange(10,11)+TestRange(22,25));
+ BOOST_CHECK_EQUAL(r-TR(-3, 3), TRSet(3,5)+TR(10,15)+TR(20,25));
+ BOOST_CHECK_EQUAL(r-TR(3, 7), TRSet(0,2)+TR(10,15)+TR(20,25));
+ BOOST_CHECK_EQUAL(r-TR(3, 12), TRSet(0,3)+TR(12,15)+TR(20,25));
+ BOOST_CHECK_EQUAL(r-TR(3, 22), TRSet(12,15)+TR(22,25));
+ BOOST_CHECK_EQUAL(r-TR(12, 22), TRSet(0,5)+TR(10,11)+TR(22,25));
// Sets
- BOOST_CHECK_EQUAL(r-(TestRangeSet(-1,6)+TestRange(11,14)+TestRange(23,25)),
- TestRangeSet(10,11)+TestRange(14,15)+TestRange(20,23));
-}
-
-QPID_AUTO_TEST_CASE(testRangeContaining) {
- TestRangeSet r;
- (((r += 1) += TestRange(3,5)) += 7);
- BOOST_CHECK_EQUAL(r.rangeContaining(0), TestRange(0,0));
- BOOST_CHECK_EQUAL(r.rangeContaining(1), TestRange(1,2));
- BOOST_CHECK_EQUAL(r.rangeContaining(2), TestRange(2,2));
- BOOST_CHECK_EQUAL(r.rangeContaining(3), TestRange(3,5));
- BOOST_CHECK_EQUAL(r.rangeContaining(4), TestRange(3,5));
- BOOST_CHECK_EQUAL(r.rangeContaining(5), TestRange(5,5));
- BOOST_CHECK_EQUAL(r.rangeContaining(6), TestRange(6,6));
- BOOST_CHECK_EQUAL(r.rangeContaining(7), TestRange(7,8));
+ BOOST_CHECK_EQUAL(r-(TRSet(-1,6)+TR(11,14)+TR(23,25)),
+ TRSet(10,11)+TR(14,15)+TR(20,23));
+ // Split the ranges
+ BOOST_CHECK_EQUAL(r-(TRSet(2,3)+TR(11,13)+TR(21,23)),
+ TRSet(0,2)+TR(4,5)+
+ TR(10,11)+TR(14,15)+
+ TR(20,21)+TR(23,25));
+ // Truncate the ranges
+ BOOST_CHECK_EQUAL(r-(TRSet(0,3)+TR(13,15)+TR(19,23)),
+ TRSet(3,5)+TR(10,13)+TR(20,23));
+ // Remove multiple ranges with truncation
+ BOOST_CHECK_EQUAL(r-(TRSet(3,23)), TRSet(0,3)+TR(23,25));
+ // Remove multiple ranges in middle
+ TRSet r2 = TRSet(0,5)+TR(10,15)+TR(20,25)+TR(30,35);
+ BOOST_CHECK_EQUAL(r2-TRSet(11,24),
+ TRSet(0,5)+TR(10,11)+TR(24,25)+TR(30,35));
}
QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/RefCounted.cpp b/qpid/cpp/src/tests/RefCounted.cpp
index e4c1da5696..3ac3895322 100644
--- a/qpid/cpp/src/tests/RefCounted.cpp
+++ b/qpid/cpp/src/tests/RefCounted.cpp
@@ -21,15 +21,15 @@
#include "unit_test.h"
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(RefCountedTestSuiteTestSuite)
using boost::intrusive_ptr;
using namespace std;
using namespace qpid;
-namespace qpid {
-namespace tests {
-
struct CountMe : public RefCounted {
static int instances;
CountMe() { ++instances; }
diff --git a/qpid/cpp/src/tests/ReplicationTest.cpp b/qpid/cpp/src/tests/ReplicationTest.cpp
index 1219a6b59e..055f06579f 100644
--- a/qpid/cpp/src/tests/ReplicationTest.cpp
+++ b/qpid/cpp/src/tests/ReplicationTest.cpp
@@ -62,7 +62,7 @@ qpid::sys::Shlib plugin(getLibPath("REPLICATING_LISTENER_LIB", default_shlib));
qpid::broker::Broker::Options getBrokerOpts(const std::vector<std::string>& args)
{
std::vector<const char*> argv(args.size());
- transform(args.begin(), args.end(), argv.begin(), boost::bind(&string::c_str, _1));
+ transform(args.begin(), args.end(), argv.begin(), boost::bind(&std::string::c_str, _1));
qpid::broker::Broker::Options opts;
qpid::Plugin::addOptions(opts);
@@ -72,7 +72,7 @@ qpid::broker::Broker::Options getBrokerOpts(const std::vector<std::string>& args
QPID_AUTO_TEST_CASE(testReplicationExchange)
{
- qpid::broker::Broker::Options brokerOpts(getBrokerOpts(list_of<string>("qpidd")
+ qpid::broker::Broker::Options brokerOpts(getBrokerOpts(list_of<std::string>("qpidd")
("--replication-exchange-name=qpid.replication")));
SessionFixture f(brokerOpts);
diff --git a/qpid/cpp/src/tests/SessionState.cpp b/qpid/cpp/src/tests/SessionState.cpp
index 3be9bb0cbc..1cf3415484 100644
--- a/qpid/cpp/src/tests/SessionState.cpp
+++ b/qpid/cpp/src/tests/SessionState.cpp
@@ -34,7 +34,6 @@ namespace tests {
QPID_AUTO_TEST_SUITE(SessionStateTestSuite)
using namespace std;
-using namespace boost;
using namespace qpid::framing;
// ================================================================
diff --git a/qpid/cpp/src/tests/StringUtils.cpp b/qpid/cpp/src/tests/StringUtils.cpp
index 6a19119288..c50287a4f4 100644
--- a/qpid/cpp/src/tests/StringUtils.cpp
+++ b/qpid/cpp/src/tests/StringUtils.cpp
@@ -23,9 +23,11 @@
#include "unit_test.h"
+namespace qpid {
+namespace tests {
+
QPID_AUTO_TEST_SUITE(StringUtilsTestSuite)
-using namespace qpid;
using std::string;
QPID_AUTO_TEST_CASE(testSplit_general)
@@ -75,3 +77,5 @@ QPID_AUTO_TEST_CASE(testSplit_empty)
}
QPID_AUTO_TEST_SUITE_END()
+
+}}
diff --git a/qpid/cpp/src/tests/SystemInfo.cpp b/qpid/cpp/src/tests/SystemInfo.cpp
new file mode 100644
index 0000000000..12d8d3dba8
--- /dev/null
+++ b/qpid/cpp/src/tests/SystemInfo.cpp
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed 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.
+ *
+ */
+
+
+#include "unit_test.h"
+#include "test_tools.h"
+#include "qpid/sys/SystemInfo.h"
+#include <boost/assign.hpp>
+
+using namespace std;
+using namespace qpid::sys;
+using namespace boost::assign;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(SystemInfoTestSuite)
+
+QPID_AUTO_TEST_CASE(TestIsLocalHost) {
+ // Test that local hostname and addresses are considered local
+ Address a;
+ BOOST_ASSERT(SystemInfo::getLocalHostname(a));
+ BOOST_ASSERT(SystemInfo::isLocalHost(a.host));
+ std::vector<Address> addrs;
+ SystemInfo::getLocalIpAddresses(0, addrs);
+ for (std::vector<Address>::iterator i = addrs.begin(); i != addrs.end(); ++i)
+ BOOST_ASSERT(SystemInfo::isLocalHost(i->host));
+ // Check some non-local addresses
+ BOOST_ASSERT(!SystemInfo::isLocalHost("123.4.5.6"));
+ BOOST_ASSERT(!SystemInfo::isLocalHost("nosuchhost"));
+ BOOST_ASSERT(SystemInfo::isLocalHost("127.0.0.1"));
+ BOOST_ASSERT(SystemInfo::isLocalHost("::1"));
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/qpid/cpp/src/tests/TestMessageStore.h b/qpid/cpp/src/tests/TestMessageStore.h
index 20e0b755b2..0b63bc9c15 100644
--- a/qpid/cpp/src/tests/TestMessageStore.h
+++ b/qpid/cpp/src/tests/TestMessageStore.h
@@ -31,7 +31,7 @@ using namespace qpid::framing;
namespace qpid {
namespace tests {
-typedef std::pair<string, boost::intrusive_ptr<PersistableMessage> > msg_queue_pair;
+typedef std::pair<std::string, boost::intrusive_ptr<PersistableMessage> > msg_queue_pair;
class TestMessageStore : public NullMessageStore
{
diff --git a/qpid/cpp/src/tests/TimerTest.cpp b/qpid/cpp/src/tests/TimerTest.cpp
index 6a0a196f4e..fc5004dcb0 100644
--- a/qpid/cpp/src/tests/TimerTest.cpp
+++ b/qpid/cpp/src/tests/TimerTest.cpp
@@ -81,6 +81,8 @@ class TestTask : public TimerTask
uint64_t difference = _abs64(expected - actual);
#elif defined(_WIN32)
uint64_t difference = labs(expected - actual);
+#elif defined(__SUNPRO_CC)
+ uint64_t difference = llabs(expected - actual);
#else
uint64_t difference = abs(expected - actual);
#endif
diff --git a/qpid/cpp/src/tests/TopicExchangeTest.cpp b/qpid/cpp/src/tests/TopicExchangeTest.cpp
index ff8931f9c9..d57951ea3f 100644
--- a/qpid/cpp/src/tests/TopicExchangeTest.cpp
+++ b/qpid/cpp/src/tests/TopicExchangeTest.cpp
@@ -16,6 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
+#include "qpid/broker/TopicKeyNode.h"
#include "qpid/broker/TopicExchange.h"
#include "unit_test.h"
#include "test_tools.h"
@@ -32,14 +33,15 @@ class TopicExchange::TopicExchangeTester {
public:
typedef std::vector<std::string> BindingVec;
+ typedef TopicKeyNode<TopicExchange::BindingKey> TestBindingNode;
private:
// binding node iterator that collects all routes that are bound
- class TestFinder : public TopicExchange::BindingNode::TreeIterator {
+ class TestFinder : public TestBindingNode::TreeIterator {
public:
TestFinder(BindingVec& m) : bv(m) {};
~TestFinder() {};
- bool visit(BindingNode& node) {
+ bool visit(TestBindingNode& node) {
if (!node.bindings.bindingVector.empty())
bv.push_back(node.routePattern);
return true;
@@ -53,7 +55,7 @@ public:
~TopicExchangeTester() {};
bool addBindingKey(const std::string& bKey) {
string routingPattern = normalize(bKey);
- BindingKey *bk = bindingTree.addBindingKey(routingPattern);
+ BindingKey *bk = bindingTree.add(routingPattern);
if (bk) {
// push a dummy binding to mark this node as "non-leaf"
bk->bindingVector.push_back(Binding::shared_ptr());
@@ -64,12 +66,12 @@ public:
bool removeBindingKey(const std::string& bKey){
string routingPattern = normalize(bKey);
- BindingKey *bk = bindingTree.getBindingKey(routingPattern);
+ BindingKey *bk = bindingTree.get(routingPattern);
if (bk) {
bk->bindingVector.pop_back();
if (bk->bindingVector.empty()) {
// no more bindings - remove this node
- bindingTree.removeBindingKey(routingPattern);
+ bindingTree.remove(routingPattern);
}
return true;
}
@@ -87,7 +89,7 @@ public:
}
private:
- TopicExchange::BindingNode bindingTree;
+ TestBindingNode bindingTree;
};
} // namespace broker
diff --git a/qpid/cpp/src/tests/TxPublishTest.cpp b/qpid/cpp/src/tests/TxPublishTest.cpp
index 152581e4ba..a636646035 100644
--- a/qpid/cpp/src/tests/TxPublishTest.cpp
+++ b/qpid/cpp/src/tests/TxPublishTest.cpp
@@ -69,9 +69,9 @@ QPID_AUTO_TEST_CASE(testPrepare)
//ensure messages are enqueued in store
t.op.prepare(0);
BOOST_CHECK_EQUAL((size_t) 2, t.store.enqueued.size());
- BOOST_CHECK_EQUAL(string("queue1"), t.store.enqueued[0].first);
+ BOOST_CHECK_EQUAL(std::string("queue1"), t.store.enqueued[0].first);
BOOST_CHECK_EQUAL(pmsg, t.store.enqueued[0].second);
- BOOST_CHECK_EQUAL(string("queue2"), t.store.enqueued[1].first);
+ BOOST_CHECK_EQUAL(std::string("queue2"), t.store.enqueued[1].first);
BOOST_CHECK_EQUAL(pmsg, t.store.enqueued[1].second);
BOOST_CHECK_EQUAL( true, ( boost::static_pointer_cast<PersistableMessage>(t.msg))->isIngressComplete());
}
diff --git a/qpid/cpp/src/tests/Uuid.cpp b/qpid/cpp/src/tests/Uuid.cpp
index 0195455ca3..aa9580e25e 100644
--- a/qpid/cpp/src/tests/Uuid.cpp
+++ b/qpid/cpp/src/tests/Uuid.cpp
@@ -19,7 +19,6 @@
#include "qpid/framing/Uuid.h"
#include "qpid/framing/Buffer.h"
#include "qpid/types/Uuid.h"
-#include "qpid/sys/alloca.h"
#include "unit_test.h"
@@ -48,10 +47,15 @@ QPID_AUTO_TEST_CASE(testUuidCtor) {
for_each(uuids.begin(), uuids.end(), unique);
}
-boost::array<uint8_t, 16> sample = {{'\x1b', '\x4e', '\x28', '\xba', '\x2f', '\xa1', '\x11', '\xd2', '\x88', '\x3f', '\xb9', '\xa7', '\x61', '\xbd', '\xe3', '\xfb'}};
+boost::array<uint8_t, 16> sample = {{0x1b, 0x4e, 0x28, 0xba, 0x2f, 0xa1, 0x11, 0xd2, 0x88, 0x3f, 0xb9, 0xa7, 0x61, 0xbd, 0xe3, 0xfb}};
const string sampleStr("1b4e28ba-2fa1-11d2-883f-b9a761bde3fb");
const string zeroStr("00000000-0000-0000-0000-000000000000");
+QPID_AUTO_TEST_CASE(testUuidStr) {
+ Uuid uuid(sampleStr);
+ BOOST_CHECK(uuid == sample);
+}
+
QPID_AUTO_TEST_CASE(testUuidIstream) {
Uuid uuid;
istringstream in(sampleStr);
@@ -92,12 +96,12 @@ QPID_AUTO_TEST_CASE(testUuidIOstream) {
}
QPID_AUTO_TEST_CASE(testUuidEncodeDecode) {
- char* buff = static_cast<char*>(::alloca(Uuid::size()));
- Buffer wbuf(buff, Uuid::size());
+ std::vector<char> buff(Uuid::size());
+ Buffer wbuf(&buff[0], Uuid::size());
Uuid uuid(sample.c_array());
uuid.encode(wbuf);
- Buffer rbuf(buff, Uuid::size());
+ Buffer rbuf(&buff[0], Uuid::size());
Uuid decoded;
decoded.decode(rbuf);
BOOST_CHECK_EQUAL(string(sample.begin(), sample.end()),
diff --git a/qpid/cpp/src/tests/acl.py b/qpid/cpp/src/tests/acl.py
index a1b7f93a2e..663af3e8ac 100755
--- a/qpid/cpp/src/tests/acl.py
+++ b/qpid/cpp/src/tests/acl.py
@@ -47,6 +47,19 @@ class ACLTests(TestBase010):
connection.start()
return connection.session(str(uuid4()))
+ def port_i(self):
+ return int(self.defines["port-i"])
+
+ def port_u(self):
+ return int(self.defines["port-u"])
+
+ def get_session_by_port(self, user, passwd, byPort):
+ socket = connect(self.broker.host, byPort)
+ connection = Connection (sock=socket, username=user, password=passwd,
+ mechanism="PLAIN")
+ connection.start()
+ return connection.session(str(uuid4()))
+
def reload_acl(self):
result = None
try:
@@ -55,6 +68,24 @@ class ACLTests(TestBase010):
result = str(e)
return result
+ def acl_lookup(self, userName, action, aclObj, aclObjName, propMap):
+ result = {}
+ try:
+ result = self.broker_access.acl_lookup(userName, action, aclObj, aclObjName, propMap)
+ except Exception, e:
+ result['text'] = str(e)
+ result['result'] = str(e)
+ return result
+
+ def acl_lookupPublish(self, userName, exchange, key):
+ result = {}
+ try:
+ result = self.broker_access.acl_lookupPublish(userName, exchange, key)
+ except Exception, e:
+ result['text'] = str(e)
+ result['result'] = str(e)
+ return result
+
def get_acl_file(self):
return ACLFile(self.config.defines.get("policy-file", "data_dir/policy.acl"))
@@ -73,6 +104,37 @@ class ACLTests(TestBase010):
self.reload_acl()
TestBase010.tearDown(self)
+
+ def Lookup(self, userName, action, aclObj, aclObjName, propMap, expectedResult):
+ result = self.acl_lookup(userName, action, aclObj, aclObjName, propMap)
+ if (result['result'] != expectedResult):
+ suffix = ', [ERROR: Expected= ' + expectedResult
+ if (result['result'] is None):
+ suffix = suffix + ', Exception= ' + result['text'] + ']'
+ else:
+ suffix = suffix + ', Actual= ' + result['result'] + ']'
+ self.fail('Lookup: name=' + userName + ', action=' + action + ', aclObj=' + aclObj + ', aclObjName=' + aclObjName + ', propertyMap=' + str(propMap) + suffix)
+
+
+ def LookupPublish(self, userName, exchName, keyName, expectedResult):
+ result = self.acl_lookupPublish(userName, exchName, keyName)
+ if (result['result'] != expectedResult):
+ if (result['result'] is None):
+ suffix = suffix + ', Exception= ' + result['text'] + ']'
+ else:
+ suffix = suffix + ', Actual= ' + result['result'] + ']'
+ self.fail('LookupPublish: name=' + userName + ', exchange=' + exchName + ', key=' + keyName + suffix)
+
+ def AllBut(self, allList, removeList):
+ tmpList = allList[:]
+ for item in removeList:
+ try:
+ tmpList.remove(item)
+ except Exception, e:
+ self.fail("ERROR in AllBut() \nallList = %s \nremoveList = %s \nerror = %s " \
+ % (allList, removeList, e))
+ return tmpList
+
#=====================================
# ACL general tests
#=====================================
@@ -223,10 +285,38 @@ class ACLTests(TestBase010):
if (result):
self.fail(result)
+ def test_nested_groups(self):
+ """
+ Test nested groups
+ """
+
+ aclf = self.get_acl_file()
+ aclf.write('group user-consume martin@QPID ted@QPID\n')
+ aclf.write('group group2 kim@QPID user-consume rob@QPID \n')
+ aclf.write('acl allow anonymous all all \n')
+ aclf.write('acl allow group2 create queue \n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ session = self.get_session('rob','rob')
+ try:
+ session.queue_declare(queue="rob_queue")
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request");
+ self.fail("Error during queue create request");
+
+
+
def test_user_realm(self):
"""
Test a user defined without a realm
Ex. group admin rajith
+ Note: a user name without a realm is interpreted as a group name
"""
aclf = self.get_acl_file()
aclf.write('group admin bob\n') # shouldn't be allowed
@@ -235,7 +325,7 @@ class ACLTests(TestBase010):
aclf.close()
result = self.reload_acl()
- if (result.find("Username 'bob' must contain a realm",0,len(result)) == -1):
+ if (result.find("not defined yet.",0,len(result)) == -1):
self.fail(result)
def test_allowed_chars_for_username(self):
@@ -460,7 +550,8 @@ class ACLTests(TestBase010):
Test cases for queue acl in allow mode
"""
aclf = self.get_acl_file()
- aclf.write('acl deny bob@QPID create queue name=q1 durable=true passive=true\n')
+ aclf.write('acl deny bob@QPID access queue name=q1\n')
+ aclf.write('acl deny bob@QPID create queue name=q1 durable=true\n')
aclf.write('acl deny bob@QPID create queue name=q2 exclusive=true policytype=ring\n')
aclf.write('acl deny bob@QPID access queue name=q3\n')
aclf.write('acl deny bob@QPID purge queue name=q3\n')
@@ -476,8 +567,15 @@ class ACLTests(TestBase010):
session = self.get_session('bob','bob')
try:
+ session.queue_declare(queue="q1", durable=True)
+ self.fail("ACL should deny queue create request with name=q1 durable=true");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
session.queue_declare(queue="q1", durable=True, passive=True)
- self.fail("ACL should deny queue create request with name=q1 durable=true passive=true");
+ self.fail("ACL should deny queue passive declare request with name=q1 durable=true");
except qpid.session.SessionException, e:
self.assertEqual(403,e.args[0].error_code)
session = self.get_session('bob','bob')
@@ -563,7 +661,8 @@ class ACLTests(TestBase010):
Test cases for queue acl in deny mode
"""
aclf = self.get_acl_file()
- aclf.write('acl allow bob@QPID create queue name=q1 durable=true passive=true\n')
+ aclf.write('acl allow bob@QPID access queue name=q1\n')
+ aclf.write('acl allow bob@QPID create queue name=q1 durable=true\n')
aclf.write('acl allow bob@QPID create queue name=q2 exclusive=true policytype=ring\n')
aclf.write('acl allow bob@QPID access queue name=q3\n')
aclf.write('acl allow bob@QPID purge queue name=q3\n')
@@ -583,10 +682,16 @@ class ACLTests(TestBase010):
session = self.get_session('bob','bob')
try:
+ session.queue_declare(queue="q1", durable=True)
+ except qpid.session.SessionException, e:
+ if (403 == e.args[0].error_code):
+ self.fail("ACL should allow queue create request with name=q1 durable=true");
+
+ try:
session.queue_declare(queue="q1", durable=True, passive=True)
except qpid.session.SessionException, e:
if (403 == e.args[0].error_code):
- self.fail("ACL should allow queue create request with name=q1 durable=true passive=true");
+ self.fail("ACL should allow queue passive declare request with name=q1 durable=true passive=true");
try:
session.queue_declare(queue="q1", durable=False, passive=False)
@@ -736,7 +841,8 @@ class ACLTests(TestBase010):
Test cases for exchange acl in allow mode
"""
aclf = self.get_acl_file()
- aclf.write('acl deny bob@QPID create exchange name=testEx durable=true passive=true\n')
+ aclf.write('acl deny bob@QPID access exchange name=testEx\n')
+ aclf.write('acl deny bob@QPID create exchange name=testEx durable=true\n')
aclf.write('acl deny bob@QPID create exchange name=ex1 type=direct\n')
aclf.write('acl deny bob@QPID access exchange name=myEx queuename=q1 routingkey=rk1.*\n')
aclf.write('acl deny bob@QPID bind exchange name=myEx queuename=q1 routingkey=rk1\n')
@@ -755,18 +861,25 @@ class ACLTests(TestBase010):
session.exchange_declare(exchange='myEx', type='direct')
try:
+ session.exchange_declare(exchange='testEx', durable=True)
+ self.fail("ACL should deny exchange create request with name=testEx durable=true");
+ except qpid.session.SessionException, e:
+ self.assertEqual(403,e.args[0].error_code)
+ session = self.get_session('bob','bob')
+
+ try:
session.exchange_declare(exchange='testEx', durable=True, passive=True)
- self.fail("ACL should deny exchange create request with name=testEx durable=true passive=true");
+ self.fail("ACL should deny passive exchange declare request with name=testEx durable=true passive=true");
except qpid.session.SessionException, e:
self.assertEqual(403,e.args[0].error_code)
session = self.get_session('bob','bob')
try:
- session.exchange_declare(exchange='testEx', type='direct', durable=True, passive=False)
+ session.exchange_declare(exchange='testEx', type='direct', durable=False)
except qpid.session.SessionException, e:
print e
if (403 == e.args[0].error_code):
- self.fail("ACL should allow exchange create request for testEx with any parameter other than durable=true and passive=true");
+ self.fail("ACL should allow exchange create request for testEx with any parameter other than durable=true");
try:
session.exchange_declare(exchange='ex1', type='direct')
@@ -867,7 +980,7 @@ class ACLTests(TestBase010):
Test cases for exchange acl in deny mode
"""
aclf = self.get_acl_file()
- aclf.write('acl allow bob@QPID create exchange name=myEx durable=true passive=false\n')
+ aclf.write('acl allow bob@QPID create exchange name=myEx durable=true\n')
aclf.write('acl allow bob@QPID bind exchange name=amq.topic queuename=bar routingkey=foo.*\n')
aclf.write('acl allow bob@QPID unbind exchange name=amq.topic queuename=bar routingkey=foo.*\n')
aclf.write('acl allow bob@QPID access exchange name=myEx queuename=q1 routingkey=rk1.*\n')
@@ -1278,6 +1391,200 @@ class ACLTests(TestBase010):
admin.set_timestamp_cfg(ts) #should pass
+
+ #=====================================
+ # QMF Functional tests
+ #=====================================
+
+ def test_qmf_functional_tests(self):
+ """
+ Test using QMF method hooks into ACL logic
+ """
+ aclf = self.get_acl_file()
+ aclf.write('group admins moe@COMPANY.COM \\\n')
+ aclf.write(' larry@COMPANY.COM \\\n')
+ aclf.write(' curly@COMPANY.COM \\\n')
+ aclf.write(' shemp@COMPANY.COM\n')
+ aclf.write('group auditors aaudit@COMPANY.COM baudit@COMPANY.COM caudit@COMPANY.COM \\\n')
+ aclf.write(' daudit@COMPANY.COM eaduit@COMPANY.COM eaudit@COMPANY.COM\n')
+ aclf.write('group tatunghosts tatung01@COMPANY.COM \\\n')
+ aclf.write(' tatung02/x86.build.company.com@COMPANY.COM \\\n')
+ aclf.write(' tatung03/x86.build.company.com@COMPANY.COM \\\n')
+ aclf.write(' tatung04/x86.build.company.com@COMPANY.COM \n')
+ aclf.write('group publishusers publish@COMPANY.COM x-pubs@COMPANY.COM\n')
+ aclf.write('acl allow-log admins all all\n')
+ aclf.write('# begin hack alert: allow anonymous to access the lookup debug functions\n')
+ aclf.write('acl allow-log anonymous create queue\n')
+ aclf.write('acl allow-log anonymous all exchange name=qmf.*\n')
+ aclf.write('acl allow-log anonymous all exchange name=amq.direct\n')
+ aclf.write('acl allow-log anonymous all exchange name=qpid.management\n')
+ aclf.write('acl allow-log anonymous access method name=*\n')
+ aclf.write('# end hack alert\n')
+ aclf.write('acl allow-log auditors all exchange name=company.topic routingkey=private.audit.*\n')
+ aclf.write('acl allow-log tatunghosts publish exchange name=company.topic routingkey=tatung.*\n')
+ aclf.write('acl allow-log tatunghosts publish exchange name=company.direct routingkey=tatung-service-queue\n')
+ aclf.write('acl allow-log publishusers create queue\n')
+ aclf.write('acl allow-log publishusers publish exchange name=qpid.management routingkey=broker\n')
+ aclf.write('acl allow-log publishusers publish exchange name=qmf.default.topic routingkey=*\n')
+ aclf.write('acl allow-log publishusers publish exchange name=qmf.default.direct routingkey=*\n')
+ aclf.write('acl allow-log all bind exchange name=company.topic routingkey=tatung.*\n')
+ aclf.write('acl allow-log all bind exchange name=company.direct routingkey=tatung-service-queue\n')
+ aclf.write('acl allow-log all consume queue\n')
+ aclf.write('acl allow-log all access exchange\n')
+ aclf.write('acl allow-log all access queue\n')
+ aclf.write('acl allow-log all create queue name=tmp.* durable=false autodelete=true exclusive=true policytype=ring\n')
+ aclf.write('acl allow mrQ create queue queuemaxsizelowerlimit=100 queuemaxsizeupperlimit=200 queuemaxcountlowerlimit=300 queuemaxcountupperlimit=400\n')
+ aclf.write('acl deny-log all all\n')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ #
+ # define some group lists
+ #
+ g_admins = ['moe@COMPANY.COM', \
+ 'larry@COMPANY.COM', \
+ 'curly@COMPANY.COM', \
+ 'shemp@COMPANY.COM']
+
+ g_auditors = [ 'aaudit@COMPANY.COM','baudit@COMPANY.COM','caudit@COMPANY.COM', \
+ 'daudit@COMPANY.COM','eaduit@COMPANY.COM','eaudit@COMPANY.COM']
+
+ g_tatunghosts = ['tatung01@COMPANY.COM', \
+ 'tatung02/x86.build.company.com@COMPANY.COM', \
+ 'tatung03/x86.build.company.com@COMPANY.COM', \
+ 'tatung04/x86.build.company.com@COMPANY.COM']
+
+ g_publishusers = ['publish@COMPANY.COM', 'x-pubs@COMPANY.COM']
+
+ g_public = ['jpublic@COMPANY.COM', 'me@yahoo.com']
+
+ g_all = g_admins + g_auditors + g_tatunghosts + g_publishusers + g_public
+
+ action_all = ['consume','publish','create','access','bind','unbind','delete','purge','update']
+
+ #
+ # Run some tests verifying against users who are in and who are out of given groups.
+ #
+
+ for u in g_admins:
+ self.Lookup(u, "create", "queue", "anything", {"durable":"true"}, "allow-log")
+
+ uInTest = g_auditors + g_admins
+ uOutTest = self.AllBut(g_all, uInTest)
+
+ for u in uInTest:
+ self.LookupPublish(u, "company.topic", "private.audit.This", "allow-log")
+
+ for u in uInTest:
+ for a in action_all:
+ self.Lookup(u, a, "exchange", "company.topic", {"routingkey":"private.audit.This"}, "allow-log")
+
+ for u in uOutTest:
+ self.LookupPublish(u, "company.topic", "private.audit.This", "deny-log")
+ self.Lookup(u, "bind", "exchange", "company.topic", {"routingkey":"private.audit.This"}, "deny-log")
+
+ uInTest = g_admins + g_tatunghosts
+ uOutTest = self.AllBut(g_all, uInTest)
+
+ for u in uInTest:
+ self.LookupPublish(u, "company.topic", "tatung.this2", "allow-log")
+ self.LookupPublish(u, "company.direct", "tatung-service-queue", "allow-log")
+
+ for u in uOutTest:
+ self.LookupPublish(u, "company.topic", "tatung.this2", "deny-log")
+ self.LookupPublish(u, "company.direct", "tatung-service-queue", "deny-log")
+
+ for u in uOutTest:
+ for a in ["bind", "access"]:
+ self.Lookup(u, a, "exchange", "company.topic", {"routingkey":"tatung.this2"}, "allow-log")
+ self.Lookup(u, a, "exchange", "company.direct", {"routingkey":"tatung-service-queue"}, "allow-log")
+
+ uInTest = g_admins + g_publishusers
+ uOutTest = self.AllBut(g_all, uInTest)
+
+ for u in uInTest:
+ self.LookupPublish(u, "qpid.management", "broker", "allow-log")
+ self.LookupPublish(u, "qmf.default.topic", "this3", "allow-log")
+ self.LookupPublish(u, "qmf.default.direct", "this4", "allow-log")
+
+ for u in uOutTest:
+ self.LookupPublish(u, "qpid.management", "broker", "deny-log")
+ self.LookupPublish(u, "qmf.default.topic", "this3", "deny-log")
+ self.LookupPublish(u, "qmf.default.direct", "this4", "deny-log")
+
+ for u in uOutTest:
+ for a in ["bind"]:
+ self.Lookup(u, a, "exchange", "qpid.management", {"routingkey":"broker"}, "deny-log")
+ self.Lookup(u, a, "exchange", "qmf.default.topic", {"routingkey":"this3"}, "deny-log")
+ self.Lookup(u, a, "exchange", "qmf.default.direct", {"routingkey":"this4"}, "deny-log")
+ for a in ["access"]:
+ self.Lookup(u, a, "exchange", "qpid.management", {"routingkey":"broker"}, "allow-log")
+ self.Lookup(u, a, "exchange", "qmf.default.topic", {"routingkey":"this3"}, "allow-log")
+ self.Lookup(u, a, "exchange", "qmf.default.direct", {"routingkey":"this4"}, "allow-log")
+
+ # Test against queue size limits
+
+ self.Lookup('mrQ', 'create', 'queue', 'abc', {"maxqueuesize":"150", "maxqueuecount":"350"}, "allow")
+ self.Lookup('mrQ', 'create', 'queue', 'def', {"maxqueuesize":"99", "maxqueuecount":"350"}, "deny")
+ self.Lookup('mrQ', 'create', 'queue', 'uvw', {"maxqueuesize":"201", "maxqueuecount":"350"}, "deny")
+ self.Lookup('mrQ', 'create', 'queue', 'xyz', {"maxqueuesize":"150", "maxqueuecount":"299"}, "deny")
+ self.Lookup('mrQ', 'create', 'queue', '', {"maxqueuesize":"150", "maxqueuecount":"401"}, "deny")
+ self.Lookup('mrQ', 'create', 'queue', '', {"maxqueuesize":"0", "maxqueuecount":"401"}, "deny")
+ self.Lookup('mrQ', 'create', 'queue', '', {"maxqueuesize":"150", "maxqueuecount":"0" }, "deny")
+
+
+ #=====================================
+ # Connection limits
+ #=====================================
+
+ def test_connection_limits(self):
+ """
+ Test ACL control connection limits
+ """
+ # By username should be able to connect twice per user
+ try:
+ sessiona1 = self.get_session_by_port('alice','alice', self.port_u())
+ sessiona2 = self.get_session_by_port('alice','alice', self.port_u())
+ except Exception, e:
+ self.fail("Could not create two connections for user alice: " + str(e))
+
+ # Third session should fail
+ try:
+ sessiona3 = self.get_session_by_port('alice','alice', self.port_u())
+ self.fail("Should not be able to create third connection for user alice")
+ except Exception, e:
+ result = None
+
+ try:
+ sessionb1 = self.get_session_by_port('bob','bob', self.port_u())
+ sessionb2 = self.get_session_by_port('bob','bob', self.port_u())
+ except Exception, e:
+ self.fail("Could not create two connections for user bob: " + str(e))
+
+ try:
+ sessionb3 = self.get_session_by_port('bob','bob', self.port_u())
+ self.fail("Should not be able to create third connection for user bob")
+ except Exception, e:
+ result = None
+
+ # By IP address should be able to connect twice per client address
+ try:
+ sessionb1 = self.get_session_by_port('alice','alice', self.port_i())
+ sessionb2 = self.get_session_by_port('bob','bob', self.port_i())
+ except Exception, e:
+ self.fail("Could not create two connections for client address: " + str(e))
+
+ # Third session should fail
+ try:
+ sessionb3 = self.get_session_by_port('charlie','charlie', self.port_i())
+ self.fail("Should not be able to create third connection for client address")
+ except Exception, e:
+ result = None
+
+
class BrokerAdmin:
def __init__(self, broker, username=None, password=None):
self.connection = qpid.messaging.Connection(broker)
diff --git a/qpid/cpp/src/tests/amqp_0_10/Map.cpp b/qpid/cpp/src/tests/amqp_0_10/Map.cpp
deleted file mode 100644
index ffb235829e..0000000000
--- a/qpid/cpp/src/tests/amqp_0_10/Map.cpp
+++ /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.
- *
- */
-#include "amqp_0_10/unit_test.h"
-#include "qpid/amqp_0_10/Map.h"
-#include "qpid/amqp_0_10/Array.h"
-#include "qpid/amqp_0_10/Struct32.h"
-#include "qpid/amqp_0_10/UnknownType.h"
-#include "qpid/amqp_0_10/Codec.h"
-#include <iostream>
-
-using namespace qpid::amqp_0_10;
-using namespace std;
-
-QPID_AUTO_TEST_SUITE(MapTestSuite)
-
- QPID_AUTO_TEST_CASE(testGetSet) {
- MapValue v;
- v = Str8("foo");
- BOOST_CHECK(v.get<Str8>());
- BOOST_CHECK(!v.get<uint8_t>());
- BOOST_CHECK_EQUAL(*v.get<Str8>(), "foo");
-
- v = uint8_t(42);
- BOOST_CHECK(!v.get<Str8>());
- BOOST_CHECK(v.get<uint8_t>());
- BOOST_CHECK_EQUAL(*v.get<uint8_t>(), 42);
-
- v = uint16_t(12);
- BOOST_CHECK(v.get<uint16_t>());
- BOOST_CHECK_EQUAL(*v.get<uint16_t>(), 12);
-}
-
-template <class R> struct TestVisitor : public MapValue::Visitor<R> {
- template <class T> R operator()(const T&) const { throw MapValue::BadTypeException(); }
- R operator()(const R& r) const { return r; }
-};
-
-QPID_AUTO_TEST_CASE(testVisit) {
- MapValue v;
- v = Str8("foo");
- BOOST_CHECK_EQUAL(v.apply_visitor(TestVisitor<Str8>()), "foo");
- v = Uint16(42);
- BOOST_CHECK_EQUAL(v.apply_visitor(TestVisitor<Uint16>()), 42);
- try {
- v.apply_visitor(TestVisitor<bool>());
- BOOST_FAIL("Expecting exception");
- }
- catch(const MapValue::BadTypeException&) {}
-}
-
-
-QPID_AUTO_TEST_CASE(testEncodeMapValue) {
- MapValue mv;
- std::string data;
- mv = Str8("hello");
- Codec::encode(back_inserter(data))(mv);
- BOOST_CHECK_EQUAL(data.size(), Codec::size(mv));
- MapValue mv2;
- Codec::decode(data.begin())(mv2);
- BOOST_CHECK_EQUAL(mv2.getCode(), 0x85);
- BOOST_REQUIRE(mv2.get<Str8>());
- BOOST_CHECK_EQUAL(*mv2.get<Str8>(), "hello");
-}
-
-QPID_AUTO_TEST_CASE(testEncode) {
- Map map;
- std::string data;
- map["A"] = true;
- map["b"] = Str8("hello");
- Codec::encode(back_inserter(data))(map);
- BOOST_CHECK_EQUAL(Codec::size(map), data.size());
- Map map2;
- Codec::decode(data.begin())(map2);
- BOOST_CHECK_EQUAL(map.size(), 2u);
- BOOST_CHECK(map["A"].get<bool>());
- BOOST_CHECK_EQUAL(*map["b"].get<Str8>(), "hello");
-}
-
-
-QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp b/qpid/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp
deleted file mode 100644
index f54ee0da22..0000000000
--- a/qpid/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp
+++ /dev/null
@@ -1,49 +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.
- *
- */
-
-#include "amqp_0_10/unit_test.h"
-#include "qpid/amqp_0_10/ProxyTemplate.h"
-#include <boost/any.hpp>
-
-QPID_AUTO_TEST_SUITE(ProxyTemplateTestSuite)
-
-using namespace qpid::amqp_0_10;
-
-struct ToAny {
- template <class T>
- boost::any operator()(const T& t) { return boost::any(t); }
-};
-
-struct AnyProxy : public ProxyTemplate<ToAny, boost::any> {};
-
-QPID_AUTO_TEST_CASE(testAnyProxy) {
- AnyProxy p;
- boost::any a=p.connectionTune(1,2,3,4);
- BOOST_CHECK_EQUAL(a.type().name(), typeid(connection::Tune).name());
- connection::Tune* tune=boost::any_cast<connection::Tune>(&a);
- BOOST_REQUIRE(tune);
- BOOST_CHECK_EQUAL(tune->channelMax, 1u);
- BOOST_CHECK_EQUAL(tune->maxFrameSize, 2u);
- BOOST_CHECK_EQUAL(tune->heartbeatMin, 3u);
- BOOST_CHECK_EQUAL(tune->heartbeatMax, 4u);
-}
-
-QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/amqp_0_10/apply.cpp b/qpid/cpp/src/tests/amqp_0_10/apply.cpp
deleted file mode 100644
index 0aa4421791..0000000000
--- a/qpid/cpp/src/tests/amqp_0_10/apply.cpp
+++ /dev/null
@@ -1,99 +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.
- *
- */
-#include "amqp_0_10/unit_test.h"
-#include "qpid/amqp_0_10/specification.h"
-#include "qpid/amqp_0_10/ApplyControl.h"
-
-QPID_AUTO_TEST_SUITE(VisitorTestSuite)
-
-using namespace qpid::amqp_0_10;
-
-struct GetCode : public ApplyFunctor<uint8_t> {
- template <class T> uint8_t operator()(const T&) const { return T::CODE; }
-};
-
-struct SetChannelMax : ApplyFunctor<void> {
- template <class T> void operator()(T&) const { BOOST_FAIL(""); }
- void operator()(connection::Tune& t) const { t.channelMax=42; }
-};
-
-struct TestFunctor {
- typedef bool result_type;
- bool operator()(const connection::Tune& tune) {
- BOOST_CHECK_EQUAL(tune.channelMax, 1u);
- BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u);
- BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u);
- BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u);
- return true;
- }
- template <class T>
- bool operator()(const T&) { return false; }
-};
-
-QPID_AUTO_TEST_CASE(testApply) {
- connection::Tune tune(1,2,3,4);
- Control* p = &tune;
-
- // boost oddity - without the cast we get undefined symbol errors.
- BOOST_CHECK_EQUAL(apply(GetCode(), *p), (uint8_t)connection::Tune::CODE);
-
- TestFunctor tf;
- BOOST_CHECK(apply(tf, *p));
-
- connection::Start start;
- p = &start;
- BOOST_CHECK(!apply(tf, *p));
-
- apply(SetChannelMax(), tune);
- BOOST_CHECK_EQUAL(tune.channelMax, 42);
-}
-
-struct VoidTestFunctor {
- typedef void result_type;
-
- int code;
- VoidTestFunctor() : code() {}
-
- void operator()(const connection::Tune& tune) {
- BOOST_CHECK_EQUAL(tune.channelMax, 1u);
- BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u);
- BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u);
- BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u);
- code=connection::Tune::CODE;
- }
- template <class T>
- void operator()(const T&) { code=0xFF; }
-};
-
-QPID_AUTO_TEST_CASE(testApplyVoid) {
- connection::Tune tune(1,2,3,4);
- Control* p = &tune;
- VoidTestFunctor tf;
- apply(tf, *p);
- BOOST_CHECK_EQUAL(uint8_t(connection::Tune::CODE), tf.code);
-
- connection::Start start;
- p = &start;
- apply(tf, *p);
- BOOST_CHECK_EQUAL(0xFF, tf.code);
-}
-
-QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/amqp_0_10/handlers.cpp b/qpid/cpp/src/tests/amqp_0_10/handlers.cpp
deleted file mode 100644
index 91bb304a17..0000000000
--- a/qpid/cpp/src/tests/amqp_0_10/handlers.cpp
+++ /dev/null
@@ -1,125 +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.
- *
- */
-
-#include "amqp_0_10/unit_test.h"
-#include "qpid/Exception.h"
-#include "qpid/amqp_0_10/Unit.h"
-#include "qpid/amqp_0_10/ControlHolder.h"
-#include "qpid/amqp_0_10/CommandHolder.h"
-#include "qpid/amqp_0_10/handlers.h"
-#include "qpid/amqp_0_10/specification.h"
-
-QPID_AUTO_TEST_SUITE(handler_tests)
-
-using namespace qpid::amqp_0_10;
-using namespace std;
-
-string called; // Set by called handler function
-
-// Note on handlers:
-//
-// Control and Command handlers are separate, both behave the same way,
-// so substitute "control or command" for command in the following.
-//
-// Command handlers derive from CommandHandler and implement functions
-// for all the commands they handle. Handling an unimplemented command
-// will raise NotImplementedException.
-//
-// Using virtual inheritance from CommandHandler allows multiple
-// handlers to be aggregated into one with multiple inheritance,
-// See test code for example.
-//
-// E.g. the existing broker model would have two control handlers:
-// - ConnectionHandler: ControlHandler for connection controls.
-// - SessionHandler: ControlHandler for session controls.
-// It would have class-command handlers for each AMQP class:
-// - QueueHandler, MessageHandler etc.. handle each class.
-// And an aggregate handler in place of BrokerAdapter
-// - BrokerCommandHandler: public QueueHandler, MessageHandler ...
-//
-// In other applications (e.g. cluster) any combination of commands
-// can be handled by a given handler. It _might_ simplify the code
-// to collaps ConnectionHandler and SessionHandler into a single
-// ControlHandler (or it might not.)
-
-struct TestExecutionHandler : public virtual CommandHandler {
- void executionSync() { called = "executionSync"; }
- // ... etc. for all execution commands
-};
-
-struct TestMessageHandler : public virtual CommandHandler {
- void messageCancel(const Str8&) { called="messageCancel"; }
- // ... etc.
-};
-
-// Aggregate handler for all recognised commands.
-struct TestCommandHandler :
- public TestExecutionHandler,
- public TestMessageHandler
- // ... etc. handlers for all command classes.
-{}; // Nothing to do.
-
-
-// Sample unit handler, written as a static_visitor.
-// Note it could equally be written with if/else statements
-// in handle.
-//
-struct TestUnitHandler : public boost::static_visitor<void> {
- TestCommandHandler handler;
- void handle(const Unit& u) { u.applyVisitor(*this); }
-
- void operator()(const Body&) { called="Body"; }
- void operator()(const Header&) { called="Header"; }
- void operator()(const ControlHolder&) { throw qpid::Exception("I don't do controls."); }
- void operator()(const CommandHolder& c) { c.invoke(handler); }
-};
-
-QPID_AUTO_TEST_CASE(testHandlers) {
- TestUnitHandler handler;
- Unit u;
-
- u = Body();
- handler.handle(u);
- BOOST_CHECK_EQUAL("Body", called);
-
- u = Header();
- handler.handle(u);
- BOOST_CHECK_EQUAL("Header", called);
-
- // in_place<Foo>(...) is equivalent to Foo(...) but
- // constructs Foo directly in the holder, avoiding
- // a copy.
-
- u = CommandHolder(in_place<execution::Sync>());
- handler.handle(u);
- BOOST_CHECK_EQUAL("executionSync", called);
-
- u = ControlHolder(in_place<connection::Start>(Map(), Str16Array(), Str16Array()));
- try {
- handler.handle(u);
- } catch (const qpid::Exception&) {}
-
- u = CommandHolder(in_place<message::Cancel>(Str8()));
- handler.handle(u);
- BOOST_CHECK_EQUAL("messageCancel", called);
-}
-
-QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/amqp_0_10/serialize.cpp b/qpid/cpp/src/tests/amqp_0_10/serialize.cpp
deleted file mode 100644
index 975d6206ec..0000000000
--- a/qpid/cpp/src/tests/amqp_0_10/serialize.cpp
+++ /dev/null
@@ -1,429 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-#include "amqp_0_10/unit_test.h"
-#include "amqp_0_10/allSegmentTypes.h"
-
-#include "qpid/framing/AMQFrame.h"
-#include "qpid/framing/Buffer.h"
-
-#include "qpid/amqp_0_10/Packer.h"
-#include "qpid/amqp_0_10/built_in_types.h"
-#include "qpid/amqp_0_10/Codec.h"
-#include "qpid/amqp_0_10/specification.h"
-#include "qpid/amqp_0_10/ControlHolder.h"
-#include "qpid/amqp_0_10/Struct32.h"
-#include "qpid/amqp_0_10/FrameHeader.h"
-#include "qpid/amqp_0_10/Map.h"
-#include "qpid/amqp_0_10/Unit.h"
-#include "allSegmentTypes.h"
-
-#include <boost/test/test_case_template.hpp>
-#include <boost/type_traits/is_arithmetic.hpp>
-#include <boost/utility/enable_if.hpp>
-#include <boost/optional.hpp>
-#include <boost/mpl/vector.hpp>
-#include <boost/mpl/back_inserter.hpp>
-#include <boost/mpl/copy.hpp>
-#include <boost/mpl/empty_sequence.hpp>
-#include <boost/current_function.hpp>
-#include <iterator>
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <netinet/in.h>
-
-// Missing operators needed for tests.
-namespace boost {
-template <class T, size_t N>
-std::ostream& operator<<(std::ostream& out, const array<T,N>& a) {
- std::ostream_iterator<T> o(out, " ");
- std::copy(a.begin(), a.end(), o);
- return out;
-}
-} // boost
-
-QPID_AUTO_TEST_SUITE(SerializeTestSuite)
-
-using namespace std;
-namespace mpl=boost::mpl;
-using namespace qpid::amqp_0_10;
-using qpid::framing::in_place;
-
-template <class A, class B> struct concat2 { typedef typename mpl::copy<B, typename mpl::back_inserter<A> >::type type; };
-template <class A, class B, class C> struct concat3 { typedef typename concat2<A, typename concat2<B, C>::type>::type type; };
-template <class A, class B, class C, class D> struct concat4 { typedef typename concat2<A, typename concat3<B, C, D>::type>::type type; };
-
-typedef mpl::vector<Boolean, Char, Int32, Int64, Int8, Uint16, CharUtf32, Uint32, Uint64, Bin8, Uint8>::type IntegralTypes;
-typedef mpl::vector<Bin1024, Bin128, Bin16, Bin256, Bin32, Bin40, Bin512, Bin64, Bin72>::type BinTypes;
-typedef mpl::vector<Double, Float>::type FloatTypes;
-typedef mpl::vector<SequenceNo, Uuid, Datetime, Dec32, Dec64> FixedSizeClassTypes;
-typedef mpl::vector<Map, Vbin8, Str8Latin, Str8, Str8Utf16, Vbin16, Str16Latin, Str16, Str16Utf16, Vbin32> VariableSizeTypes;
-
-typedef concat4<IntegralTypes, BinTypes, FloatTypes, FixedSizeClassTypes>::type FixedSizeTypes;
-typedef concat2<FixedSizeTypes, VariableSizeTypes>::type AllTypes;
-
-// TODO aconway 2008-02-20: should test 64 bit integrals for order also.
-QPID_AUTO_TEST_CASE(testNetworkByteOrder) {
- string data;
-
- uint32_t l = 0x11223344;
- Codec::encode(std::back_inserter(data))(l);
- uint32_t enc=reinterpret_cast<const uint32_t&>(*data.data());
- uint32_t l2 = ntohl(enc);
- BOOST_CHECK_EQUAL(l, l2);
-
- data.clear();
- uint16_t s = 0x1122;
- Codec::encode(std::back_inserter(data))(s);
- uint32_t s2 = ntohs(*reinterpret_cast<const uint32_t*>(data.data()));
- BOOST_CHECK_EQUAL(s, s2);
-}
-
-QPID_AUTO_TEST_CASE(testSetLimit) {
- typedef Codec::Encoder<back_insert_iterator<string> > Encoder;
- string data;
- Encoder encode(back_inserter(data), 3);
- encode('1')('2')('3');
- try {
- encode('4');
- BOOST_FAIL("Expected exception");
- } catch (...) {} // FIXME aconway 2008-04-03: catch proper exception
- BOOST_CHECK_EQUAL(data, "123");
-}
-
-QPID_AUTO_TEST_CASE(testScopedLimit) {
- typedef Codec::Encoder<back_insert_iterator<string> > Encoder;
- string data;
- Encoder encode(back_inserter(data), 10);
- encode(Str8("123")); // 4 bytes
- {
- Encoder::ScopedLimit l(encode, 3);
- encode('a')('b')('c');
- try {
- encode('d');
- BOOST_FAIL("Expected exception");
- } catch(...) {} // FIXME aconway 2008-04-03: catch proper exception
- }
- BOOST_CHECK_EQUAL(data, "\003123abc");
- encode('x')('y')('z');
- try {
- encode('!');
- BOOST_FAIL("Expected exception");
- } catch(...) {} // FIXME aconway 2008-04-03: catch proper exception
- BOOST_CHECK_EQUAL(data.size(), 10u);
-}
-
-// Assign test values to the various types.
-void testValue(bool& b) { b = true; }
-void testValue(Bit&) { }
-template <class T> typename boost::enable_if<boost::is_arithmetic<T> >::type testValue(T& n) { n=42; }
-void testValue(CharUtf32& c) { c = 43; }
-void testValue(long long& l) { l = 0x012345; }
-void testValue(Datetime& dt) { dt = qpid::sys::now(); }
-void testValue(Uuid& uuid) { uuid=Uuid(true); }
-template <class E, class M> void testValue(Decimal<E,M>& d) { d.exponent=2; d.mantissa=0x1122; }
-void testValue(SequenceNo& s) { s = 42; }
-template <size_t N> void testValue(Bin<N>& a) { a.assign(42); }
-template <class T, class S, int Unique> void testValue(SerializableString<T, S, Unique>& s) {
- char msg[]="foobar";
- s.assign(msg, msg+sizeof(msg));
-}
-void testValue(Str16& s) { s = "the quick brown fox jumped over the lazy dog"; }
-void testValue(Str8& s) { s = "foobar"; }
-void testValue(Map& m) { m["s"] = Str8("foobar"); m["b"] = true; m["c"] = uint16_t(42); }
-
-//typedef mpl::vector<Str8, Str16>::type TestTypes;
-/*BOOST_AUTO_TEST_CASE_TEMPLATE(testEncodeDecode, T, AllTypes)
-{
- string data;
- T t;
- testValue(t);
- Codec::encode(std::back_inserter(data))(t);
-
- BOOST_CHECK_EQUAL(Codec::size(t), data.size());
-
- T t2;
- Codec::decode(data.begin())(t2);
- BOOST_CHECK_EQUAL(t,t2);
-}
-*/
-
-struct TestMe {
- bool encoded, decoded;
- char value;
- TestMe(char v) : encoded(), decoded(), value(v) {}
- template <class S> void encode(S& s) const {
- const_cast<TestMe*>(this)->encoded=true; s(value);
- }
- template <class S> void decode(S& s) { decoded=true; s(value); }
- template <class S> void serialize(S& s) { s.split(*this); }
-};
-
-QPID_AUTO_TEST_CASE(testSplit) {
- string data;
- TestMe t1('x');
- Codec::encode(std::back_inserter(data))(t1);
- BOOST_CHECK(t1.encoded);
- BOOST_CHECK(!t1.decoded);
- BOOST_CHECK_EQUAL(data, "x");
-
- TestMe t2('y');
- Codec::decode(data.begin())(t2);
- BOOST_CHECK(!t2.encoded);
- BOOST_CHECK(t2.decoded);
- BOOST_CHECK_EQUAL(t2.value, 'x');
-}
-
-QPID_AUTO_TEST_CASE(testControlEncodeDecode) {
- string data;
- Control::Holder h(in_place<connection::Tune>(1,2,3,4));
- Codec::encode(std::back_inserter(data))(h);
-
- BOOST_CHECK_EQUAL(data.size(), Codec::size(h));
-
- Codec::Decoder<string::iterator> decode(data.begin());
- Control::Holder h2;
- decode(h2);
-
- BOOST_REQUIRE(h2.get());
- BOOST_CHECK_EQUAL(h2.get()->getClassCode(), connection::CODE);
- BOOST_CHECK_EQUAL(h2.get()->getCode(), uint8_t(connection::Tune::CODE));
- connection::Tune& tune=static_cast<connection::Tune&>(*h2.get());
- BOOST_CHECK_EQUAL(tune.channelMax, 1u);
- BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u);
- BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u);
- BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u);
-}
-
-QPID_AUTO_TEST_CASE(testStruct32) {
- message::DeliveryProperties dp;
- dp.priority=message::MEDIUM;
- dp.routingKey="foo";
- Struct32 s(dp);
- string data;
- Codec::encode(back_inserter(data))(s);
-
- uint32_t structSize; // Starts with size
- Codec::decode(data.begin())(structSize);
- BOOST_CHECK_EQUAL(structSize, Codec::size(dp) + 2); // +2 for code
- BOOST_CHECK_EQUAL(structSize, data.size()-4); // encoded body
-
- BOOST_CHECK_EQUAL(data.size(), Codec::size(s));
- Struct32 s2;
- Codec::decode(data.begin())(s2);
- message::DeliveryProperties* dp2 = s2.getIf<message::DeliveryProperties>();
- BOOST_REQUIRE(dp2);
- BOOST_CHECK_EQUAL(dp2->priority, message::MEDIUM);
- BOOST_CHECK_EQUAL(dp2->routingKey, "foo");
-}
-
-QPID_AUTO_TEST_CASE(testStruct32Unknown) {
- // Verify we can recode an unknown struct unchanged.
- Struct32 s;
- string data;
- Codec::encode(back_inserter(data))(uint32_t(10));
- data.append(10, 'X');
- Codec::decode(data.begin())(s);
- string data2;
- Codec::encode(back_inserter(data2))(s);
- BOOST_CHECK_EQUAL(data.size(), data2.size());
- BOOST_CHECK_EQUAL(data, data2);
-}
-
-struct DummyPacked {
- static const uint8_t PACK=1;
- boost::optional<char> i, j;
- char k;
- Bit l,m;
- DummyPacked(char a=0, char b=0, char c=0) : i(a), j(b), k(c), l(), m() {}
- template <class S> void serialize(S& s) { s(i)(j)(k)(l)(m); }
-};
-
-Packer<DummyPacked> serializable(DummyPacked& d) { return Packer<DummyPacked>(d); }
-
-QPID_AUTO_TEST_CASE(testPackBits) {
- DummyPacked d('a','b','c');
- BOOST_CHECK_EQUAL(packBits(d), 7u);
- d.j = boost::none;
- BOOST_CHECK_EQUAL(packBits(d), 5u);
- d.m = true;
- BOOST_CHECK_EQUAL(packBits(d), 0x15u);
-}
-
-
-QPID_AUTO_TEST_CASE(testPacked) {
- string data;
-
- Codec::encode(back_inserter(data))('a')(boost::optional<char>('b'))(boost::optional<char>())('c');
- BOOST_CHECK_EQUAL(data, "abc");
- data.clear();
-
- DummyPacked dummy('a','b','c');
-
- Codec::encode(back_inserter(data))(dummy);
- BOOST_CHECK_EQUAL(data.size(), 4u);
- BOOST_CHECK_EQUAL(data, string("\007abc"));
- data.clear();
-
- dummy.i = boost::none;
- Codec::encode(back_inserter(data))(dummy);
- BOOST_CHECK_EQUAL(data, string("\6bc"));
- data.clear();
-
- const char* missing = "\5xy";
- Codec::decode(missing)(dummy);
- BOOST_CHECK(dummy.i);
- BOOST_CHECK_EQUAL(*dummy.i, 'x');
- BOOST_CHECK(!dummy.j);
- BOOST_CHECK_EQUAL(dummy.k, 'y');
-}
-
-QPID_AUTO_TEST_CASE(testUnitControl) {
- string data;
- Control::Holder h(in_place<connection::Tune>(1,2,3,4));
- Codec::encode(std::back_inserter(data))(h);
-
- Unit unit(FrameHeader(FIRST_FRAME|LAST_FRAME, CONTROL));
- Codec::decode(data.begin())(unit);
-
- BOOST_REQUIRE(unit.get<ControlHolder>());
-
- string data2;
- Codec::encode(back_inserter(data2))(unit);
-
- BOOST_CHECK_EQUAL(data, data2);
-}
-
-QPID_AUTO_TEST_CASE(testArray) {
- ArrayDomain<char> a;
- a.resize(3, 'x');
- string data;
- Codec::encode(back_inserter(data))(a);
-
- ArrayDomain<char> b;
- Codec::decode(data.begin())(b);
- BOOST_CHECK_EQUAL(b.size(), 3u);
- string data3;
- Codec::encode(back_inserter(data3))(a);
- BOOST_CHECK_EQUAL(data, data3);
-
- Array x;
- Codec::decode(data.begin())(x);
- BOOST_CHECK_EQUAL(x.size(), 3u);
- BOOST_CHECK_EQUAL(x[0].size(), 1u);
- BOOST_CHECK_EQUAL(*x[0].begin(), 'x');
- BOOST_CHECK_EQUAL(*x[2].begin(), 'x');
-
- string data2;
- Codec::encode(back_inserter(data2))(x);
- BOOST_CHECK_EQUAL(data,data2);
-}
-
-QPID_AUTO_TEST_CASE(testStruct) {
- string data;
-
- message::DeliveryProperties dp;
- BOOST_CHECK(!dp.discardUnroutable);
- dp.immediate = true;
- dp.redelivered = false;
- dp.priority = message::MEDIUM;
- dp.exchange = "foo";
-
- Codec::encode(back_inserter(data))(dp);
- // Skip 4 bytes size, little-endian decode for pack bits.
- uint16_t encodedBits=uint8_t(data[5]);
- encodedBits <<= 8;
- encodedBits += uint8_t(data[4]);
- BOOST_CHECK_EQUAL(encodedBits, packBits(dp));
-
- data.clear();
- Struct32 h(dp);
- Codec::encode(back_inserter(data))(h);
-
- Struct32 h2;
- Codec::decode(data.begin())(h2);
- BOOST_CHECK_EQUAL(h2.getClassCode(), Uint8(message::DeliveryProperties::CLASS_CODE));
- BOOST_CHECK_EQUAL(h2.getCode(), Uint8(message::DeliveryProperties::CODE));
- message::DeliveryProperties* dp2 =
- dynamic_cast<message::DeliveryProperties*>(h2.get());
- BOOST_CHECK(dp2);
- BOOST_CHECK(!dp2->discardUnroutable);
- BOOST_CHECK(dp2->immediate);
- BOOST_CHECK(!dp2->redelivered);
- BOOST_CHECK_EQUAL(dp2->priority, message::MEDIUM);
- BOOST_CHECK_EQUAL(dp2->exchange, "foo");
-}
-
-struct RecodeUnit {
- template <class T>
- void operator() (const T& t) {
- BOOST_MESSAGE(BOOST_CURRENT_FUNCTION << " called with: " << t);
- using qpid::framing::Buffer;
- using qpid::framing::AMQFrame;
-
- session::Header sh;
- BOOST_CHECK_EQUAL(Codec::size(sh), 2u);
-
- // Encode unit.
- Unit u(t);
- string data;
- Codec::encode(back_inserter(data))(u.getHeader())(u);
- data.push_back(char(0xCE)); // Preview end-of-frame
-
- // Decode AMQFrame
- Buffer buf(&data[0], data.size());
- AMQFrame f;
- f.decode(buf);
- BOOST_MESSAGE("AMQFrame decoded: " << f);
- // Encode AMQFrame
- string data2(f.size(), ' ');
- Buffer buf2(&data2[0], data.size());
- f.encode(buf2);
-
- // Verify encoded by unit == encoded by AMQFrame
- BOOST_CHECK_MESSAGE(data == data2, BOOST_CURRENT_FUNCTION);
-
- // Decode unit
- // FIXME aconway 2008-04-15: must set limit to decode a header.
- Codec::Decoder<string::iterator> decode(data2.begin(), data2.size()-1);
-
- FrameHeader h;
- decode(h);
- BOOST_CHECK_EQUAL(u.getHeader(), h);
- Unit u2(h);
- decode(u2);
-
- // Re-encode unit
- string data3;
- Codec::encode(back_inserter(data3))(u2.getHeader())(u2);
- data3.push_back(char(0xCE)); // Preview end-of-frame
-
- BOOST_CHECK_MESSAGE(data3 == data2, BOOST_CURRENT_FUNCTION);
- }
-};
-
-QPID_AUTO_TEST_CASE(testSerializeAllSegmentTypes) {
- RecodeUnit recode;
- allSegmentTypes(recode);
-}
-
-QPID_AUTO_TEST_SUITE_END()
diff --git a/qpid/cpp/src/tests/brokertest.py b/qpid/cpp/src/tests/brokertest.py
index 3207a51b79..aea4460e5a 100644
--- a/qpid/cpp/src/tests/brokertest.py
+++ b/qpid/cpp/src/tests/brokertest.py
@@ -76,18 +76,20 @@ def error_line(filename, n=1):
except: return ""
return ":\n" + "".join(result)
-def retry(function, timeout=10, delay=.01):
- """Call function until it returns True or timeout expires.
- Double the delay for each retry. Return True if function
- returns true, False if timeout expires."""
+def retry(function, timeout=10, delay=.01, max_delay=1):
+ """Call function until it returns a true value or timeout expires.
+ Double the delay for each retry up to max_delay.
+ Returns what function returns if true, None if timeout expires."""
deadline = time.time() + timeout
- while not function():
+ ret = None
+ while True:
+ ret = function()
+ if ret: return ret
remaining = deadline - time.time()
if remaining <= 0: return False
delay = min(delay, remaining)
time.sleep(delay)
- delay *= 2
- return True
+ delay = min(delay*2, max_delay)
class AtomicCounter:
def __init__(self):
@@ -239,15 +241,13 @@ def find_in_file(str, filename):
class Broker(Popen):
"A broker process. Takes care of start, stop and logging."
_broker_count = 0
+ _log_count = 0
- def __str__(self): return "Broker<%s %s>"%(self.name, self.pname)
+ def __str__(self): return "Broker<%s %s :%d>"%(self.log, self.pname, self.port())
def find_log(self):
- self.log = "%s.log" % self.name
- i = 1
- while (os.path.exists(self.log)):
- self.log = "%s-%d.log" % (self.name, i)
- i += 1
+ self.log = "%03d:%s.log" % (Broker._log_count, self.name)
+ Broker._log_count += 1
def get_log(self):
return os.path.abspath(self.log)
@@ -298,9 +298,9 @@ class Broker(Popen):
# Read port from broker process stdout if not already read.
if (self._port == 0):
try: self._port = int(self.stdout.readline())
- except ValueError:
- raise Exception("Can't get port for broker %s (%s)%s" %
- (self.name, self.pname, error_line(self.log,5)))
+ except ValueError, e:
+ raise Exception("Can't get port for broker %s (%s)%s: %s" %
+ (self.name, self.pname, error_line(self.log,5), e))
return self._port
def unexpected(self,msg):
@@ -436,6 +436,35 @@ class Cluster:
def __getitem__(self,index): return self._brokers[index]
def __iter__(self): return self._brokers.__iter__()
+
+def browse(session, queue, timeout=0, transform=lambda m: m.content):
+ """Return a list with the contents of each message on queue."""
+ r = session.receiver("%s;{mode:browse}"%(queue))
+ r.capacity = 100
+ try:
+ contents = []
+ try:
+ while True: contents.append(transform(r.fetch(timeout=timeout)))
+ except messaging.Empty: pass
+ finally: r.close()
+ return contents
+
+def assert_browse(session, queue, expect_contents, timeout=0, transform=lambda m: m.content, msg="browse failed"):
+ """Assert that the contents of messages on queue (as retrieved
+ using session and timeout) exactly match the strings in
+ expect_contents"""
+ actual_contents = browse(session, queue, timeout, transform=transform)
+ if msg: msg = "%s: %r != %r"%(msg, expect_contents, actual_contents)
+ assert expect_contents == actual_contents, msg
+
+def assert_browse_retry(session, queue, expect_contents, timeout=1, delay=.01, transform=lambda m:m.content, msg="browse failed"):
+ """Wait up to timeout for contents of queue to match expect_contents"""
+ test = lambda: browse(session, queue, 0, transform=transform) == expect_contents
+ retry(test, timeout, delay)
+ actual_contents = browse(session, queue, 0, transform=transform)
+ if msg: msg = "%s: %r != %r"%(msg, expect_contents, actual_contents)
+ assert expect_contents == actual_contents, msg
+
class BrokerTest(TestCase):
"""
Tracks processes started by test and kills at end of test.
@@ -501,30 +530,9 @@ class BrokerTest(TestCase):
cluster = Cluster(self, count, args, expect=expect, wait=wait, show_cmd=show_cmd)
return cluster
- def browse(self, session, queue, timeout=0, transform=lambda m: m.content):
- """Return a list with the contents of each message on queue."""
- r = session.receiver("%s;{mode:browse}"%(queue))
- r.capacity = 100
- try:
- contents = []
- try:
- while True: contents.append(transform(r.fetch(timeout=timeout)))
- except messaging.Empty: pass
- finally: r.close()
- return contents
-
- def assert_browse(self, session, queue, expect_contents, timeout=0, transform=lambda m: m.content):
- """Assert that the contents of messages on queue (as retrieved
- using session and timeout) exactly match the strings in
- expect_contents"""
- actual_contents = self.browse(session, queue, timeout, transform=transform)
- self.assertEqual(expect_contents, actual_contents)
-
- def assert_browse_retry(self, session, queue, expect_contents, timeout=1, delay=.01, transform=lambda m:m.content):
- """Wait up to timeout for contents of queue to match expect_contents"""
- test = lambda: self.browse(session, queue, 0, transform=transform) == expect_contents
- retry(test, timeout, delay)
- self.assertEqual(expect_contents, self.browse(session, queue, 0, transform=transform))
+ def browse(self, *args, **kwargs): browse(*args, **kwargs)
+ def assert_browse(self, *args, **kwargs): assert_browse(*args, **kwargs)
+ def assert_browse_retry(self, *args, **kwargs): assert_browse_retry(*args, **kwargs)
def join(thread, timeout=10):
thread.join(timeout)
@@ -564,7 +572,7 @@ class NumberedSender(Thread):
"""
Thread.__init__(self)
cmd = ["qpid-send",
- "--broker", url or broker.host_port(),
+ "--broker", url or broker.host_port(),
"--address", "%s;{create:always}"%queue,
"--connection-options", "{%s}"%(connection_options),
"--content-stdin"
@@ -639,6 +647,7 @@ class NumberedReceiver(Thread):
self.error = None
self.sender = sender
self.received = 0
+ self.queue = queue
def read_message(self):
n = int(self.receiver.stdout.readline())
@@ -649,7 +658,7 @@ class NumberedReceiver(Thread):
m = self.read_message()
while m != -1:
self.receiver.assert_running()
- assert(m <= self.received) # Check for missing messages
+ assert m <= self.received, "%s missing message %s>%s"%(self.queue, m, self.received)
if (m == self.received): # Ignore duplicates
self.received += 1
if self.sender:
diff --git a/qpid/cpp/src/tests/cli_tests.py b/qpid/cpp/src/tests/cli_tests.py
index b9a7dda15c..7ac5b1deed 100755
--- a/qpid/cpp/src/tests/cli_tests.py
+++ b/qpid/cpp/src/tests/cli_tests.py
@@ -330,6 +330,9 @@ class CliTests(TestBase010):
ret = os.system(self.qpid_config_command(" add queue %s --alternate-exchange=%s" % (qName, altName)))
self.assertEqual(ret, 0)
+ ret = os.system(self.qpid_config_command(" queues"))
+ self.assertEqual(ret, 0)
+
queues = self.broker_access.getAllQueues()
found = False
for queue in queues:
diff --git a/qpid/cpp/src/tests/cluster.cmake b/qpid/cpp/src/tests/cluster.cmake
index 3471173e97..31e2d337d1 100644
--- a/qpid/cpp/src/tests/cluster.cmake
+++ b/qpid/cpp/src/tests/cluster.cmake
@@ -55,7 +55,7 @@ add_test (clustered_replication_test ${CMAKE_CURRENT_SOURCE_DIR}/clustered_repli
# CLEANFILES += cluster_test.acl cluster.ports
# EXTRA_DIST += \
-# ais_check \
+# cpg_check.sh.in \
# run_cluster_test \
# cluster_read_credit \
# test_watchdog \
diff --git a/qpid/cpp/src/tests/cluster.mk b/qpid/cpp/src/tests/cluster.mk
index 199d1e7b57..852b2dda8c 100644
--- a/qpid/cpp/src/tests/cluster.mk
+++ b/qpid/cpp/src/tests/cluster.mk
@@ -25,12 +25,11 @@
CLUSTER_TEST_SCRIPTS_LIST= \
allhosts rsynchosts \
qpid-build-rinstall qpid-src-rinstall \
- qpid-test-cluster \
- qpid-cluster-benchmark
+ qpid-test-cluster
EXTRA_DIST += \
$(CLUSTER_TEST_SCRIPTS_LIST) \
- ais_check \
+ cpg_check.sh.in \
run_cluster_test \
cluster_read_credit \
test_watchdog \
@@ -55,14 +54,12 @@ if HAVE_LIBCPG
#
# Cluster tests makefile fragment, to be included in Makefile.am
-#
+#
# NOTE: Programs using the openais library must be run with gid=ais
# You should do "newgrp ais" before running the tests to run these.
-#
-
+#
-# ais_check checks pre-requisites for cluster tests and runs them if ok.
TESTS += \
run_cluster_test \
cluster_read_credit \
diff --git a/qpid/cpp/src/tests/cluster_failover b/qpid/cpp/src/tests/cluster_failover
index c978ee910c..43170c731a 100755
--- a/qpid/cpp/src/tests/cluster_failover
+++ b/qpid/cpp/src/tests/cluster_failover
@@ -1,6 +1,6 @@
#!/bin/sh
# A simple manual failover test, sends a stream of numbered messages.
-# You can kill the connected broker and verify that the client reconnects
+# You can kill the connected broker and verify that the clients reconnect
# and no messages are lost.
URL=$1
@@ -11,8 +11,9 @@ echo $SEND $RECV
seq 1000000 > $SEND
-qpid-receive -f -a 'q;{create:always}' -b $URL --connection-options "{reconnect:true}" | tee $RECV &
-
-qpid-send -a 'q;{create:always}' -b $URL --connection-options "{reconnect:true}" --send-rate 10 --content-stdin < $SEND &
+qpid-send -a 'cluster_failover;{create:always}' -b $URL --connection-options "{reconnect:true}" --send-rate 10 --content-stdin < $SEND &
+while msg=$(qpid-receive -m1 -f -a 'cluster_failover;{create:always}' -b $URL --connection-options "{reconnect:true,heartbeat:1}"); do
+ echo -n $msg; date
+done
wait
diff --git a/qpid/cpp/src/tests/cluster_python_tests b/qpid/cpp/src/tests/cluster_python_tests
index 8e17ffc8bc..25c7889246 100755
--- a/qpid/cpp/src/tests/cluster_python_tests
+++ b/qpid/cpp/src/tests/cluster_python_tests
@@ -20,7 +20,8 @@
#
# Skip if cluster services not running.
-. `dirname $0`/ais_check
+. cpg_check.sh
+cpg_enabled || exit 0
FAILING=`dirname $0`/cluster_python_tests_failing.txt
source `dirname $0`/python_tests
diff --git a/qpid/cpp/src/tests/cluster_read_credit b/qpid/cpp/src/tests/cluster_read_credit
index fb3b72fbaf..552ffee53b 100755
--- a/qpid/cpp/src/tests/cluster_read_credit
+++ b/qpid/cpp/src/tests/cluster_read_credit
@@ -21,7 +21,9 @@
# Regression test for http://issues.apache.org/jira/browse/QPID-2086
srcdir=`dirname $0`
-. $srcdir/ais_check
+source cpg_check.sh
+cpg_enabled || exit 0
+
$srcdir/start_cluster 1 --cluster-read-max=2 || exit 1
trap $srcdir/stop_cluster EXIT
seq 1 10000 | ./sender --port `cat cluster.ports` --routing-key no-such-queue
diff --git a/qpid/cpp/src/tests/cluster_test_logs.py b/qpid/cpp/src/tests/cluster_test_logs.py
index 3c7e8e8020..003d82c619 100755
--- a/qpid/cpp/src/tests/cluster_test_logs.py
+++ b/qpid/cpp/src/tests/cluster_test_logs.py
@@ -60,11 +60,13 @@ def filter_log(log):
'task late',
'task overran',
'warning CLOSING .* unsent data',
- 'Inter-broker link ',
+ 'Inter-broker link ', # ignore link state changes
+ 'Updated link key from ', # ignore link state changes
'Running in a cluster, marking store',
'debug Sending keepalive signal to watchdog', # Watchdog timer thread
'last broker standing joined by 1 replicas, updating queue policies.',
- 'Connection .* timed out: closing' # heartbeat connection close
+ 'Connection .* timed out: closing', # heartbeat connection close
+ "org.apache.qpid.broker:bridge:" # ignore bridge index
])
# Regex to match a UUID
uuid='\w\w\w\w\w\w\w\w-\w\w\w\w-\w\w\w\w-\w\w\w\w-\w\w\w\w\w\w\w\w\w\w\w\w'
diff --git a/qpid/cpp/src/tests/cluster_tests.py b/qpid/cpp/src/tests/cluster_tests.py
index 79df21aada..3c96b252df 100755
--- a/qpid/cpp/src/tests/cluster_tests.py
+++ b/qpid/cpp/src/tests/cluster_tests.py
@@ -227,6 +227,18 @@ acl deny all all
self.assertEqual("x", cluster[0].get_message("q").content)
self.assertEqual("y", cluster[1].get_message("q").content)
+ def test_other_mech(self):
+ """Test using a mechanism other than PLAIN/ANONYMOUS for cluster update authentication.
+ Regression test for https://issues.apache.org/jira/browse/QPID-3849"""
+ sasl_config=os.path.join(self.rootdir, "sasl_config")
+ cluster = self.cluster(2, args=["--auth", "yes", "--sasl-config", sasl_config,
+ "--cluster-username=zig",
+ "--cluster-password=zig",
+ "--cluster-mechanism=DIGEST-MD5"])
+ cluster[0].connect()
+ cluster.start() # Before the fix this broker falied to join the cluster.
+ cluster[2].connect()
+
def test_link_events(self):
"""Regression test for https://bugzilla.redhat.com/show_bug.cgi?id=611543"""
args = ["--mgmt-pub-interval", 1] # Publish management information every second.
@@ -767,6 +779,223 @@ acl deny all all
cluster.start()
fetch(cluster[2])
+
+ def _verify_federation(self, src_broker, src, dst_broker, dst, timeout=30):
+ """ Prove that traffic can pass between two federated brokers.
+ """
+ tot_time = 0
+ active = False
+ send_session = src_broker.connect().session()
+ sender = send_session.sender(src)
+ receive_session = dst_broker.connect().session()
+ receiver = receive_session.receiver(dst)
+ while not active and tot_time < timeout:
+ sender.send(Message("Hello from Source!"))
+ try:
+ receiver.fetch(timeout = 1)
+ receive_session.acknowledge()
+ # Get this far without Empty exception, and the link is good!
+ active = True
+ while True:
+ # Keep receiving msgs, as several may have accumulated
+ receiver.fetch(timeout = 1)
+ receive_session.acknowledge()
+ except Empty:
+ if not active:
+ tot_time += 1
+ receiver.close()
+ receive_session.close()
+ sender.close()
+ send_session.close()
+ return active
+
+ def test_federation_failover(self):
+ """
+ Verify that federation operates across failures occuring in a cluster.
+ Specifically:
+ 1) Destination cluster learns of membership changes in the source
+ cluster
+ 2) Destination cluster replicates the current state of the source
+ cluster to newly-added members
+ """
+
+ # 2 node cluster source, 2 node cluster destination
+ src_cluster = self.cluster(2, expect=EXPECT_EXIT_FAIL)
+ src_cluster.ready();
+ dst_cluster = self.cluster(2, expect=EXPECT_EXIT_FAIL)
+ dst_cluster.ready();
+
+ cmd = self.popen(["qpid-config",
+ "--broker", src_cluster[0].host_port(),
+ "add", "queue", "srcQ"], EXPECT_EXIT_OK)
+ cmd.wait()
+
+ cmd = self.popen(["qpid-config",
+ "--broker", dst_cluster[0].host_port(),
+ "add", "exchange", "fanout", "destX"], EXPECT_EXIT_OK)
+ cmd.wait()
+
+ cmd = self.popen(["qpid-config",
+ "--broker", dst_cluster[0].host_port(),
+ "add", "queue", "destQ"], EXPECT_EXIT_OK)
+ cmd.wait()
+
+ cmd = self.popen(["qpid-config",
+ "--broker", dst_cluster[0].host_port(),
+ "bind", "destX", "destQ"], EXPECT_EXIT_OK)
+ cmd.wait()
+
+ # federate the srcQ to the destination exchange
+ dst_cluster[0].startQmf()
+ dst_broker = dst_cluster[0].qmf_session.getObjects(_class="broker")[0]
+ result = dst_broker.connect(src_cluster[0].host(), src_cluster[0].port(), False, "PLAIN",
+ "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0, result);
+
+ link = dst_cluster[0].qmf_session.getObjects(_class="link")[0]
+ result = link.bridge(False, "srcQ", "destX", "", "", "", True, False, False, 10)
+ self.assertEqual(result.status, 0, result)
+
+ # check that traffic passes
+ assert self._verify_federation(src_cluster[0], "srcQ", dst_cluster[0], "destQ")
+
+ # add src[2] broker to source cluster
+ src_cluster.start(expect=EXPECT_EXIT_FAIL);
+ src_cluster.ready();
+ assert self._verify_federation(src_cluster[2], "srcQ", dst_cluster[0], "destQ")
+
+ # Kill src[0]. dst[0] should fail over to src[1]
+ src_cluster[0].kill()
+ for b in src_cluster[1:]: b.ready()
+ assert self._verify_federation(src_cluster[1], "srcQ", dst_cluster[0], "destQ")
+
+ # Kill src[1], dst[0] should fail over to src[2]
+ src_cluster[1].kill()
+ for b in src_cluster[2:]: b.ready()
+ assert self._verify_federation(src_cluster[2], "srcQ", dst_cluster[0], "destQ")
+
+ # Kill dest[0], force failover to dest[1]
+ dst_cluster[0].kill()
+ for b in dst_cluster[1:]: b.ready()
+ assert self._verify_federation(src_cluster[2], "srcQ", dst_cluster[1], "destQ")
+
+ # Add dest[2]
+ # dest[1] syncs dest[2] to current remote state
+ dst_cluster.start(expect=EXPECT_EXIT_FAIL);
+ for b in dst_cluster[1:]: b.ready()
+ assert self._verify_federation(src_cluster[2], "srcQ", dst_cluster[1], "destQ")
+
+ # Kill dest[1], force failover to dest[2]
+ dst_cluster[1].kill()
+ for b in dst_cluster[2:]: b.ready()
+ assert self._verify_federation(src_cluster[2], "srcQ", dst_cluster[2], "destQ")
+
+ for i in range(2, len(src_cluster)): src_cluster[i].kill()
+ for i in range(2, len(dst_cluster)): dst_cluster[i].kill()
+
+
+ def test_federation_multilink_failover(self):
+ """
+ Verify that multi-link federation operates across failures occuring in
+ a cluster.
+ """
+
+ # 1 node cluster source, 1 node cluster destination
+ src_cluster = self.cluster(1, expect=EXPECT_EXIT_FAIL)
+ src_cluster.ready();
+ dst_cluster = self.cluster(1, expect=EXPECT_EXIT_FAIL)
+ dst_cluster.ready();
+
+ # federate a direct binding across two separate links
+
+ # first, create a direct exchange bound to two queues using different
+ # bindings
+ cmd = self.popen(["qpid-config",
+ "--broker", src_cluster[0].host_port(),
+ "add", "exchange", "direct", "FedX"],
+ EXPECT_EXIT_OK)
+ cmd.wait()
+
+ cmd = self.popen(["qpid-config",
+ "--broker", dst_cluster[0].host_port(),
+ "add", "exchange", "direct", "FedX"],
+ EXPECT_EXIT_OK)
+ cmd.wait()
+
+ cmd = self.popen(["qpid-config",
+ "--broker", dst_cluster[0].host_port(),
+ "add", "queue", "destQ1"],
+ EXPECT_EXIT_OK)
+ cmd.wait()
+
+ cmd = self.popen(["qpid-config",
+ "--broker", dst_cluster[0].host_port(),
+ "bind", "FedX", "destQ1", "one"],
+ EXPECT_EXIT_OK)
+ cmd.wait()
+
+ cmd = self.popen(["qpid-config",
+ "--broker", dst_cluster[0].host_port(),
+ "add", "queue", "destQ2"],
+ EXPECT_EXIT_OK)
+ cmd.wait()
+
+ cmd = self.popen(["qpid-config",
+ "--broker", dst_cluster[0].host_port(),
+ "bind", "FedX", "destQ2", "two"],
+ EXPECT_EXIT_OK)
+ cmd.wait()
+
+ # Create two separate links between the dst and source brokers, bind
+ # each to different keys
+ dst_cluster[0].startQmf()
+ dst_broker = dst_cluster[0].qmf_session.getObjects(_class="broker")[0]
+
+ for _l in [("link1", "bridge1", "one"),
+ ("link2", "bridge2", "two")]:
+ result = dst_broker.create("link", _l[0],
+ {"host":src_cluster[0].host(),
+ "port":src_cluster[0].port()},
+ False)
+ self.assertEqual(result.status, 0, result);
+ result = dst_broker.create("bridge", _l[1],
+ {"link":_l[0],
+ "src":"FedX",
+ "dest":"FedX",
+ "key":_l[2]}, False)
+ self.assertEqual(result.status, 0);
+
+ # check that traffic passes
+ assert self._verify_federation(src_cluster[0], "FedX/one", dst_cluster[0], "destQ1")
+ assert self._verify_federation(src_cluster[0], "FedX/two", dst_cluster[0], "destQ2")
+
+ # add new member, verify traffic
+ src_cluster.start(expect=EXPECT_EXIT_FAIL);
+ src_cluster.ready();
+
+ dst_cluster.start(expect=EXPECT_EXIT_FAIL);
+ dst_cluster.ready();
+
+ assert self._verify_federation(src_cluster[0], "FedX/one", dst_cluster[0], "destQ1")
+ assert self._verify_federation(src_cluster[0], "FedX/two", dst_cluster[0], "destQ2")
+
+ src_cluster[0].kill()
+ for b in src_cluster[1:]: b.ready()
+
+ assert self._verify_federation(src_cluster[1], "FedX/one", dst_cluster[0], "destQ1")
+ assert self._verify_federation(src_cluster[1], "FedX/two", dst_cluster[0], "destQ2")
+
+ dst_cluster[0].kill()
+ for b in dst_cluster[1:]: b.ready()
+
+ assert self._verify_federation(src_cluster[1], "FedX/one", dst_cluster[1], "destQ1")
+ assert self._verify_federation(src_cluster[1], "FedX/two", dst_cluster[1], "destQ2")
+
+ for i in range(1, len(src_cluster)): src_cluster[i].kill()
+ for i in range(1, len(dst_cluster)): dst_cluster[i].kill()
+
+
+
# Some utility code for transaction tests
XA_RBROLLBACK = 1
XA_RBTIMEOUT = 2
diff --git a/qpid/cpp/src/tests/clustered_replication_test b/qpid/cpp/src/tests/clustered_replication_test
index 8c8522c2eb..5a9f143eb4 100755
--- a/qpid/cpp/src/tests/clustered_replication_test
+++ b/qpid/cpp/src/tests/clustered_replication_test
@@ -8,9 +8,9 @@
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -34,11 +34,11 @@ stop_brokers() {
if [[ $PRIMARY1 ]] ; then
$QPIDD_EXEC --no-module-dir -q --port $PRIMARY1
unset PRIMARY1
- fi
+ fi
if [[ $PRIMARY2 ]] ; then
$QPIDD_EXEC --no-module-dir -q --port $PRIMARY2
unset PRIMARY2
- fi
+ fi
if [[ $DR1 ]] ; then
$QPIDD_EXEC --no-module-dir -q --port $DR1
unset DR1
@@ -50,7 +50,8 @@ stop_brokers() {
}
if test -d $PYTHON_DIR; then
- . $srcdir/ais_check
+ . cpg_check.sh
+ cpg_enabled || exit 0
#todo: these cluster names need to be unique to prevent clashes
PRIMARY_CLUSTER=PRIMARY_$(hostname)_$$
@@ -89,20 +90,20 @@ if test -d $PYTHON_DIR; then
#send more messages to primary
for i in `seq 11 20`; do echo Message$i; done | ./sender --port $PRIMARY1 --send-eos 1
- #wait for replication events to all be processed:
+ #wait for replication events to all be processed:
echo Waiting for replication to complete
echo Done | ./sender --port $PRIMARY1 --routing-key control-queue --send-eos 1
./receiver --queue control-queue --port $DR1 > /dev/null
#verify contents of test queue on dr cluster:
- echo Verifying...
+ echo Verifying...
./receiver --port $DR2 > repl.out.tmp
for i in `seq 6 20`; do echo Message$i; done | diff repl.out.tmp - || FAIL=1
if [[ $FAIL ]]; then
echo Clustered replication test failed: expectations not met!
exit 1
- else
+ else
echo Clustered replication test passed
rm -f repl*.tmp
fi
diff --git a/qpid/cpp/src/tests/ais_check b/qpid/cpp/src/tests/cpg_check.sh.in
index a865260543..ed97776218 100755
--- a/qpid/cpp/src/tests/ais_check
+++ b/qpid/cpp/src/tests/cpg_check.sh.in
@@ -7,9 +7,9 @@
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -18,12 +18,16 @@
# under the License.
#
-srcdir=`dirname $0`
+QPID_USE_CPG=${QPID_USE_CPG:-@USE_CPG@}
-# Check AIS requirements and run tests if found.
-ps -u root | grep 'aisexec\|corosync' >/dev/null || {
- echo WARNING: Skipping cluster tests, the aisexec or corosync daemon is not running.
- exit 0; # A warning, not a failure.
+# Check if CPG is enabled
+cpg_enabled() {
+ test x$QPID_USE_CPG = xyes || return 1 # disabled
+ ps -u root | grep 'aisexec\|corosync' >/dev/null || {
+ echo WARNING: Skip cluster tests, aisexec or corosync daemon is not running.
+ return 1; # A warning, not a failure.
+ }
+ return 0
}
# Execute command with the ais group set if user is a member.
diff --git a/qpid/cpp/src/tests/federated_cluster_test b/qpid/cpp/src/tests/federated_cluster_test
index 50b877e666..f42b7501b8 100755
--- a/qpid/cpp/src/tests/federated_cluster_test
+++ b/qpid/cpp/src/tests/federated_cluster_test
@@ -8,9 +8,9 @@
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -35,7 +35,7 @@ stop_brokers() {
if [[ $BROKER_A ]] ; then
../qpidd --no-module-dir -q --port $BROKER_A
unset BROKER_A
- fi
+ fi
if [[ $NODE_1 ]] ; then
../qpidd --no-module-dir -q --port $NODE_1
unset NODE_1
@@ -92,7 +92,7 @@ run_test_pull_to_cluster_two_consumers() {
wait
sort -g -k 2 fed1.out.tmp fed2.out.tmp > fed.out.tmp
diff fed.in.tmp fed.out.tmp || fail "federated link to cluster failed: expectations not met!"
-
+
rm -f fed*.tmp #cleanup
}
@@ -106,7 +106,7 @@ run_test_pull_to_cluster() {
#verify all messages are received
diff fed.in.tmp fed.out.tmp || fail "federated link to cluster failed: expectations not met!"
-
+
rm -f fed*.tmp #cleanup
}
@@ -121,22 +121,23 @@ run_test_pull_from_cluster() {
#verify all messages are received
wait
diff fed.in.tmp fed.out.tmp || fail "federated link from cluster failed: expectations not met!"
-
+
rm -f fed*.tmp #cleanup
}
if test -d ${PYTHON_DIR}; then
- . $srcdir/ais_check
+ . cpg_check.sh
+ cpg_enabled || exit 0
rm -f fed*.tmp #cleanup any files left from previous run
start_brokers
echo "brokers started"
setup
echo "setup completed"
- run_test_pull_to_cluster_two_consumers
+ run_test_pull_to_cluster_two_consumers
echo "federated link to cluster verified"
- run_test_pull_from_cluster
+ run_test_pull_from_cluster
echo "federated link from cluster verified"
if [[ $TEST_NODE_FAILURE ]] ; then
#kill first cluster node and retest
@@ -146,7 +147,7 @@ if test -d ${PYTHON_DIR}; then
echo "retesting..."
run_test_pull_to_cluster
echo "federated link to cluster verified"
- run_test_pull_from_cluster
- echo "federated link from cluster verified"
+ run_test_pull_from_cluster
+ echo "federated link from cluster verified"
fi
fi
diff --git a/qpid/cpp/src/tests/federation.py b/qpid/cpp/src/tests/federation.py
index 7d613b98ce..dcd074eda9 100755
--- a/qpid/cpp/src/tests/federation.py
+++ b/qpid/cpp/src/tests/federation.py
@@ -23,6 +23,7 @@ from qpid.testlib import TestBase010
from qpid.datatypes import Message
from qpid.queue import Empty
from qpid.util import URL
+import qpid.messaging
from time import sleep, time
@@ -94,6 +95,11 @@ class FederationTests(TestBase010):
break
self._brokers.append(_b)
+ # add a new-style messaging connection to each broker
+ for _b in self._brokers:
+ _b.connection = qpid.messaging.Connection(_b.url)
+ _b.connection.open()
+
def _teardown_brokers(self):
""" Un-does _setup_brokers()
"""
@@ -103,7 +109,7 @@ class FederationTests(TestBase010):
if not _b.client_session.error():
_b.client_session.close(timeout=10)
_b.client_conn.close(timeout=10)
-
+ _b.connection.close()
def test_bridge_create_and_close(self):
self.startQmf();
@@ -127,18 +133,28 @@ class FederationTests(TestBase010):
self.verify_cleanup()
def test_pull_from_exchange(self):
+ """ This test uses an alternative method to manage links and bridges
+ via the broker object.
+ """
session = self.session
-
+
self.startQmf()
qmf = self.qmf
broker = qmf.getObjects(_class="broker")[0]
- result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
- self.assertEqual(result.status, 0, result)
- link = qmf.getObjects(_class="link")[0]
- result = link.bridge(False, "amq.direct", "amq.fanout", "my-key", "", "", False, False, False, 0)
+ # create link
+ link_args = {"host":self.remote_host(), "port":self.remote_port(), "durable":False,
+ "authMechanism":"PLAIN", "username":"guest", "password":"guest",
+ "transport":"tcp"}
+ result = broker.create("link", "test-link-1", link_args, False)
self.assertEqual(result.status, 0, result)
+ link = qmf.getObjects(_class="link")[0]
+ # create bridge
+ bridge_args = {"link":"test-link-1", "src":"amq.direct", "dest":"amq.fanout",
+ "key":"my-key"}
+ result = broker.create("bridge", "test-bridge-1", bridge_args, False);
+ self.assertEqual(result.status, 0, result)
bridge = qmf.getObjects(_class="bridge")[0]
#setup queue to receive messages from local broker
@@ -164,9 +180,11 @@ class FederationTests(TestBase010):
self.fail("Got unexpected message in queue: " + extra.body)
except Empty: None
- result = bridge.close()
+
+ result = broker.delete("bridge", "test-bridge-1", {})
self.assertEqual(result.status, 0, result)
- result = link.close()
+
+ result = broker.delete("link", "test-link-1", {})
self.assertEqual(result.status, 0, result)
self.verify_cleanup()
@@ -376,6 +394,9 @@ class FederationTests(TestBase010):
for i in range(1, 11):
try:
msg = queue.get(timeout=5)
+ mp = msg.get("message_properties").application_headers
+ self.assertEqual(mp.__class__, dict)
+ self.assertEqual(mp['x-qpid.trace'], 'REMOTE') # check that the federation-tag override works
self.assertEqual("Message %d" % i, msg.body)
except Empty:
self.fail("Failed to find expected message containing 'Message %d'" % i)
@@ -2153,3 +2174,433 @@ class FederationTests(TestBase010):
self.verify_cleanup()
+ def test_multilink_direct(self):
+ """ Verify that two distinct links can be created between federated
+ brokers.
+ """
+ self.startQmf()
+ qmf = self.qmf
+ self._setup_brokers()
+ src_broker = self._brokers[0]
+ dst_broker = self._brokers[1]
+
+ # create a direct exchange on each broker
+ for _b in [src_broker, dst_broker]:
+ _b.client_session.exchange_declare(exchange="fedX.direct", type="direct")
+ self.assertEqual(_b.client_session.exchange_query(name="fedX.direct").type,
+ "direct", "exchange_declare failed!")
+
+ # create destination queues
+ for _q in [("HiQ", "high"), ("MedQ", "medium"), ("LoQ", "low")]:
+ dst_broker.client_session.queue_declare(queue=_q[0], auto_delete=True)
+ dst_broker.client_session.exchange_bind(queue=_q[0], exchange="fedX.direct", binding_key=_q[1])
+
+ # create two connections, one for high priority traffic
+ for _q in ["HiPri", "Traffic"]:
+ result = dst_broker.qmf_object.create("link", _q,
+ {"host":src_broker.host,
+ "port":src_broker.port},
+ False)
+ self.assertEqual(result.status, 0);
+
+ links = qmf.getObjects(_broker=dst_broker.qmf_broker, _class="link")
+ for _l in links:
+ if _l.name == "HiPri":
+ hi_link = _l
+ elif _l.name == "Traffic":
+ data_link = _l
+ else:
+ self.fail("Unexpected Link found: " + _l.name)
+
+ # now create a route for messages sent with key "high" to use the
+ # hi_link
+ result = dst_broker.qmf_object.create("bridge", "HiPriBridge",
+ {"link":hi_link.name,
+ "src":"fedX.direct",
+ "dest":"fedX.direct",
+ "key":"high"}, False)
+ self.assertEqual(result.status, 0);
+
+
+ # create routes for the "medium" and "low" links to use the normal
+ # data_link
+ for _b in [("MediumBridge", "medium"), ("LowBridge", "low")]:
+ result = dst_broker.qmf_object.create("bridge", _b[0],
+ {"link":data_link.name,
+ "src":"fedX.direct",
+ "dest":"fedX.direct",
+ "key":_b[1]}, False)
+ self.assertEqual(result.status, 0);
+
+ # now wait for the links to become operational
+ for _l in [hi_link, data_link]:
+ expire_time = time() + 30
+ while _l.state != "Operational" and time() < expire_time:
+ _l.update()
+ self.assertEqual(_l.state, "Operational", "Link failed to become operational")
+
+ # verify each link uses a different connection
+ self.assertNotEqual(hi_link.connectionRef, data_link.connectionRef,
+ "Different links using the same connection")
+
+ hi_conn = qmf.getObjects(_broker=dst_broker.qmf_broker,
+ _objectId=hi_link.connectionRef)[0]
+ data_conn = qmf.getObjects(_broker=dst_broker.qmf_broker,
+ _objectId=data_link.connectionRef)[0]
+
+
+ # send hi data, verify only goes over hi link
+
+ r_ssn = dst_broker.connection.session()
+ hi_receiver = r_ssn.receiver("HiQ");
+ med_receiver = r_ssn.receiver("MedQ");
+ low_receiver = r_ssn.receiver("LoQ");
+
+ for _c in [hi_conn, data_conn]:
+ _c.update()
+ self.assertEqual(_c.msgsToClient, 0, "Unexpected messages received")
+
+ s_ssn = src_broker.connection.session()
+ hi_sender = s_ssn.sender("fedX.direct/high")
+ med_sender = s_ssn.sender("fedX.direct/medium")
+ low_sender = s_ssn.sender("fedX.direct/low")
+
+ try:
+ hi_sender.send(qpid.messaging.Message(content="hi priority"))
+ msg = hi_receiver.fetch(timeout=10)
+ r_ssn.acknowledge()
+ self.assertEqual(msg.content, "hi priority");
+ except:
+ self.fail("Hi Pri message failure")
+
+ hi_conn.update()
+ data_conn.update()
+ self.assertEqual(hi_conn.msgsToClient, 1, "Expected 1 hi pri message")
+ self.assertEqual(data_conn.msgsToClient, 0, "Expected 0 data messages")
+
+ # send low and medium, verify it does not go over hi link
+
+ try:
+ med_sender.send(qpid.messaging.Message(content="medium priority"))
+ msg = med_receiver.fetch(timeout=10)
+ r_ssn.acknowledge()
+ self.assertEqual(msg.content, "medium priority");
+ except:
+ self.fail("Medium Pri message failure")
+
+ hi_conn.update()
+ data_conn.update()
+ self.assertEqual(hi_conn.msgsToClient, 1, "Expected 1 hi pri message")
+ self.assertEqual(data_conn.msgsToClient, 1, "Expected 1 data message")
+
+ try:
+ low_sender.send(qpid.messaging.Message(content="low priority"))
+ msg = low_receiver.fetch(timeout=10)
+ r_ssn.acknowledge()
+ self.assertEqual(msg.content, "low priority");
+ except:
+ self.fail("Low Pri message failure")
+
+ hi_conn.update()
+ data_conn.update()
+ self.assertEqual(hi_conn.msgsToClient, 1, "Expected 1 hi pri message")
+ self.assertEqual(data_conn.msgsToClient, 2, "Expected 2 data message")
+
+ # cleanup
+
+ for _b in qmf.getObjects(_broker=dst_broker.qmf_broker,_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+
+ for _l in qmf.getObjects(_broker=dst_broker.qmf_broker,_class="link"):
+ result = _l.close()
+ self.assertEqual(result.status, 0)
+
+ for _q in [("HiQ", "high"), ("MedQ", "medium"), ("LoQ", "low")]:
+ dst_broker.client_session.exchange_unbind(queue=_q[0], exchange="fedX.direct", binding_key=_q[1])
+ dst_broker.client_session.queue_delete(queue=_q[0])
+
+ for _b in [src_broker, dst_broker]:
+ _b.client_session.exchange_delete(exchange="fedX.direct")
+
+ self._teardown_brokers()
+
+ self.verify_cleanup()
+
+
+ def test_multilink_shared_queue(self):
+ """ Verify that two distinct links can be created between federated
+ brokers.
+ """
+ self.startQmf()
+ qmf = self.qmf
+ self._setup_brokers()
+ src_broker = self._brokers[0]
+ dst_broker = self._brokers[1]
+
+ # create a topic exchange on the destination broker
+ dst_broker.client_session.exchange_declare(exchange="fedX.topic", type="topic")
+ self.assertEqual(dst_broker.client_session.exchange_query(name="fedX.topic").type,
+ "topic", "exchange_declare failed!")
+
+ # create a destination queue
+ dst_broker.client_session.queue_declare(queue="destQ", auto_delete=True)
+ dst_broker.client_session.exchange_bind(queue="destQ", exchange="fedX.topic", binding_key="srcQ")
+
+ # create a single source queue
+ src_broker.client_session.queue_declare(queue="srcQ", auto_delete=True)
+
+ # create two connections
+ for _q in ["Link1", "Link2"]:
+ result = dst_broker.qmf_object.create("link", _q,
+ {"host":src_broker.host,
+ "port":src_broker.port},
+ False)
+ self.assertEqual(result.status, 0);
+
+ links = qmf.getObjects(_broker=dst_broker.qmf_broker, _class="link")
+ self.assertEqual(len(links), 2)
+
+ # now create two "parallel" queue routes from the source queue to the
+ # destination exchange.
+ result = dst_broker.qmf_object.create("bridge", "Bridge1",
+ {"link":"Link1",
+ "src":"srcQ",
+ "dest":"fedX.topic",
+ "srcIsQueue": True},
+ False)
+ self.assertEqual(result.status, 0);
+ result = dst_broker.qmf_object.create("bridge", "Bridge2",
+ {"link":"Link2",
+ "src":"srcQ",
+ "dest":"fedX.topic",
+ "srcIsQueue": True},
+ False)
+ self.assertEqual(result.status, 0);
+
+
+ # now wait for the links to become operational
+ for _l in links:
+ expire_time = time() + 30
+ while _l.state != "Operational" and time() < expire_time:
+ _l.update()
+ self.assertEqual(_l.state, "Operational", "Link failed to become operational")
+
+ # verify each link uses a different connection
+ self.assertNotEqual(links[0].connectionRef, links[1].connectionRef,
+ "Different links using the same connection")
+
+ conn1 = qmf.getObjects(_broker=dst_broker.qmf_broker,
+ _objectId=links[0].connectionRef)[0]
+ conn2 = qmf.getObjects(_broker=dst_broker.qmf_broker,
+ _objectId=links[1].connectionRef)[0]
+
+ # verify messages sent to the queue are pulled by each connection
+
+ r_ssn = dst_broker.connection.session()
+ receiver = r_ssn.receiver("destQ");
+
+ for _c in [conn1, conn2]:
+ _c.update()
+ self.assertEqual(_c.msgsToClient, 0, "Unexpected messages received")
+
+ s_ssn = src_broker.connection.session()
+ sender = s_ssn.sender("srcQ")
+
+ try:
+ for x in range(5):
+ sender.send(qpid.messaging.Message(content="hello"))
+ for x in range(5):
+ msg = receiver.fetch(timeout=10)
+ self.assertEqual(msg.content, "hello");
+ r_ssn.acknowledge()
+ except:
+ self.fail("Message failure")
+
+ # expect messages to be split over each connection.
+ conn1.update()
+ conn2.update()
+ self.assertNotEqual(conn1.msgsToClient, 0, "No messages sent")
+ self.assertNotEqual(conn2.msgsToClient, 0, "No messages sent")
+ self.assertEqual(conn2.msgsToClient + conn1.msgsToClient, 5,
+ "Expected 5 messages total")
+
+ for _b in qmf.getObjects(_broker=dst_broker.qmf_broker,_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+
+ for _l in qmf.getObjects(_broker=dst_broker.qmf_broker,_class="link"):
+ result = _l.close()
+ self.assertEqual(result.status, 0)
+
+ dst_broker.client_session.exchange_unbind(queue="destQ", exchange="fedX.topic", binding_key="srcQ")
+ dst_broker.client_session.exchange_delete(exchange="fedX.topic")
+
+ self._teardown_brokers()
+
+ self.verify_cleanup()
+
+
+ def test_dynamic_direct_shared_queue(self):
+ """
+ Route Topology:
+
+ +<--- B1
+ B0 <---+<--- B2
+ +<--- B3
+ """
+ session = self.session
+
+ # create the federation
+
+ self.startQmf()
+ qmf = self.qmf
+
+ self._setup_brokers()
+
+ # create direct exchange on each broker, and retrieve the corresponding
+ # management object for that exchange
+
+ exchanges=[]
+ for _b in self._brokers:
+ _b.client_session.exchange_declare(exchange="fedX.direct", type="direct")
+ self.assertEqual(_b.client_session.exchange_query(name="fedX.direct").type,
+ "direct", "exchange_declare failed!")
+ # pull the exchange out of qmf...
+ retries = 0
+ my_exchange = None
+ while my_exchange is None:
+ objs = qmf.getObjects(_broker=_b.qmf_broker, _class="exchange")
+ for ooo in objs:
+ if ooo.name == "fedX.direct":
+ my_exchange = ooo
+ break
+ if my_exchange is None:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "QMF failed to find new exchange!")
+ sleep(1)
+ exchanges.append(my_exchange)
+
+ self.assertEqual(len(exchanges), len(self._brokers), "Exchange creation failed!")
+
+ # Create 2 links per each source broker (1,2,3) to the downstream
+ # broker 0:
+ for _b in range(1,4):
+ for _l in ["dynamic", "queue"]:
+ result = self._brokers[0].qmf_object.create( "link",
+ "Link-%d-%s" % (_b, _l),
+ {"host":self._brokers[_b].host,
+ "port":self._brokers[_b].port}, False)
+ self.assertEqual(result.status, 0)
+
+ # create queue on source brokers for use by the dynamic route
+ self._brokers[_b].client_session.queue_declare(queue="fedSrcQ", exclusive=False, auto_delete=True)
+
+ for _l in range(1,4):
+ # for each dynamic link, create a dynamic bridge for the "fedX.direct"
+ # exchanges, using the fedSrcQ on each upstream source broker
+ result = self._brokers[0].qmf_object.create("bridge",
+ "Bridge-%d-dynamic" % _l,
+ {"link":"Link-%d-dynamic" % _l,
+ "src":"fedX.direct",
+ "dest":"fedX.direct",
+ "dynamic":True,
+ "queue":"fedSrcQ"}, False)
+ self.assertEqual(result.status, 0)
+
+ # create a queue route that shares the queue used by the dynamic route
+ result = self._brokers[0].qmf_object.create("bridge",
+ "Bridge-%d-queue" % _l,
+ {"link":"Link-%d-queue" % _l,
+ "src":"fedSrcQ",
+ "dest":"fedX.direct",
+ "srcIsQueue":True}, False)
+ self.assertEqual(result.status, 0)
+
+
+ # wait for the inter-broker links to become operational
+ retries = 0
+ operational = False
+ while not operational:
+ operational = True
+ for _l in qmf.getObjects(_class="link"):
+ #print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.state)))
+ if _l.state != "Operational":
+ operational = False
+ if not operational:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "inter-broker links failed to become operational.")
+ sleep(1)
+
+ # @todo - There is no way to determine when the bridge objects become
+ # active. Hopefully, this is long enough!
+ sleep(6)
+
+ # create a queue on B0, bound to "spudboy"
+ self._brokers[0].client_session.queue_declare(queue="DestQ", exclusive=True, auto_delete=True)
+ self._brokers[0].client_session.exchange_bind(queue="DestQ", exchange="fedX.direct", binding_key="spudboy")
+
+ # subscribe to messages arriving on B2's queue
+ self.subscribe(self._brokers[0].client_session, queue="DestQ", destination="f1")
+ queue = self._brokers[0].client_session.incoming("f1")
+
+ # wait until the binding key has propagated to each broker
+
+ binding_counts = [1, 1, 1, 1]
+ self.assertEqual(len(binding_counts), len(exchanges), "Update Test!")
+ for i in range(3,-1,-1):
+ retries = 0
+ exchanges[i].update()
+ while exchanges[i].bindingCount < binding_counts[i]:
+ retries += 1
+ self.failIfEqual(retries, 10,
+ "binding failed to propagate to broker %d"
+ % i)
+ sleep(3)
+ exchanges[i].update()
+
+ for _b in range(1,4):
+ # send 3 msgs from each source broker
+ for i in range(3):
+ dp = self._brokers[_b].client_session.delivery_properties(routing_key="spudboy")
+ self._brokers[_b].client_session.message_transfer(destination="fedX.direct", message=Message(dp, "Message_drp %d" % i))
+
+ # get exactly 9 (3 per broker) on B0
+ for i in range(9):
+ msg = queue.get(timeout=5)
+
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ # verify that messages went across every link
+ for _l in qmf.getObjects(_broker=self._brokers[0].qmf_broker,
+ _class="link"):
+ for _c in qmf.getObjects(_broker=self._brokers[0].qmf_broker,
+ _objectId=_l.connectionRef):
+ self.assertNotEqual(_c.msgsToClient, 0, "Messages did not pass over link as expected.")
+
+ # cleanup
+
+ self._brokers[0].client_session.exchange_unbind(queue="DestQ", exchange="fedX.direct", binding_key="spudboy")
+ self._brokers[0].client_session.message_cancel(destination="f1")
+ self._brokers[0].client_session.queue_delete(queue="DestQ")
+
+ for _b in qmf.getObjects(_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+
+ for _l in qmf.getObjects(_class="link"):
+ result = _l.close()
+ self.assertEqual(result.status, 0)
+
+ for _b in self._brokers:
+ _b.client_session.exchange_delete(exchange="fedX.direct")
+
+ self._teardown_brokers()
+
+ self.verify_cleanup()
+
diff --git a/qpid/cpp/src/tests/ha_tests.py b/qpid/cpp/src/tests/ha_tests.py
index 822e07c702..4d07d386f9 100755
--- a/qpid/cpp/src/tests/ha_tests.py
+++ b/qpid/cpp/src/tests/ha_tests.py
@@ -19,50 +19,121 @@
#
import os, signal, sys, time, imp, re, subprocess, glob, random, logging, shutil, math
-from qpid.messaging import Message, NotFound, ConnectionError, Connection
+import traceback
+from qpid.messaging import Message, NotFound, ConnectionError, ReceiverError, Connection, Timeout
from qpid.datatypes import uuid4
from brokertest import *
from threading import Thread, Lock, Condition
-from logging import getLogger, WARN, ERROR, DEBUG
+from logging import getLogger, WARN, ERROR, DEBUG, INFO
from qpidtoollibs import BrokerAgent
+from uuid import UUID
-log = getLogger("qpid.ha-tests")
+log = getLogger(__name__)
+
+class QmfAgent(object):
+ """Access to a QMF broker agent."""
+ def __init__(self, address):
+ self._connection = Connection.establish(
+ address, client_properties={"qpid.ha-admin":1})
+ self._agent = BrokerAgent(self._connection)
+ assert self._agent.getHaBroker(), "HA module not loaded in broker at: %s"%(address)
+
+ def __getattr__(self, name):
+ a = getattr(self._agent, name)
+ return a
class HaBroker(Broker):
- def __init__(self, test, args=[], broker_url=None, ha_cluster=True, **kwargs):
+ """Start a broker with HA enabled"""
+ def __init__(self, test, args=[], brokers_url=None, ha_cluster=True,
+ ha_replicate="all", **kwargs):
assert BrokerTest.ha_lib, "Cannot locate HA plug-in"
args = copy(args)
- args.extend(["--load-module", BrokerTest.ha_lib,
- # FIXME aconway 2012-02-13: workaround slow link failover.
- "--link-maintenace-interval=0.1",
- "--ha-cluster=%s"%ha_cluster])
- if broker_url: args.extend([ "--ha-brokers", broker_url ])
+ args += ["--load-module", BrokerTest.ha_lib,
+ "--log-enable=debug+:ha::",
+ # FIXME aconway 2012-02-13: workaround slow link failover.
+ "--link-maintenace-interval=0.1",
+ "--ha-cluster=%s"%ha_cluster]
+ if ha_replicate is not None:
+ args += [ "--ha-replicate=%s"%ha_replicate ]
+ if brokers_url: args += [ "--ha-brokers-url", brokers_url ]
Broker.__init__(self, test, args, **kwargs)
- self.commands=os.getenv("PYTHON_COMMANDS")
- assert os.path.isdir(self.commands)
+ self.qpid_ha_path=os.path.join(os.getenv("PYTHON_COMMANDS"), "qpid-ha")
+ assert os.path.exists(self.qpid_ha_path)
+ self.qpid_config_path=os.path.join(os.getenv("PYTHON_COMMANDS"), "qpid-config")
+ assert os.path.exists(self.qpid_config_path)
+ getLogger().setLevel(ERROR) # Hide expected WARNING log messages from failover.
+ self.qpid_ha_script=import_script(self.qpid_ha_path)
+ self._agent = None
+
+ def __str__(self): return Broker.__str__(self)
+
+ def qpid_ha(self, args): self.qpid_ha_script.main(["", "-b", self.host_port()]+args)
+
+ def promote(self): self.qpid_ha(["promote"])
+ def set_client_url(self, url): self.qpid_ha(["set", "--public-url", url])
+ def set_brokers_url(self, url): self.qpid_ha(["set", "--brokers-url", url])
+ def replicate(self, from_broker, queue): self.qpid_ha(["replicate", from_broker, queue])
+ def agent(self):
+ if not self._agent: self._agent = QmfAgent(self.host_port())
+ return self._agent
+
+ def ha_status(self):
+ hb = self.agent().getHaBroker()
+ hb.update()
+ return hb.status
+
+ def wait_status(self, status):
+ def try_get_status():
+ # Ignore ConnectionError, the broker may not be up yet.
+ try: return self.ha_status() == status;
+ except ConnectionError: return False
+ assert retry(try_get_status, timeout=20), "%s status != %r"%(self, status)
+
+ # FIXME aconway 2012-05-01: do direct python call to qpid-config code.
+ def qpid_config(self, args):
+ assert subprocess.call(
+ [self.qpid_config_path, "--broker", self.host_port()]+args) == 0
- def promote(self):
- assert os.system("%s/qpid-ha promote -b %s"%(self.commands, self.host_port())) == 0
+ def config_replicate(self, from_broker, queue):
+ self.qpid_config(["add", "queue", "--start-replica", from_broker, queue])
- def set_client_url(self, url):
- assert os.system(
- "%s/qpid-ha set --public-brokers=%s -b %s"%(self.commands, url,self.host_port())) == 0
+ def config_declare(self, queue, replication):
+ self.qpid_config(["add", "queue", queue, "--replicate", replication])
- def set_broker_url(self, url):
- assert os.system(
- "%s/qpid-ha set --brokers=%s -b %s"%(self.commands, url, self.host_port())) == 0
+ def connect_admin(self, **kwargs):
+ return Broker.connect(self, client_properties={"qpid.ha-admin":1}, **kwargs)
- def replicate(self, from_broker, queue):
- assert os.system(
- "%s/qpid-ha replicate -b %s %s %s"%(self.commands, self.host_port(), from_broker, queue)) == 0
+ def wait_backup(self, address):
+ """Wait for address to become valid on a backup broker."""
+ bs = self.connect_admin().session()
+ try: wait_address(bs, address)
+ finally: bs.connection.close()
- def config_replicate(self, from_broker, queue):
- assert os.system(
- "%s/qpid-config --broker=%s add queue --start-replica %s %s"%(self.commands, self.host_port(), from_broker, queue)) == 0
+ def assert_browse(self, queue, expected, **kwargs):
+ """Verify queue contents by browsing."""
+ bs = self.connect().session()
+ try:
+ wait_address(bs, queue)
+ assert_browse_retry(bs, queue, expected, **kwargs)
+ finally: bs.connection.close()
- def config_declare(self, queue, replication):
- assert os.system(
- "%s/qpid-config --broker=%s add queue %s --replicate %s"%(self.commands, self.host_port(), queue, replication)) == 0
+ def assert_browse_backup(self, queue, expected, **kwargs):
+ """Combines wait_backup and assert_browse_retry."""
+ bs = self.connect_admin().session()
+ try:
+ wait_address(bs, queue)
+ assert_browse_retry(bs, queue, expected, **kwargs)
+ finally: bs.connection.close()
+
+ def assert_connect_fail(self):
+ try:
+ self.connect()
+ self.test.fail("Expected ConnectionError")
+ except ConnectionError: pass
+
+ def try_connect(self):
+ try: return self.connect()
+ except ConnectionError: return None
class HaCluster(object):
_cluster_count = 0
@@ -70,77 +141,82 @@ class HaCluster(object):
def __init__(self, test, n, **kwargs):
"""Start a cluster of n brokers"""
self.test = test
- self._brokers = [ HaBroker(test, name="broker%s-%s"%(HaCluster._cluster_count, i), **kwargs) for i in xrange(n)]
+ self.kwargs = kwargs
+ self._brokers = []
+ self.id = HaCluster._cluster_count
+ self.broker_id = 0
HaCluster._cluster_count += 1
+ for i in xrange(n): self.start(False)
+ self.update_urls()
self[0].promote()
+
+ def next_name(self):
+ name="cluster%s-%s"%(self.id, self.broker_id)
+ self.broker_id += 1
+ return name
+
+ def start(self, update_urls=True):
+ """Start a new broker in the cluster"""
+ b = HaBroker(self.test, name=self.next_name(), **self.kwargs)
+ self._brokers.append(b)
+ if update_urls: self.update_urls()
+ return b
+
+ def update_urls(self):
self.url = ",".join([b.host_port() for b in self])
- for b in self: b.set_broker_url(self.url)
+ for b in self: b.set_brokers_url(self.url)
def connect(self, i):
"""Connect with reconnect_urls"""
return self[i].connect(reconnect=True, reconnect_urls=self.url.split(","))
- def kill(self, i):
+ def kill(self, i, promote_next=True):
"""Kill broker i, promote broker i+1"""
- self[i].kill()
self[i].expect = EXPECT_EXIT_FAIL
- self[(i+1) % len(self)].promote()
+ self[i].kill()
+ if promote_next: self[(i+1) % len(self)].promote()
+
+ def restart(self, i):
+ """Start a broker with the same name and data directory. It will get
+ a separate log file: foo.n.log"""
+ b = self._brokers[i]
+ self._brokers[i] = HaBroker(
+ self.test, name=b.name, port=b.port(), brokers_url=self.url,
+ **self.kwargs)
- def bounce(self, i):
+ def bounce(self, i, promote_next=True):
"""Stop and restart a broker in a cluster."""
- self.kill(i)
- b = self[i]
- self._brokers[i] = HaBroker(self.test, name=b.name, port=b.port(), broker_url=self.url)
+ self.kill(i, promote_next)
+ self.restart(i)
# Behave like a list of brokers.
def __len__(self): return len(self._brokers)
def __getitem__(self,index): return self._brokers[index]
def __iter__(self): return self._brokers.__iter__()
-
-def qr_node(value="all"): return "node:{x-declare:{arguments:{'qpid.replicate':%s}}}" % value
-
-class ShortTests(BrokerTest):
- """Short HA functionality tests."""
-
- # Wait for an address to become valid.
- def wait(self, session, address):
- def check():
- try:
- session.sender(address)
- return True
- except NotFound: return False
- assert retry(check), "Timed out waiting for address %s"%(address)
-
- # Wait for address to become valid on a backup broker.
- def wait_backup(self, backup, address):
- bs = self.connect_admin(backup).session()
- self.wait(bs, address)
- bs.connection.close()
-
- # Combines wait_backup and assert_browse_retry
- def assert_browse_backup(self, backup, queue, expected, **kwargs):
- bs = self.connect_admin(backup).session()
- self.wait(bs, queue)
- self.assert_browse_retry(bs, queue, expected, **kwargs)
- bs.connection.close()
-
- def assert_missing(self, session, address):
+def wait_address(session, address):
+ """Wait for an address to become valid."""
+ def check():
try:
- session.receiver(address)
- self.fail("Should not have been replicated: %s"%(address))
- except NotFound: pass
+ session.sender(address)
+ return True
+ except NotFound: return False
+ assert retry(check), "Timed out waiting for address %s"%(address)
- def connect_admin(self, backup, **kwargs):
- """Connect to a backup broker as an admin connection"""
- return backup.connect(client_properties={"qpid.ha-admin":1}, **kwargs)
+def valid_address(session, address):
+ """Test if an address is valid"""
+ try:
+ session.receiver(address)
+ return True
+ except NotFound: return False
+
+class ReplicationTests(BrokerTest):
+ """Correctness tests for HA replication."""
def test_replication(self):
"""Test basic replication of configuration and messages before and
after backup has connected"""
- getLogger().setLevel(ERROR) # Hide expected WARNING log messages from failover.
-
def queue(name, replicate):
return "%s;{create:always,node:{x-declare:{arguments:{'qpid.replicate':%s}}}}"%(name, replicate)
@@ -170,9 +246,8 @@ class ShortTests(BrokerTest):
def verify(b, prefix, p):
"""Verify setup was replicated to backup b"""
-
# Wait for configuration to replicate.
- self.wait(b, prefix+"x");
+ wait_address(b, prefix+"x");
self.assert_browse_retry(b, prefix+"q1", ["b", "1", "4"])
self.assertEqual(p.receiver(prefix+"q1").fetch(timeout=0).content, "b")
@@ -180,7 +255,7 @@ class ShortTests(BrokerTest):
self.assert_browse_retry(b, prefix+"q1", ["1", "4"])
self.assert_browse_retry(b, prefix+"q2", []) # configuration only
- self.assert_missing(b, prefix+"q3")
+ assert not valid_address(b, prefix+"q3")
b.sender(prefix+"e1").send(Message(prefix+"e1")) # Verify binds with replicate=all
self.assert_browse_retry(b, prefix+"q1", ["1", "4", prefix+"e1"])
b.sender(prefix+"e2").send(Message(prefix+"e2")) # Verify binds with replicate=configuration
@@ -195,17 +270,17 @@ class ShortTests(BrokerTest):
# Create config, send messages before starting the backup, to test catch-up replication.
setup(p, "1", primary)
- backup = HaBroker(self, name="backup", broker_url=primary.host_port())
+ backup = HaBroker(self, name="backup", brokers_url=primary.host_port())
# Create config, send messages after starting the backup, to test steady-state replication.
setup(p, "2", primary)
# Verify the data on the backup
- b = self.connect_admin(backup).session()
+ b = backup.connect_admin().session()
verify(b, "1", p)
verify(b, "2", p)
# Test a series of messages, enqueue all then dequeue all.
s = p.sender(queue("foo","all"))
- self.wait(b, "foo")
+ wait_address(b, "foo")
msgs = [str(i) for i in range(10)]
for m in msgs: s.send(Message(m))
self.assert_browse_retry(p, "foo", msgs)
@@ -227,103 +302,91 @@ class ShortTests(BrokerTest):
self.assert_browse_retry(b, "foo", msgs[i+1:])
def test_sync(self):
- def queue(name, replicate):
- return "%s;{create:always,%s}"%(name, qr_node(replicate))
primary = HaBroker(self, name="primary")
primary.promote()
p = primary.connect().session()
- s = p.sender(queue("q","all"))
+ s = p.sender("q;{create:always}")
for m in [str(i) for i in range(0,10)]: s.send(m)
s.sync()
- backup1 = HaBroker(self, name="backup1", broker_url=primary.host_port())
+ backup1 = HaBroker(self, name="backup1", brokers_url=primary.host_port())
for m in [str(i) for i in range(10,20)]: s.send(m)
s.sync()
- backup2 = HaBroker(self, name="backup2", broker_url=primary.host_port())
+ backup2 = HaBroker(self, name="backup2", brokers_url=primary.host_port())
for m in [str(i) for i in range(20,30)]: s.send(m)
s.sync()
msgs = [str(i) for i in range(30)]
- b1 = self.connect_admin(backup1).session()
- self.wait(b1, "q");
+ b1 = backup1.connect_admin().session()
+ wait_address(b1, "q");
self.assert_browse_retry(b1, "q", msgs)
- b2 = self.connect_admin(backup2).session()
- self.wait(b2, "q");
+ b2 = backup2.connect_admin().session()
+ wait_address(b2, "q");
self.assert_browse_retry(b2, "q", msgs)
def test_send_receive(self):
"""Verify sequence numbers of messages sent by qpid-send"""
- getLogger().setLevel(ERROR) # Hide expected WARNING log messages from failover.
- primary = HaBroker(self, name="primary")
- primary.promote()
- backup1 = HaBroker(self, name="backup1", broker_url=primary.host_port())
- backup2 = HaBroker(self, name="backup2", broker_url=primary.host_port())
+ brokers = HaCluster(self, 3)
sender = self.popen(
["qpid-send",
- "--broker", primary.host_port(),
- "--address", "q;{create:always,%s}"%(qr_node("all")),
+ "--broker", brokers[0].host_port(),
+ "--address", "q;{create:always}",
"--messages=1000",
"--content-string=x"
])
receiver = self.popen(
["qpid-receive",
- "--broker", primary.host_port(),
- "--address", "q;{create:always,%s}"%(qr_node("all")),
+ "--broker", brokers[0].host_port(),
+ "--address", "q;{create:always}",
"--messages=990",
"--timeout=10"
])
- try:
- self.assertEqual(sender.wait(), 0)
- self.assertEqual(receiver.wait(), 0)
- expect = [long(i) for i in range(991, 1001)]
- sn = lambda m: m.properties["sn"]
- self.assert_browse_retry(self.connect_admin(backup1).session(), "q", expect, transform=sn)
- self.assert_browse_retry(self.connect_admin(backup2).session(), "q", expect, transform=sn)
- except:
- print self.browse(primary.connect().session(), "q", transform=sn)
- print self.browse(self.connect_admin(backup1).session(), "q", transform=sn)
- print self.browse(self.connect_admin(backup2).session(), "q", transform=sn)
- raise
+ self.assertEqual(sender.wait(), 0)
+ self.assertEqual(receiver.wait(), 0)
+ expect = [long(i) for i in range(991, 1001)]
+ sn = lambda m: m.properties["sn"]
+ brokers[1].assert_browse_backup("q", expect, transform=sn)
+ brokers[2].assert_browse_backup("q", expect, transform=sn)
def test_failover_python(self):
"""Verify that backups rejects connections and that fail-over works in python client"""
- getLogger().setLevel(ERROR) # Hide expected WARNING log messages from failover.
primary = HaBroker(self, name="primary", expect=EXPECT_EXIT_FAIL)
primary.promote()
- backup = HaBroker(self, name="backup", broker_url=primary.host_port())
+ backup = HaBroker(self, name="backup", brokers_url=primary.host_port())
# Check that backup rejects normal connections
try:
backup.connect().session()
self.fail("Expected connection to backup to fail")
except ConnectionError: pass
# Check that admin connections are allowed to backup.
- self.connect_admin(backup).close()
+ backup.connect_admin().close()
# Test discovery: should connect to primary after reject by backup
c = backup.connect(reconnect_urls=[primary.host_port(), backup.host_port()], reconnect=True)
s = c.session()
- sender = s.sender("q;{create:always,%s}"%(qr_node()))
- self.wait_backup(backup, "q")
+ sender = s.sender("q;{create:always}")
+ backup.wait_backup("q")
sender.send("foo")
primary.kill()
assert retry(lambda: not is_running(primary.pid))
backup.promote()
- self.assert_browse_retry(s, "q", ["foo"])
+ sender.send("bar")
+ self.assert_browse_retry(s, "q", ["foo", "bar"])
c.close()
def test_failover_cpp(self):
"""Verify that failover works in the C++ client."""
primary = HaBroker(self, name="primary", expect=EXPECT_EXIT_FAIL)
primary.promote()
- backup = HaBroker(self, name="backup", broker_url=primary.host_port())
+ backup = HaBroker(self, name="backup", brokers_url=primary.host_port())
url="%s,%s"%(primary.host_port(), backup.host_port())
- primary.connect().session().sender("q;{create:always,%s}"%(qr_node()))
- self.wait_backup(backup, "q")
+ primary.connect().session().sender("q;{create:always}")
+ backup.wait_backup("q")
sender = NumberedSender(primary, url=url, queue="q", failover_updates = False)
receiver = NumberedReceiver(primary, url=url, queue="q", failover_updates = False)
receiver.start()
sender.start()
- self.wait_backup(backup, "q")
+ backup.wait_backup("q")
assert retry(lambda: receiver.received > 10) # Wait for some messages to get thru
primary.kill()
@@ -337,122 +400,123 @@ class ShortTests(BrokerTest):
def test_backup_failover(self):
"""Verify that a backup broker fails over and recovers queue state"""
brokers = HaCluster(self, 3)
- brokers[0].connect().session().sender(
- "q;{create:always,%s}"%(qr_node())).send("a")
- for b in brokers[1:]: self.assert_browse_backup(b, "q", ["a"])
+ brokers[0].connect().session().sender("q;{create:always}").send("a")
+ for b in brokers[1:]: b.assert_browse_backup("q", ["a"], msg=b)
brokers[0].expect = EXPECT_EXIT_FAIL
brokers.kill(0)
brokers[1].connect().session().sender("q").send("b")
- self.assert_browse_backup(brokers[2], "q", ["a","b"])
+ brokers[2].assert_browse_backup("q", ["a","b"])
s = brokers[1].connect().session()
self.assertEqual("a", s.receiver("q").fetch().content)
s.acknowledge()
- self.assert_browse_backup(brokers[2], "q", ["b"])
+ brokers[2].assert_browse_backup("q", ["b"])
def test_qpid_config_replication(self):
"""Set up replication via qpid-config"""
brokers = HaCluster(self,2)
brokers[0].config_declare("q","all")
brokers[0].connect().session().sender("q").send("foo")
- self.assert_browse_backup(brokers[1], "q", ["foo"])
+ brokers[1].assert_browse_backup("q", ["foo"])
def test_standalone_queue_replica(self):
"""Test replication of individual queues outside of cluster mode"""
getLogger().setLevel(ERROR) # Hide expected WARNING log messages from failover.
- primary = HaBroker(self, name="primary", ha_cluster=False, args=["--log-enable=debug+"])
+ primary = HaBroker(self, name="primary", ha_cluster=False)
pc = primary.connect()
ps = pc.session().sender("q;{create:always}")
pr = pc.session().receiver("q;{create:always}")
- backup = HaBroker(self, name="backup", ha_cluster=False, args=["--log-enable=debug+"])
+ backup = HaBroker(self, name="backup", ha_cluster=False)
br = backup.connect().session().receiver("q;{create:always}")
# Set up replication with qpid-ha
backup.replicate(primary.host_port(), "q")
ps.send("a")
- self.assert_browse_backup(backup, "q", ["a"])
+ backup.assert_browse_backup("q", ["a"])
ps.send("b")
- self.assert_browse_backup(backup, "q", ["a", "b"])
+ backup.assert_browse_backup("q", ["a", "b"])
self.assertEqual("a", pr.fetch().content)
pr.session.acknowledge()
- self.assert_browse_backup(backup, "q", ["b"])
+ backup.assert_browse_backup("q", ["b"])
# Set up replication with qpid-config
ps2 = pc.session().sender("q2;{create:always}")
backup.config_replicate(primary.host_port(), "q2");
ps2.send("x")
- self.assert_browse_backup(backup, "q2", ["x"])
+ backup.assert_browse_backup("q2", ["x"])
def test_queue_replica_failover(self):
"""Test individual queue replication from a cluster to a standalone backup broker, verify it fails over."""
- getLogger().setLevel(ERROR) # Hide expected WARNING log messages from failover.
cluster = HaCluster(self, 2)
primary = cluster[0]
pc = cluster.connect(0)
- ps = pc.session().sender("q;{create:always,%s}"%qr_node("all"))
- pr = pc.session().receiver("q;{create:always,%s}"%qr_node("all"))
- backup = HaBroker(self, name="backup", ha_cluster=False, args=["--log-enable=debug+"])
+ ps = pc.session().sender("q;{create:always}")
+ pr = pc.session().receiver("q;{create:always}")
+ backup = HaBroker(self, name="backup", ha_cluster=False)
br = backup.connect().session().receiver("q;{create:always}")
backup.replicate(cluster.url, "q")
ps.send("a")
- self.assert_browse_backup(backup, "q", ["a"])
+ backup.assert_browse_backup("q", ["a"])
cluster.bounce(0)
- self.assert_browse_backup(backup, "q", ["a"])
+ backup.assert_browse_backup("q", ["a"])
ps.send("b")
- self.assert_browse_backup(backup, "q", ["a", "b"])
+ backup.assert_browse_backup("q", ["a", "b"])
cluster.bounce(1)
self.assertEqual("a", pr.fetch().content)
pr.session.acknowledge()
- self.assert_browse_backup(backup, "q", ["b"])
+ backup.assert_browse_backup("q", ["b"])
def test_lvq(self):
"""Verify that we replicate to an LVQ correctly"""
primary = HaBroker(self, name="primary")
primary.promote()
- backup = HaBroker(self, name="backup", broker_url=primary.host_port())
- s = primary.connect().session().sender("lvq; {create:always, node:{x-declare:{arguments:{'qpid.last_value_queue_key':lvq-key, 'qpid.replicate':all}}}}")
+ backup = HaBroker(self, name="backup", brokers_url=primary.host_port())
+ s = primary.connect().session().sender("lvq; {create:always, node:{x-declare:{arguments:{'qpid.last_value_queue_key':lvq-key}}}}")
def send(key,value): s.send(Message(content=value,properties={"lvq-key":key}))
for kv in [("a","a-1"),("b","b-1"),("a","a-2"),("a","a-3"),("c","c-1"),("c","c-2")]:
send(*kv)
- self.assert_browse_backup(backup, "lvq", ["b-1", "a-3", "c-2"])
+ backup.assert_browse_backup("lvq", ["b-1", "a-3", "c-2"])
send("b","b-2")
- self.assert_browse_backup(backup, "lvq", ["a-3", "c-2", "b-2"])
+ backup.assert_browse_backup("lvq", ["a-3", "c-2", "b-2"])
send("c","c-3")
- self.assert_browse_backup(backup, "lvq", ["a-3", "b-2", "c-3"])
+ backup.assert_browse_backup("lvq", ["a-3", "b-2", "c-3"])
send("d","d-1")
- self.assert_browse_backup(backup, "lvq", ["a-3", "b-2", "c-3", "d-1"])
+ backup.assert_browse_backup("lvq", ["a-3", "b-2", "c-3", "d-1"])
def test_ring(self):
+ """Test replication with the ring queue policy"""
primary = HaBroker(self, name="primary")
primary.promote()
- backup = HaBroker(self, name="backup", broker_url=primary.host_port())
- s = primary.connect().session().sender("q; {create:always, node:{x-declare:{arguments:{'qpid.policy_type':ring, 'qpid.max_count':5, 'qpid.replicate':all}}}}")
+ backup = HaBroker(self, name="backup", brokers_url=primary.host_port())
+ s = primary.connect().session().sender("q; {create:always, node:{x-declare:{arguments:{'qpid.policy_type':ring, 'qpid.max_count':5}}}}")
for i in range(10): s.send(Message(str(i)))
- self.assert_browse_backup(backup, "q", [str(i) for i in range(5,10)])
+ backup.assert_browse_backup("q", [str(i) for i in range(5,10)])
def test_reject(self):
- getLogger().setLevel(ERROR) # Hide expected WARNING log messages from failover.
+ """Test replication with the reject queue policy"""
primary = HaBroker(self, name="primary")
primary.promote()
- backup = HaBroker(self, name="backup", broker_url=primary.host_port())
- s = primary.connect().session().sender("q; {create:always, node:{x-declare:{arguments:{'qpid.policy_type':reject, 'qpid.max_count':5, 'qpid.replicate':all}}}}")
+ backup = HaBroker(self, name="backup", brokers_url=primary.host_port())
+ s = primary.connect().session().sender("q; {create:always, node:{x-declare:{arguments:{'qpid.policy_type':reject, 'qpid.max_count':5}}}}")
try:
for i in range(10): s.send(Message(str(i)), sync=False)
except qpid.messaging.exceptions.TargetCapacityExceeded: pass
- self.assert_browse_backup(backup, "q", [str(i) for i in range(0,5)])
+ backup.assert_browse_backup("q", [str(i) for i in range(0,5)])
+ # Detach, don't close as there is a broken session
+ s.session.connection.detach()
def test_priority(self):
"""Verify priority queues replicate correctly"""
primary = HaBroker(self, name="primary")
primary.promote()
- backup = HaBroker(self, name="backup", broker_url=primary.host_port())
+ backup = HaBroker(self, name="backup", brokers_url=primary.host_port())
session = primary.connect().session()
- s = session.sender("priority-queue; {create:always, node:{x-declare:{arguments:{'qpid.priorities':10, 'qpid.replicate':all}}}}")
+ s = session.sender("priority-queue; {create:always, node:{x-declare:{arguments:{'qpid.priorities':10}}}}")
priorities = [8,9,5,1,2,2,3,4,9,7,8,9,9,2]
for p in priorities: s.send(Message(priority=p))
# Can't use browse_backup as browser sees messages in delivery order not priority.
- self.wait_backup(backup, "priority-queue")
- r = self.connect_admin(backup).session().receiver("priority-queue")
+ backup.wait_backup("priority-queue")
+ r = backup.connect_admin().session().receiver("priority-queue")
received = [r.fetch().priority for i in priorities]
self.assertEqual(sorted(priorities, reverse=True), received)
@@ -460,17 +524,17 @@ class ShortTests(BrokerTest):
"""Verify priority queues replicate correctly"""
primary = HaBroker(self, name="primary")
primary.promote()
- backup = HaBroker(self, name="backup", broker_url=primary.host_port())
+ backup = HaBroker(self, name="backup", brokers_url=primary.host_port())
session = primary.connect().session()
levels = 8
priorities = [4,5,3,7,8,8,2,8,2,8,8,16,6,6,6,6,6,6,8,3,5,8,3,5,5,3,3,8,8,3,7,3,7,7,7,8,8,8,2,3]
limits={7:0,6:4,5:3,4:2,3:2,2:2,1:2}
limit_policy = ",".join(["'qpid.fairshare':5"] + ["'qpid.fairshare-%s':%s"%(i[0],i[1]) for i in limits.iteritems()])
- s = session.sender("priority-queue; {create:always, node:{x-declare:{arguments:{'qpid.priorities':%s, %s, 'qpid.replicate':all}}}}"%(levels,limit_policy))
+ s = session.sender("priority-queue; {create:always, node:{x-declare:{arguments:{'qpid.priorities':%s, %s}}}}"%(levels,limit_policy))
messages = [Message(content=str(uuid4()), priority = p) for p in priorities]
for m in messages: s.send(m)
- self.wait_backup(backup, s.target)
- r = self.connect_admin(backup).session().receiver("priority-queue")
+ backup.wait_backup(s.target)
+ r = backup.connect_admin().session().receiver("priority-queue")
received = [r.fetch().content for i in priorities]
sort = sorted(messages, key=lambda m: priority_level(m.priority, levels), reverse=True)
fair = [m.content for m in fairshare(sort, lambda l: limits.get(l,0), levels)]
@@ -479,17 +543,144 @@ class ShortTests(BrokerTest):
def test_priority_ring(self):
primary = HaBroker(self, name="primary")
primary.promote()
- backup = HaBroker(self, name="backup", broker_url=primary.host_port())
- s = primary.connect().session().sender("q; {create:always, node:{x-declare:{arguments:{'qpid.policy_type':ring, 'qpid.max_count':5, 'qpid.priorities':10, 'qpid.replicate':all}}}}")
+ backup = HaBroker(self, name="backup", brokers_url=primary.host_port())
+ s = primary.connect().session().sender("q; {create:always, node:{x-declare:{arguments:{'qpid.policy_type':ring, 'qpid.max_count':5, 'qpid.priorities':10}}}}")
priorities = [8,9,5,1,2,2,3,4,9,7,8,9,9,2]
for p in priorities: s.send(Message(priority=p))
- # FIXME aconway 2012-02-22: there is a bug in priority ring queues that allows a low
- # priority message to displace a high one. The following commented-out assert_browse
- # is for the correct result, the uncommented one is for the actualy buggy result.
- # See https://issues.apache.org/jira/browse/QPID-3866
+
+ # FIXME aconway 2012-02-22: there is a bug in priority ring
+ # queues that allows a low priority message to displace a high
+ # one. The following commented-out assert_browse is for the
+ # correct result, the uncommented one is for the actualy buggy
+ # result. See https://issues.apache.org/jira/browse/QPID-3866
#
- # self.assert_browse_backup(backup, "q", sorted(priorities,reverse=True)[0:5], transform=lambda m: m.priority)
- self.assert_browse_backup(backup, "q", [9,9,9,9,2], transform=lambda m: m.priority)
+ # backup.assert_browse_backup("q", sorted(priorities,reverse=True)[0:5], transform=lambda m: m.priority)
+ backup.assert_browse_backup("q", [9,9,9,9,2], transform=lambda m: m.priority)
+
+ def test_backup_acquired(self):
+ """Verify that acquired messages are backed up, for all queue types."""
+ class Test:
+ def __init__(self, queue, arguments, expect):
+ self.queue = queue
+ self.address = "%s;{create:always,node:{x-declare:{arguments:{%s}}}}"%(
+ self.queue, ",".join(arguments + ["'qpid.replicate':all"]))
+ self.expect = [str(i) for i in expect]
+
+ def send(self, connection):
+ """Send messages, then acquire one but don't acknowledge"""
+ s = connection.session()
+ for m in range(10): s.sender(self.address).send(str(m))
+ s.receiver(self.address).fetch()
+
+ def wait(self, brokertest, backup):
+ backup.wait_backup(self.queue)
+
+ def verify(self, brokertest, backup):
+ backup.assert_browse_backup(self.queue, self.expect, msg=self.queue)
+
+ tests = [
+ Test("plain",[],range(10)),
+ Test("ring", ["'qpid.policy_type':ring", "'qpid.max_count':5"], range(5,10)),
+ Test("priority",["'qpid.priorities':10"], range(10)),
+ Test("fairshare", ["'qpid.priorities':10,'qpid.fairshare':5"], range(10)),
+ Test("lvq", ["'qpid.last_value_queue_key':lvq-key"], [9])
+ ]
+
+ primary = HaBroker(self, name="primary")
+ primary.promote()
+ backup1 = HaBroker(self, name="backup1", brokers_url=primary.host_port())
+ c = primary.connect()
+ for t in tests: t.send(c) # Send messages, leave one unacknowledged.
+
+ backup2 = HaBroker(self, name="backup2", brokers_url=primary.host_port())
+ # Wait for backups to catch up.
+ for t in tests:
+ t.wait(self, backup1)
+ t.wait(self, backup2)
+ # Verify acquired message was replicated
+ for t in tests: t.verify(self, backup1)
+ for t in tests: t.verify(self, backup2)
+
+ def test_replicate_default(self):
+ """Make sure we don't replicate if ha-replicate is unspecified or none"""
+ cluster1 = HaCluster(self, 2, ha_replicate=None)
+ c1 = cluster1[0].connect().session().sender("q;{create:always}")
+ cluster2 = HaCluster(self, 2, ha_replicate="none")
+ cluster2[0].connect().session().sender("q;{create:always}")
+ time.sleep(.1) # Give replication a chance.
+ try:
+ cluster1[1].connect_admin().session().receiver("q")
+ self.fail("Excpected no-such-queue exception")
+ except NotFound: pass
+ try:
+ cluster2[1].connect_admin().session().receiver("q")
+ self.fail("Excpected no-such-queue exception")
+ except NotFound: pass
+
+ def test_invalid_replication(self):
+ """Verify that we reject an attempt to declare a queue with invalid replication value."""
+ cluster = HaCluster(self, 1, ha_replicate="all")
+ try:
+ c = cluster[0].connect().session().sender("q;{create:always, node:{x-declare:{arguments:{'qpid.replicate':XXinvalidXX}}}}")
+ self.fail("Expected ConnectionError")
+ except ConnectionError: pass
+
+ def test_exclusive_queue(self):
+ """Ensure that we can back-up exclusive queues, i.e. the replicating
+ subscriptions are exempt from the exclusivity"""
+ cluster = HaCluster(self, 2)
+ def test(addr):
+ c = cluster[0].connect()
+ q = addr.split(";")[0]
+ r = c.session().receiver(addr)
+ try: c.session().receiver(addr); self.fail("Expected exclusive exception")
+ except ReceiverError: pass
+ s = c.session().sender(q).send(q)
+ cluster[1].assert_browse_backup(q, [q])
+ test("excl_sub;{create:always, link:{x-subscribe:{exclusive:True}}}");
+ test("excl_queue;{create:always, node:{x-declare:{exclusive:True}}}")
+
+ def test_auto_delete_exclusive(self):
+ """Verify that we ignore auto-delete, exclusive, non-auto-delete-timeout queues"""
+ cluster = HaCluster(self,2)
+ s = cluster[0].connect().session()
+ s.receiver("exad;{create:always,node:{x-declare:{exclusive:True,auto-delete:True}}}")
+ s.receiver("ex;{create:always,node:{x-declare:{exclusive:True}}}")
+ s.receiver("ad;{create:always,node:{x-declare:{auto-delete:True}}}")
+ s.receiver("time;{create:always,node:{x-declare:{exclusive:True,auto-delete:True,arguments:{'qpid.auto_delete_timeout':1}}}}")
+ s.receiver("q;{create:always}")
+
+ s = cluster[1].connect_admin().session()
+ cluster[1].wait_backup("q")
+ assert not valid_address(s, "exad")
+ assert valid_address(s, "ex")
+ assert valid_address(s, "ad")
+ assert valid_address(s, "time")
+
+ def test_broker_info(self):
+ """Check that broker information is correctly published via management"""
+ cluster = HaCluster(self, 3)
+
+ for broker in cluster: # Make sure HA system-id matches broker's
+ qmf = broker.agent().getHaBroker()
+ self.assertEqual(qmf.systemId, UUID(broker.agent().getBroker().systemRef))
+
+ cluster_ports = map(lambda b: b.port(), cluster)
+ cluster_ports.sort()
+ def ports(qmf):
+ qmf.update()
+ return sorted(map(lambda b: b["port"], qmf.members))
+ # Check that all brokers have the same membership as the cluster
+ for broker in cluster:
+ qmf = broker.agent().getHaBroker()
+ assert retry(lambda: cluster_ports == ports(qmf), 1), "%s != %s on %s"%(cluster_ports, ports(qmf), broker)
+ # Add a new broker, check it is updated everywhere
+ b = cluster.start()
+ cluster_ports.append(b.port())
+ cluster_ports.sort()
+ for broker in cluster:
+ qmf = broker.agent().getHaBroker()
+ assert retry(lambda: cluster_ports == ports(qmf), 1), "%s != %s"%(cluster_ports, ports(qmf))
def fairshare(msgs, limit, levels):
"""
@@ -533,49 +724,108 @@ class LongTests(BrokerTest):
if d: return float(d)*60
else: return 3 # Default is to be quick
-
- def disable_test_failover(self):
+ def test_failover_send_receive(self):
"""Test failover with continuous send-receive"""
- # FIXME aconway 2012-02-03: fails due to dropped messages,
- # known issue: sending messages to new primary before
- # backups are ready. Enable when fixed.
-
- # Start a cluster, all members will be killed during the test.
- brokers = [ HaBroker(self, name=name, expect=EXPECT_EXIT_FAIL)
- for name in ["ha0","ha1","ha2"] ]
- url = ",".join([b.host_port() for b in brokers])
- for b in brokers: b.set_broker_url(url)
- brokers[0].promote()
+ brokers = HaCluster(self, 3)
# Start sender and receiver threads
- sender = NumberedSender(brokers[0], max_depth=1000, failover_updates=False)
- receiver = NumberedReceiver(brokers[0], sender=sender, failover_updates=False)
- receiver.start()
- sender.start()
+ n = 10
+ senders = [NumberedSender(brokers[0], max_depth=1024, failover_updates=False,
+ queue="test%s"%(i)) for i in xrange(n)]
+ receivers = [NumberedReceiver(brokers[0], sender=senders[i],
+ failover_updates=False,
+ queue="test%s"%(i)) for i in xrange(n)]
+ for r in receivers: r.start()
+ for s in senders: s.start()
+
# Wait for sender & receiver to get up and running
- assert retry(lambda: receiver.received > 100)
+ assert retry(lambda: receivers[0].received > 100), "%s<=100"%receivers[0].received
# Kill and restart brokers in a cycle:
endtime = time.time() + self.duration()
i = 0
- while time.time() < endtime or i < 3: # At least 3 iterations
- sender.sender.assert_running()
- receiver.receiver.assert_running()
- port = brokers[i].port()
- brokers[i].kill()
- brokers.append(
- HaBroker(self, name="ha%d"%(i+3), broker_url=url, port=port,
- expect=EXPECT_EXIT_FAIL))
- i += 1
- brokers[i].promote()
- n = receiver.received # Verify we're still running
- def enough():
- receiver.check() # Verify no exceptions
- return receiver.received > n + 100
- assert retry(enough, timeout=5)
-
- sender.stop()
- receiver.stop()
- for b in brokers[i:]: b.kill()
+ try:
+ while time.time() < endtime or i < 3: # At least 3 iterations
+ for s in senders: s.sender.assert_running()
+ for r in receivers: r.receiver.assert_running()
+ n = receivers[0].received
+ # FIXME aconway 2012-05-01: don't kill primary till it's active
+ # and backups are ready, otherwise we can lose messages. When we
+ # implement non-promotion of catchup brokers we can make this
+ # stronger: wait only for there to be at least one ready backup.
+ brokers[i%3].wait_status("active")
+ brokers[(i+1)%3].wait_status("ready")
+ brokers[(i+2)%3].wait_status("ready")
+ brokers.bounce(i%3)
+ i += 1
+ def enough(): # Verify we're still running
+ receivers[0].check() # Verify no exceptions
+ return receivers[0].received > n + 100
+ assert retry(enough), "Stalled: %s < %s+100"%(receivers[0].received, n)
+ except:
+ traceback.print_exc()
+ raise
+ finally:
+ for s in senders: s.stop()
+ for r in receivers: r.stop()
+ dead = []
+ for i in xrange(3):
+ if not brokers[i].is_running(): dead.append(i)
+ brokers.kill(i, False)
+ if dead: raise Exception("Brokers not running: %s"%dead)
+
+class RecoveryTests(BrokerTest):
+ """Tests for recovery after a failure."""
+
+ def test_queue_hold(self):
+ """Verify that the broker holds queues without sufficient backup,
+ i.e. does not complete messages sent to those queues."""
+
+ cluster = HaCluster(self, 4);
+ # Wait for the primary to be ready
+ cluster[0].wait_status("active")
+ # Create a queue before the failure.
+ s1 = cluster.connect(0).session().sender("q1;{create:always}")
+ for b in cluster: b.wait_backup("q1")
+ for i in xrange(100): s1.send(str(i))
+ # Kill primary and 2 backups
+ for i in [0,1,2]: cluster.kill(i, False)
+ cluster[3].promote() # New primary, backups will be 1 and 2
+ cluster[3].wait_status("recovering")
+
+ def trySync(s):
+ try:
+ s.sync(timeout=.1)
+ self.fail("Expected Timeout exception")
+ except Timeout: pass
+
+ # Create a queue after the failure
+ s2 = cluster.connect(3).session().sender("q2;{create:always}")
+ # Verify that messages sent are not completed
+ for i in xrange(100,200): s1.send(str(i), sync=False); s2.send(str(i), sync=False)
+ trySync(s1)
+ self.assertEqual(s1.unsettled(), 100)
+ trySync(s2)
+ self.assertEqual(s2.unsettled(), 100)
+
+ # Verify we can receive even if sending is on hold:
+ cluster[3].assert_browse("q1", [str(i) for i in range(100)+range(100,200)])
+ # Restart backups, verify queues are released only when both backups are up
+ cluster.restart(1)
+ trySync(s1)
+ self.assertEqual(s1.unsettled(), 100)
+ trySync(s2)
+ self.assertEqual(s2.unsettled(), 100)
+ self.assertEqual(cluster[3].ha_status(), "recovering")
+ cluster.restart(2)
+
+ def settled(sender): sender.sync(); return sender.unsettled() == 0;
+ assert retry(lambda: settled(s1)), "Unsetttled=%s"%(s1.unsettled())
+ assert retry(lambda: settled(s2)), "Unsetttled=%s"%(s2.unsettled())
+ cluster[1].assert_browse_backup("q1", [str(i) for i in range(100)+range(100,200)])
+ cluster[1].assert_browse_backup("q2", [str(i) for i in range(100,200)])
+ cluster[3].wait_status("active"),
+ s1.session.connection.close()
+ s2.session.connection.close()
if __name__ == "__main__":
shutil.rmtree("brokertest.tmp", True)
diff --git a/qpid/cpp/src/tests/install_env.sh.in b/qpid/cpp/src/tests/install_env.sh.in
index 2231954cb8..d29a23930d 100644
--- a/qpid/cpp/src/tests/install_env.sh.in
+++ b/qpid/cpp/src/tests/install_env.sh.in
@@ -23,4 +23,4 @@ prefix=`absdir @prefix@`
export QPID_INSTALL_PREFIX=$prefix
export PATH=$prefix/bin:$prefix/sbin:$prefix/libexec/qpid/tests:$PATH
export LD_LIBRARY_PATH=$prefix/lib:$LD_LIBRARY_PATH
-export PYTHONPATH=$prefix/lib/python2.4/site-packages:$PYTHONPATH
+export PYTHONPATH=$prefix/lib/python2.6/site-packages:$PYTHONPATH
diff --git a/qpid/cpp/src/tests/ipv6_test b/qpid/cpp/src/tests/ipv6_test
index 8fa272d514..6becfd8c96 100755
--- a/qpid/cpp/src/tests/ipv6_test
+++ b/qpid/cpp/src/tests/ipv6_test
@@ -113,7 +113,8 @@ fi
test -z $CLUSTER_LIB && exit 0 # Exit if cluster not supported.
## Test failover in a cluster using IPv6 only
-. $srcdir/ais_check # Will exit if clustering not enabled.
+. cpg_check.sh
+cpg_enabled || exit 0
pick_port() {
# We need a fixed port to set --cluster-url. Use qpidd to pick a free port.
diff --git a/qpid/cpp/src/tests/logging.cpp b/qpid/cpp/src/tests/logging.cpp
index dcecf0b54c..c408141d6f 100644
--- a/qpid/cpp/src/tests/logging.cpp
+++ b/qpid/cpp/src/tests/logging.cpp
@@ -44,8 +44,10 @@ namespace tests {
QPID_AUTO_TEST_SUITE(loggingTestSuite)
using namespace std;
-using namespace boost;
using namespace qpid::log;
+using boost::ends_with;
+using boost::contains;
+using boost::format;
QPID_AUTO_TEST_CASE(testStatementInit) {
Statement s=QPID_LOG_STATEMENT_INIT(debug); int line=__LINE__;
@@ -256,7 +258,7 @@ QPID_AUTO_TEST_CASE(testOverhead) {
Statement statement(
Level level, const char* file="", int line=0, const char* fn=0)
{
- Statement s={0, file, line, fn, level};
+ Statement s={0, file, line, fn, level, ::qpid::log::unspecified};
return s;
}
@@ -345,11 +347,11 @@ QPID_AUTO_TEST_CASE(testLoggerStateure) {
};
opts.parse(ARGC(argv), const_cast<char**>(argv));
l.configure(opts);
- QPID_LOG(critical, "foo"); int srcline=__LINE__;
+ QPID_LOG_CAT(critical, test, "foo"); int srcline=__LINE__;
ifstream log("logging.tmp");
string line;
getline(log, line);
- string expect=(format("critical %s:%d: foo")%__FILE__%srcline).str();
+ string expect=(format("[Test] critical %s:%d: foo")%__FILE__%srcline).str();
BOOST_CHECK_EQUAL(expect, line);
log.close();
unlink("logging.tmp");
@@ -377,7 +379,7 @@ QPID_AUTO_TEST_CASE(testQuoteNonPrintable) {
ifstream log("logging.tmp");
string line;
getline(log, line, '\0');
- string expect="critical null\\x00tab\tspace newline\nret\r\\x80\\x99\\xFF\\x00\n";
+ string expect="[Test] critical null\\x00tab\tspace newline\nret\r\\x80\\x99\\xFF\\x00\n";
BOOST_CHECK_EQUAL(expect, line);
log.close();
unlink("logging.tmp");
diff --git a/qpid/cpp/src/tests/qpid-cluster-benchmark b/qpid/cpp/src/tests/qpid-cluster-benchmark
index d836ed709c..610beacebd 100755
--- a/qpid/cpp/src/tests/qpid-cluster-benchmark
+++ b/qpid/cpp/src/tests/qpid-cluster-benchmark
@@ -22,38 +22,45 @@
# Default options
MESSAGES="-m 10000"
-FLOW="--flow-control 100" # Flow control limit on queue depth for latency.
REPEAT="--repeat 10"
QUEUES="-q 6"
SENDERS="-s 3"
RECEIVERS="-r 3"
BROKERS= # Local broker
CLIENT_HOSTS= # No ssh, all clients are local
+# Connection options
+TCP_NODELAY=false
+RECONNECT=true
+HEARTBEAT=1
-while getopts "m:f:n:b:q:s:r:c:txyv-" opt; do
+while getopts "m:f:n:b:q:s:r:c:h:i:txyv-" opt; do
case $opt in
+ b) BROKERS="-b $OPTARG";;
+ c) CLIENT_HOSTS="-c $OPTARG";;
+ h) HEARTBEAT=$OPTARG;;
+ i) RECONNECT=$OPTARG;;
m) MESSAGES="-m $OPTARG";;
- f) FLOW="--flow-control $OPTARG";;
n) REPEAT="--repeat $OPTARG";;
- b) BROKERS="-b $OPTARG";;
q) QUEUES="-q $OPTARG";;
- s) SENDERS="-s $OPTARG";;
r) RECEIVERS="-r $OPTARG";;
- c) CLIENT_HOSTS="-c $OPTARG";;
- t) TCP_NODELAY="--connection-options {tcp-nodelay:true}";;
+ s) SENDERS="-s $OPTARG";;
+ t) TCP_NODELAY=true;;
+ v) OPTS="--verbose";;
x) SAVE_RECEIVED="--save-received";;
y) NO_DELETE="--no-delete";;
- v) OPTS="--verbose";;
-) break ;;
*) echo "Unknown option"; exit 1;;
esac
done
shift $(($OPTIND-1))
-REPLICATE="node:{x-declare:{arguments:{'qpid.replicate':all}}}"
+CONNECTION_OPTIONS="--connection-options {tcp-nodelay:$TCP_NODELAY,reconnect:$RECONNECT,heartbeat:$HEARTBEAT}"
+CREATE_OPTIONS="node:{x-declare:{arguments:{'qpid.replicate':all}}}"
+
BROKER=$(echo $BROKERS | sed s/,.*//)
run_test() { echo $*; shift; "$@"; echo; echo; echo; }
-OPTS="$OPTS $REPEAT $BROKERS --summarize $QUEUES $SENDERS $RECEIVERS $MESSAGES $CLIENT_HOSTS $SAVE_RECEIVED $TCP_NODELAY $NO_DELETE"
-OPTS="$OPTS --create-option $REPLICATE"
+OPTS="$OPTS $REPEAT $BROKERS --summarize $QUEUES $SENDERS $RECEIVERS $MESSAGES $CLIENT_HOSTS $SAVE_RECEIVED $CONNECTION_OPTIONS $NO_DELETE"
+OPTS="$OPTS --create-option $CREATE_OPTIONS"
+
run_test "Benchmark:" qpid-cpp-benchmark $OPTS "$@"
diff --git a/qpid/cpp/src/tests/qpid-cpp-benchmark b/qpid/cpp/src/tests/qpid-cpp-benchmark
index 19c01dd08a..d5ad5191ca 100755
--- a/qpid/cpp/src/tests/qpid-cpp-benchmark
+++ b/qpid/cpp/src/tests/qpid-cpp-benchmark
@@ -37,7 +37,7 @@ op.add_option("-r", "--receivers", default=1, type="int", metavar="N",
op.add_option("-m", "--messages", default=100000, type="int", metavar="N",
help="send N messages per sender (default %default)")
op.add_option("--queue-name", default="benchmark", metavar="NAME",
- help="base name for queues (default %default)")
+ help="base name for queues (default %default)")
op.add_option("--send-rate", default=0, metavar="N",
help="send rate limited to N messages/second, 0 means no limit (default %default)")
op.add_option("--receive-rate", default=0, metavar="N",
@@ -67,18 +67,16 @@ op.add_option("--sequence", dest="sequence", default=False,
action="store_true", help="add a sequence number to each message")
op.add_option("--connection-options", type="str",
help="Connection options for senders & receivers")
-op.add_option("--flow-control", default=0, type="int", metavar="N",
- help="Flow control each sender to limit queue depth to 2*N. 0 means no flow control.")
op.add_option("--durable", default=False, action="store_true",
help="Use durable queues and messages")
op.add_option("--save-received", default=False, action="store_true",
help="Save received message content to files <queuename>-receiver-<n>.msg")
-op.add_option("--group-receivers", default=False, action="store_true",
- help="Run receivers for the same queue on the same host.")
op.add_option("--verbose", default=False, action="store_true",
help="Show commands executed")
op.add_option("--no-delete", default=False, action="store_true",
help="Don't delete the test queues.")
+op.add_option("--fill-drain", default=False, action="store_true",
+ help="First fill the queues, then drain them")
single_quote_re = re.compile("'")
def posix_quote(string):
@@ -150,7 +148,6 @@ def start_send(queue, opts, broker, host):
"--report-header=no",
"--timestamp=%s"%(opts.timestamp and "yes" or "no"),
"--sequence=%s"%(opts.sequence and "yes" or "no"),
- "--flow-control", str(opts.flow_control),
"--durable", str(opts.durable)
]
command += opts.send_arg
@@ -169,18 +166,6 @@ def first_line(p):
raise Exception("Process exit %d: %s"%(p.returncode, error_msg(out,err)))
return out.split("\n")[0]
-def queue_exists(queue,broker):
- c = qpid.messaging.Connection(broker)
- c.open()
- try:
- s = c.session()
- try:
- s.sender(queue)
- return True
- except qpid.messaging.exceptions.NotFound:
- return False
- finally: c.close()
-
def recreate_queues(queues, brokers, no_delete, opts):
c = qpid.messaging.Connection(brokers[0])
c.open()
@@ -189,15 +174,9 @@ def recreate_queues(queues, brokers, no_delete, opts):
if not no_delete:
try: s.sender("%s;{delete:always}"%(q)).close()
except qpid.messaging.exceptions.NotFound: pass
- # FIXME aconway 2011-05-04: new cluster async wiring, wait for changes to propagate
- for b in brokers:
- while queue_exists(q,b): time.sleep(0.1);
address = "%s;{%s}"%(q, ",".join(opts.create_option + ["create:always"]))
if opts.verbose: print "Creating", address
s.sender(address)
- # FIXME aconway 2011-05-04: new cluster async wiring, wait for changes to propagate
- for b in brokers:
- while not queue_exists(q,b): time.sleep(0.1);
c.close()
def print_header(timestamp):
@@ -295,24 +274,32 @@ def main():
recreate_queues(queues, opts.broker, opts.no_delete, opts)
ready_receiver = ReadyReceiver(ready_queue, opts.broker[0])
- if opts.group_receivers: # Run receivers for same queue against same broker.
- receivers = []
- for q in queues:
- b = brokers.next()
- for j in xrange(opts.receivers):
- receivers.append(
- start_receive(q, j, opts, ready_queue, b, client_hosts.next()))
- else: # Don't group receivers
- receivers = [start_receive(q, j, opts, ready_queue,
- brokers.next(), client_hosts.next())
- for q in queues for j in xrange(opts.receivers)]
-
- ready_receiver.wait(filter(None, receivers)) # Wait for receivers to be ready.
- start = time.time()
- senders = [start_send(q, opts,brokers.next(), client_hosts.next())
- for q in queues for j in xrange(opts.senders)]
+ def start_receivers():
+ return [ start_receive(q, j, opts, ready_queue, brokers.next(), client_hosts.next())
+ for q in queues for j in xrange(opts.receivers) ]
+
+
+ def start_senders():
+ return [ start_send(q, opts,brokers.next(), client_hosts.next())
+ for q in queues for j in xrange(opts.senders) ]
+
if opts.report_header and i == 0: print_header(opts.timestamp)
- for p in senders + receivers: p.wait()
+
+ if opts.fill_drain:
+ # First fill the queues, then drain them
+ start = time.time()
+ senders = start_senders()
+ for p in senders: p.wait()
+ receivers = start_receivers()
+ for p in receivers: p.wait()
+ else:
+ # Run senders and receivers in parallel
+ receivers = start_receivers()
+ ready_receiver.wait(filter(None, receivers)) # Wait for receivers ready
+ start = time.time()
+ senders = start_senders()
+ for p in senders + receivers: p.wait()
+
total_sent = opts.queues * opts.senders * opts.messages
total_tp = total_sent / (time.time()-start)
send_stats=parse_senders(senders)
diff --git a/qpid/cpp/src/tests/qpid-latency-test.cpp b/qpid/cpp/src/tests/qpid-latency-test.cpp
index 20eb4568f3..2343cb1d77 100644
--- a/qpid/cpp/src/tests/qpid-latency-test.cpp
+++ b/qpid/cpp/src/tests/qpid-latency-test.cpp
@@ -359,19 +359,29 @@ void Sender::sendByRate()
}
uint64_t interval = TIME_SEC/opts.rate;
int64_t timeLimit = opts.timeLimit * TIME_SEC;
- uint64_t sent = 0, missedRate = 0;
+ uint64_t sent = 0;
AbsTime start = now();
+ AbsTime last = start;
while (true) {
AbsTime sentAt=now();
msg.getDeliveryProperties().setTimestamp(Duration(EPOCH, sentAt));
async(session).messageTransfer(arg::content=msg, arg::acceptMode=1);
if (opts.sync) session.sync();
++sent;
+ if (Duration(last, sentAt) > TIME_SEC*2) {
+ Duration t(start, now());
+ //check rate actually achieved thus far
+ uint actualRate = sent / (t/TIME_SEC);
+ //report inability to stay within 1% of desired rate
+ if (actualRate < opts.rate && opts.rate - actualRate > opts.rate/100) {
+ std::cerr << "WARNING: Desired send rate: " << opts.rate << ", actual send rate: " << actualRate << std::endl;
+ }
+ last = sentAt;
+ }
+
AbsTime waitTill(start, sent*interval);
Duration delay(sentAt, waitTill);
- if (delay < 0)
- ++missedRate;
- else
+ if (delay > 0)
sys::usleep(delay / TIME_USEC);
if (timeLimit != 0 && Duration(start, now()) > timeLimit) {
session.sync();
diff --git a/qpid/cpp/src/tests/qpid-ping.cpp b/qpid/cpp/src/tests/qpid-ping.cpp
index 0cb4afa0ee..52331499e7 100644
--- a/qpid/cpp/src/tests/qpid-ping.cpp
+++ b/qpid/cpp/src/tests/qpid-ping.cpp
@@ -32,11 +32,20 @@
#include <string>
#include <iostream>
-using namespace std;
-using namespace qpid::sys;
-using namespace qpid::framing;
-using namespace qpid::client;
-using namespace qpid;
+using std::cerr;
+using std::cout;
+using std::endl;
+using std::exception;
+using std::string;
+using namespace qpid::client::arg; // For keyword args
+using qpid::client::AsyncSession;
+using qpid::client::Connection;
+using qpid::client::Message;
+using qpid::client::SubscriptionManager;
+using qpid::framing::Uuid;
+
+namespace qpid {
+namespace tests {
struct PingOptions : public qpid::TestOptions {
int timeout; // Timeout in seconds.
@@ -48,9 +57,11 @@ struct PingOptions : public qpid::TestOptions {
}
};
+}} // namespace qpid::tests
+
int main(int argc, char** argv) {
try {
- PingOptions opts;
+ qpid::tests::PingOptions opts;
opts.parse(argc, argv);
opts.con.heartbeat = (opts.timeout+1)/2;
Connection connection;
@@ -58,8 +69,8 @@ int main(int argc, char** argv) {
if (!opts.quiet) cout << "Opened connection." << endl;
AsyncSession s = connection.newSession();
string qname(Uuid(true).str());
- s.queueDeclare(arg::queue=qname,arg::autoDelete=true,arg::exclusive=true);
- s.messageTransfer(arg::content=Message("hello", qname));
+ s.queueDeclare(queue=qname, autoDelete=true, exclusive=true);
+ s.messageTransfer(content=Message("hello", qname));
if (!opts.quiet) cout << "Sent message." << endl;
SubscriptionManager subs(s);
subs.get(qname);
diff --git a/qpid/cpp/src/tests/qpid-receive.cpp b/qpid/cpp/src/tests/qpid-receive.cpp
index 6deeb566dc..7a02b871db 100644
--- a/qpid/cpp/src/tests/qpid-receive.cpp
+++ b/qpid/cpp/src/tests/qpid-receive.cpp
@@ -68,6 +68,7 @@ struct Options : public qpid::Options
bool reportHeader;
string readyAddress;
uint receiveRate;
+ std::string replyto;
Options(const std::string& argv0=std::string())
: qpid::Options("Options"),
@@ -114,6 +115,7 @@ struct Options : public qpid::Options
("report-header", qpid::optValue(reportHeader, "yes|no"), "Headers on report.")
("ready-address", qpid::optValue(readyAddress, "ADDRESS"), "send a message to this address when ready to receive")
("receive-rate", qpid::optValue(receiveRate,"N"), "Receive at rate of N messages/second. 0 means receive as fast as possible.")
+ ("reply-to", qpid::optValue(replyto, "REPLY-TO"), "specify reply-to address on response messages")
("help", qpid::optValue(help), "print this usage statement");
add(log);
}
@@ -246,6 +248,9 @@ int main(int argc, char ** argv)
s = session.createSender(msg.getReplyTo());
s.setCapacity(opts.capacity);
}
+ if (!opts.replyto.empty()) {
+ msg.setReplyTo(Address(opts.replyto));
+ }
s.send(msg);
}
if (opts.receiveRate) {
diff --git a/qpid/cpp/src/tests/qpid-send.cpp b/qpid/cpp/src/tests/qpid-send.cpp
index b1213a484f..b1c4f2be38 100644
--- a/qpid/cpp/src/tests/qpid-send.cpp
+++ b/qpid/cpp/src/tests/qpid-send.cpp
@@ -36,15 +36,26 @@
#include <iostream>
#include <memory>
-using namespace std;
-using namespace qpid::messaging;
-using namespace qpid::types;
-
-typedef std::vector<std::string> string_vector;
+using std::string;
+using std::ios_base;
+
+using qpid::messaging::Address;
+using qpid::messaging::Connection;
+using qpid::messaging::Duration;
+using qpid::messaging::FailoverUpdates;
+using qpid::messaging::Message;
+using qpid::messaging::Receiver;
+using qpid::messaging::Session;
+using qpid::messaging::Sender;
+using qpid::types::Exception;
+using qpid::types::Uuid;
+using qpid::types::Variant;
namespace qpid {
namespace tests {
+typedef std::vector<std::string> string_vector;
+
struct Options : public qpid::Options
{
bool help;
@@ -74,7 +85,6 @@ struct Options : public qpid::Options
uint reportEvery;
bool reportHeader;
uint sendRate;
- uint flowControl;
bool sequence;
bool timestamp;
std::string groupKey;
@@ -104,7 +114,6 @@ struct Options : public qpid::Options
reportEvery(0),
reportHeader(true),
sendRate(0),
- flowControl(0),
sequence(true),
timestamp(true),
groupPrefix("GROUP-"),
@@ -138,7 +147,6 @@ struct Options : public qpid::Options
("report-every", qpid::optValue(reportEvery,"N"), "Report throughput statistics every N messages")
("report-header", qpid::optValue(reportHeader, "yes|no"), "Headers on report.")
("send-rate", qpid::optValue(sendRate,"N"), "Send at rate of N messages/second. 0 means send as fast as possible.")
- ("flow-control", qpid::optValue(flowControl,"N"), "Do end to end flow control to limit queue depth to 2*N. 0 means no flow control.")
("sequence", qpid::optValue(sequence, "yes|no"), "Add a sequence number messages property (required for duplicate/lost message detection)")
("timestamp", qpid::optValue(timestamp, "yes|no"), "Add a time stamp messages property (required for latency measurement)")
("group-key", qpid::optValue(groupKey, "KEY"), "Generate groups of messages using message header 'KEY' to hold the group identifier")
@@ -223,10 +231,6 @@ const string EOS("eos");
const string SN("sn");
const string TS("ts");
-}} // namespace qpid::tests
-
-using namespace qpid::tests;
-
class ContentGenerator {
public:
virtual ~ContentGenerator() {}
@@ -329,6 +333,20 @@ public:
}
};
+}} // namespace qpid::tests
+
+using qpid::tests::Options;
+using qpid::tests::Reporter;
+using qpid::tests::Throughput;
+using qpid::tests::ContentGenerator;
+using qpid::tests::GroupGenerator;
+using qpid::tests::GetlineContentGenerator;
+using qpid::tests::MapContentGenerator;
+using qpid::tests::FixedContentGenerator;
+using qpid::tests::SN;
+using qpid::tests::TS;
+using qpid::tests::EOS;
+
int main(int argc, char ** argv)
{
Connection connection;
@@ -350,8 +368,6 @@ int main(int argc, char ** argv)
msg.setPriority(opts.priority);
}
if (!opts.replyto.empty()) {
- if (opts.flowControl)
- throw Exception("Can't use reply-to and flow-control together");
msg.setReplyTo(Address(opts.replyto));
}
if (!opts.userid.empty()) msg.setUserId(opts.userid);
@@ -385,26 +401,10 @@ int main(int argc, char ** argv)
int64_t interval = 0;
if (opts.sendRate) interval = qpid::sys::TIME_SEC/opts.sendRate;
- Receiver flowControlReceiver;
- Address flowControlAddress("flow-"+Uuid(true).str()+";{create:always,delete:always}");
- uint flowSent = 0;
- if (opts.flowControl) {
- flowControlReceiver = session.createReceiver(flowControlAddress);
- flowControlReceiver.setCapacity(2);
- }
-
while (contentGen->setContent(msg)) {
++sent;
if (opts.sequence)
msg.getProperties()[SN] = sent;
- if (opts.flowControl) {
- if ((sent % opts.flowControl) == 0) {
- msg.setReplyTo(flowControlAddress);
- ++flowSent;
- }
- else
- msg.setReplyTo(Address()); // Clear the reply address.
- }
if (groupGen.get())
groupGen->setGroupInfo(msg);
@@ -423,19 +423,12 @@ int main(int argc, char ** argv)
}
if (opts.messages && sent >= opts.messages) break;
- if (opts.flowControl && flowSent == 2) {
- flowControlReceiver.get(Duration::SECOND);
- --flowSent;
- }
-
if (opts.sendRate) {
qpid::sys::AbsTime waitTill(start, sent*interval);
int64_t delay = qpid::sys::Duration(qpid::sys::now(), waitTill);
if (delay > 0) qpid::sys::usleep(delay/qpid::sys::TIME_USEC);
}
}
- for ( ; flowSent>0; --flowSent)
- flowControlReceiver.get(Duration::SECOND);
if (opts.reportTotal) reporter.report();
for (uint i = opts.sendEos; i > 0; --i) {
if (opts.sequence)
diff --git a/qpid/cpp/src/tests/qpid-txtest.cpp b/qpid/cpp/src/tests/qpid-txtest.cpp
index d0ba2f1245..6e7d46802c 100644
--- a/qpid/cpp/src/tests/qpid-txtest.cpp
+++ b/qpid/cpp/src/tests/qpid-txtest.cpp
@@ -33,6 +33,7 @@
#include "qpid/client/SubscriptionManager.h"
#include "qpid/framing/Array.h"
#include "qpid/framing/Buffer.h"
+#include "qpid/framing/FieldValue.h"
#include "qpid/framing/Uuid.h"
#include "qpid/sys/Thread.h"
@@ -245,10 +246,10 @@ struct Controller : public Client
// Recover DTX transactions (if any)
if (opts.dtx) {
- std::vector<std::string> inDoubtXids;
framing::DtxRecoverResult dtxRes = session.dtxRecover().get();
const framing::Array& xidArr = dtxRes.getInDoubt();
- xidArr.collect(inDoubtXids);
+ std::vector<std::string> inDoubtXids(xidArr.size());
+ std::transform(xidArr.begin(), xidArr.end(), inDoubtXids.begin(), framing::Array::get<std::string, framing::Array::ValuePtr>);
if (inDoubtXids.size()) {
if (!opts.quiet) std::cout << "Recovering DTX in-doubt transaction(s):" << std::endl;
diff --git a/qpid/cpp/src/tests/qpidd-empty.conf b/qpid/cpp/src/tests/qpidd-empty.conf
new file mode 100644
index 0000000000..bbf52bf446
--- /dev/null
+++ b/qpid/cpp/src/tests/qpidd-empty.conf
@@ -0,0 +1,3 @@
+# An empty configuration file.
+# Used when running tests to avoid picking up configuration
+# installed in the default place.
diff --git a/qpid/cpp/src/tests/run_acl_tests b/qpid/cpp/src/tests/run_acl_tests
index 8629bd0777..25241ad75e 100755
--- a/qpid/cpp/src/tests/run_acl_tests
+++ b/qpid/cpp/src/tests/run_acl_tests
@@ -22,16 +22,24 @@
# Run the acl tests. $srcdir is set by the Makefile.
source ./test_env.sh
DATA_DIR=`pwd`/data_dir
+DATA_DIRI=`pwd`/data_diri
+DATA_DIRU=`pwd`/data_diru
trap stop_brokers INT TERM QUIT
start_brokers() {
- ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIR --load-module $ACL_LIB --acl-file policy.acl --auth no > qpidd.port
+ ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIR --load-module $ACL_LIB --acl-file policy.acl --auth no --log-to-file local.log > qpidd.port
LOCAL_PORT=`cat qpidd.port`
+ ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIRI --load-module $ACL_LIB --acl-file policy.acl --auth no --max-connections-per-ip 2 --log-to-file locali.log > qpiddi.port
+ LOCAL_PORTI=`cat qpiddi.port`
+ ../qpidd --daemon --port 0 --no-module-dir --data-dir $DATA_DIRU --load-module $ACL_LIB --acl-file policy.acl --auth no --max-connections-per-user 2 --log-to-file localu.log > qpiddu.port
+ LOCAL_PORTU=`cat qpiddu.port`
}
stop_brokers() {
$QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORT
+ $QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORTI
+ $QPIDD_EXEC --no-module-dir -q --port $LOCAL_PORTU
}
test_loading_acl_from_absolute_path(){
@@ -49,14 +57,22 @@ test_loading_acl_from_absolute_path(){
if test -d ${PYTHON_DIR} ; then
rm -rf $DATA_DIR
+ rm -rf $DATA_DIRI
+ rm -rf $DATA_DIRU
mkdir -p $DATA_DIR
+ mkdir -p $DATA_DIRI
+ mkdir -p $DATA_DIRU
cp $srcdir/policy.acl $DATA_DIR
+ cp $srcdir/policy.acl $DATA_DIRI
+ cp $srcdir/policy.acl $DATA_DIRU
start_brokers
- echo "Running acl tests using brokers on ports $LOCAL_PORT"
- $QPID_PYTHON_TEST -b localhost:$LOCAL_PORT -m acl || EXITCODE=1
+ echo "Running acl tests using brokers on ports $LOCAL_PORT, $LOCAL_PORTI, and $LOCAL_PORTU"
+ $QPID_PYTHON_TEST -b localhost:$LOCAL_PORT -m acl -Dport-i=$LOCAL_PORTI -Dport-u=$LOCAL_PORTU || EXITCODE=1
stop_brokers || EXITCODE=1
test_loading_acl_from_absolute_path || EXITCODE=1
rm -rf $DATA_DIR
+ rm -rf $DATA_DIRI
+ rm -rf $DATA_DIRU
exit $EXITCODE
fi
diff --git a/qpid/cpp/src/tests/run_cluster_authentication_soak b/qpid/cpp/src/tests/run_cluster_authentication_soak
index 7bc406c4ca..24befa28ba 100755
--- a/qpid/cpp/src/tests/run_cluster_authentication_soak
+++ b/qpid/cpp/src/tests/run_cluster_authentication_soak
@@ -19,8 +19,9 @@
source ./test_env.sh
-source $srcdir/ais_check
source sasl_test_setup.sh
+source cpg_check.sh
+cpg_enabled || exit 0
with_ais_group ./cluster_authentication_soak 500
diff --git a/qpid/cpp/src/tests/run_cluster_authentication_test b/qpid/cpp/src/tests/run_cluster_authentication_test
index 647200b869..844807a857 100755
--- a/qpid/cpp/src/tests/run_cluster_authentication_test
+++ b/qpid/cpp/src/tests/run_cluster_authentication_test
@@ -19,8 +19,9 @@
source ./test_env.sh
-source $srcdir/ais_check
source sasl_test_setup.sh
+source cpg_check.sh
+cpg_enabled || exit 0
with_ais_group ./cluster_authentication_soak
diff --git a/qpid/cpp/src/tests/run_cluster_test b/qpid/cpp/src/tests/run_cluster_test
index c022eea1fe..11df3d63a3 100755
--- a/qpid/cpp/src/tests/run_cluster_test
+++ b/qpid/cpp/src/tests/run_cluster_test
@@ -22,5 +22,6 @@
# Run the tests
srcdir=`dirname $0`
-. $srcdir/ais_check
+source cpg_check.sh
+cpg_enabled || exit 0
with_ais_group $srcdir/run_test ./cluster_test
diff --git a/qpid/cpp/src/tests/run_cluster_tests b/qpid/cpp/src/tests/run_cluster_tests
index e136d3810a..a5cea5ff6e 100755
--- a/qpid/cpp/src/tests/run_cluster_tests
+++ b/qpid/cpp/src/tests/run_cluster_tests
@@ -20,7 +20,9 @@
#
source ./test_env.sh
-source $srcdir/ais_check
+source cpg_check.sh
+cpg_enabled || exit 0
+
test -x $QPID_PYTHON_TEST || { echo Skipping test, $QPID_PYTHON_TEST not found; exit 0; }
diff --git a/qpid/cpp/src/tests/run_failover_soak b/qpid/cpp/src/tests/run_failover_soak
index 4c2e8cc188..2c56bf7d6b 100755
--- a/qpid/cpp/src/tests/run_failover_soak
+++ b/qpid/cpp/src/tests/run_failover_soak
@@ -20,7 +20,8 @@
#
source ./test_env.sh
-. $srcdir/ais_check
+source cpg_check.sh
+cpg_enabled || exit 0
host=127.0.0.1
diff --git a/qpid/cpp/src/tests/run_federation_sys_tests b/qpid/cpp/src/tests/run_federation_sys_tests
index f5f772d72e..76da176914 100755
--- a/qpid/cpp/src/tests/run_federation_sys_tests
+++ b/qpid/cpp/src/tests/run_federation_sys_tests
@@ -26,12 +26,8 @@ source ./test_env.sh
MODULENAME=federation_sys
# Test for clustering
-ps -u root | grep 'aisexec\|corosync' > /dev/null
-if (( $? == 0 )); then
- CLUSTERING_ENABLED=1
-else
- echo "WARNING: No clustering detected; tests using it will be ignored."
-fi
+source cpg_check.sh
+if cpg_enabled; then CLUSTERING_ENABLED=1; fi
# Test for long test
if [[ "$1" == "LONG_TEST" ]]; then
diff --git a/qpid/cpp/src/tests/run_federation_tests b/qpid/cpp/src/tests/run_federation_tests
index b71fa14c47..c2ee550226 100755
--- a/qpid/cpp/src/tests/run_federation_tests
+++ b/qpid/cpp/src/tests/run_federation_tests
@@ -33,16 +33,13 @@ else
SKIPTESTS='-i *_xml' # note: single quotes prevent expansion of *
fi
+QPIDD_CMD="../qpidd --daemon --port 0 --no-data-dir $MODULES --auth no --log-enable=info+ --log-enable=debug+:Bridge --log-to-file"
start_brokers() {
- ../qpidd --daemon --port 0 --no-data-dir $MODULES --auth no > qpidd.port
- LOCAL_PORT=`cat qpidd.port`
- ../qpidd --daemon --port 0 --no-data-dir $MODULES --auth no > qpidd.port
- REMOTE_PORT=`cat qpidd.port`
-
- ../qpidd --daemon --port 0 --no-data-dir $MODULES --auth no > qpidd.port
- REMOTE_B1=`cat qpidd.port`
- ../qpidd --daemon --port 0 --no-data-dir $MODULES --auth no > qpidd.port
- REMOTE_B2=`cat qpidd.port`
+ rm -f fed_local.log fed_remote.log fed_b1.log fed_b2.log
+ LOCAL_PORT=$($QPIDD_CMD fed_local.log --federation-tag LOCAL)
+ REMOTE_PORT=$($QPIDD_CMD fed_remote.log --federation-tag REMOTE)
+ REMOTE_B1=$($QPIDD_CMD fed_b1.log --federation-tag B1)
+ REMOTE_B2=$($QPIDD_CMD fed_b2.log --federation-tag B2)
}
stop_brokers() {
diff --git a/qpid/doc/book/build.sh b/qpid/cpp/src/tests/run_ha_tests
index 249d2c7439..1a469646c9 100755
--- a/qpid/doc/book/build.sh
+++ b/qpid/cpp/src/tests/run_ha_tests
@@ -1,4 +1,5 @@
-#!/bin/bash -ex
+#!/bin/bash
+
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
@@ -18,23 +19,11 @@
# under the License.
#
-########################################################################
-#
-# Build a PDF from Docbook XML
-#
-# The Makefile is cleaner ....
-#
-########################################################################
-
-rm -rf build
-mkdir -p build
-mkdir -p pdf
-# Assemble all documents using XInclude
-xmllint --xinclude src/Book.xml >build/qpid-book.xml
+# Make sure the python tools are available. They will be if we are building in
+# a checkoug, they may not be in a distribution.
+test -d $PYTHON_COMMANDS -a -x $PYTHON_COMMANDS/qpid-ha -a -x $PYTHON_COMMANDS/qpid-config || { echo "Skipping HA tests, qpid-ha or qpid-config not available."; exit 0; }
-# Create the .fo
-xsltproc /usr/share/sgml/docbook/xsl-stylesheets/fo/docbook.xsl build/qpid-book.xml >build/qpid-book.fo
+srcdir=`dirname $0`
+$srcdir/ha_tests.py
-# Use Apache FOP to create the PDF
-fop build/qpid-book.fo pdf/qpid-book.pdf
diff --git a/qpid/cpp/src/tests/sasl.mk b/qpid/cpp/src/tests/sasl.mk
index 69b24c3f8a..11731dcf40 100644
--- a/qpid/cpp/src/tests/sasl.mk
+++ b/qpid/cpp/src/tests/sasl.mk
@@ -20,18 +20,37 @@
# Test that are only relevant if SASL is enabled.
if HAVE_SASL
+if HAVE_LIBCPG
check_PROGRAMS+=cluster_authentication_soak
cluster_authentication_soak_INCLUDES=$(PUBLIC_INCLUDES)
cluster_authentication_soak_SOURCES=cluster_authentication_soak.cpp ForkedBroker.h ForkedBroker.cpp
cluster_authentication_soak_LDADD=$(lib_client) $(lib_broker)
+endif HAVE_LIBCPG
# Note: sasl_version is not a test -- it is a tool used by tests.
check_PROGRAMS+=sasl_version
sasl_version_SOURCES=sasl_version.cpp
sasl_version_LDADD=$(lib_client)
-TESTS += run_cluster_authentication_test sasl_fed sasl_fed_ex_dynamic sasl_fed_ex_link sasl_fed_ex_queue sasl_fed_ex_route sasl_fed_ex_route_cluster sasl_fed_ex_link_cluster sasl_fed_ex_queue_cluster sasl_fed_ex_dynamic_cluster sasl_no_dir
+TESTS += sasl_fed
+ sasl_fed_ex_dynamic
+ sasl_fed_ex_link
+ sasl_fed_ex_queue
+ sasl_fed_ex_route
+ sasl_no_dir
+
+if HAVE_LIBCPG
+
+TESTS += run_cluster_authentication_test \
+ sasl_fed_ex_route_cluster \
+ sasl_fed_ex_link_cluster \
+ sasl_fed_ex_queue_cluster \
+ sasl_fed_ex_dynamic_cluster
+
LONG_TESTS += run_cluster_authentication_soak
+
+endif HAVE_LIBCPG
+
EXTRA_DIST += run_cluster_authentication_test \
sasl_fed \
sasl_fed_ex \
diff --git a/qpid/cpp/src/tests/sasl_fed_ex_dynamic_cluster b/qpid/cpp/src/tests/sasl_fed_ex_dynamic_cluster
index b0cceccecb..fd6b72a4f2 100755
--- a/qpid/cpp/src/tests/sasl_fed_ex_dynamic_cluster
+++ b/qpid/cpp/src/tests/sasl_fed_ex_dynamic_cluster
@@ -21,7 +21,9 @@
source ./test_env.sh
-source $srcdir/ais_check
+source cpg_check.sh
+cpg_enabled || exit 0
+
with_ais_group ${srcdir}/sasl_fed_ex dynamic cluster
diff --git a/qpid/cpp/src/tests/sasl_fed_ex_link_cluster b/qpid/cpp/src/tests/sasl_fed_ex_link_cluster
index 4139300b12..34b2aa4a5f 100755
--- a/qpid/cpp/src/tests/sasl_fed_ex_link_cluster
+++ b/qpid/cpp/src/tests/sasl_fed_ex_link_cluster
@@ -21,7 +21,8 @@
source ./test_env.sh
-source $srcdir/ais_check
+source cpg_check.sh
+cpg_enabled || exit 0
with_ais_group ${srcdir}/sasl_fed_ex link cluster
diff --git a/qpid/cpp/src/tests/sasl_fed_ex_queue_cluster b/qpid/cpp/src/tests/sasl_fed_ex_queue_cluster
index f251420e08..14f36f6fc4 100755
--- a/qpid/cpp/src/tests/sasl_fed_ex_queue_cluster
+++ b/qpid/cpp/src/tests/sasl_fed_ex_queue_cluster
@@ -21,7 +21,8 @@
source ./test_env.sh
-source ${srcdir}/ais_check
+source cpg_check.sh
+cpg_enabled || exit 0
with_ais_group ${srcdir}/sasl_fed_ex queue cluster
diff --git a/qpid/cpp/src/tests/sasl_fed_ex_route_cluster b/qpid/cpp/src/tests/sasl_fed_ex_route_cluster
index a5d1542def..756476056e 100755
--- a/qpid/cpp/src/tests/sasl_fed_ex_route_cluster
+++ b/qpid/cpp/src/tests/sasl_fed_ex_route_cluster
@@ -21,7 +21,8 @@
source ./test_env.sh
-source ${srcdir}/ais_check
+source cpg_check.sh
+cpg_enabled || exit 0
with_ais_group ${srcdir}/sasl_fed_ex route cluster
diff --git a/qpid/cpp/src/tests/sasl_test_setup.sh b/qpid/cpp/src/tests/sasl_test_setup.sh
index 3e69c0f02b..3947986517 100755
--- a/qpid/cpp/src/tests/sasl_test_setup.sh
+++ b/qpid/cpp/src/tests/sasl_test_setup.sh
@@ -30,7 +30,7 @@ pwcheck_method: auxprop
auxprop_plugin: sasldb
sasldb_path: $PWD/sasl_config/qpidd.sasldb
sql_select: dummy select
-mech_list: ANONYMOUS PLAIN DIGEST-MD5 EXTERNAL
+mech_list: ANONYMOUS PLAIN DIGEST-MD5 EXTERNAL CRAM-MD5
EOF
# Populate temporary sasl db.
diff --git a/qpid/cpp/src/tests/ssl_test b/qpid/cpp/src/tests/ssl_test
index fc634ba242..91ff8eec1e 100755
--- a/qpid/cpp/src/tests/ssl_test
+++ b/qpid/cpp/src/tests/ssl_test
@@ -100,8 +100,10 @@ start_ssl_mux_broker() {
PORTS=( ${PORTS[@]} $1 )
}
+sasl_config_dir=$builddir/sasl_config
+
start_authenticating_broker() {
- start_brokers 1 "--transport ssl --ssl-port 0 --require-encryption --ssl-sasl-no-dict --ssl-require-client-authentication --auth yes"
+ start_brokers 1 "--transport ssl --ssl-port 0 --require-encryption --ssl-sasl-no-dict --ssl-require-client-authentication --auth yes --sasl-config=${sasl_config_dir}"
}
ssl_cluster_broker() { # $1 = port
@@ -190,7 +192,8 @@ stop_brokers
test -z $CLUSTER_LIB && exit 0 # Exit if cluster not supported.
## Test failover in a cluster using SSL only
-. $srcdir/ais_check # Will exit if clustering not enabled.
+source cpg_check.sh
+cpg_enabled || exit 0
PORT1=`pick_port`; ssl_cluster_broker $PORT1
echo "Running SSL cluster broker on port $PORT1"
diff --git a/qpid/cpp/src/tests/start_cluster b/qpid/cpp/src/tests/start_cluster
index 84f98b3b2d..78fd104d9c 100755
--- a/qpid/cpp/src/tests/start_cluster
+++ b/qpid/cpp/src/tests/start_cluster
@@ -24,7 +24,8 @@
# Execute command with the ais group set.
source ./test_env.sh
-. `dirname $0`/ais_check
+source cpg_check.sh
+cpg_enabled || exit 0
rm -f cluster*.log cluster.ports qpidd.port
diff --git a/qpid/cpp/src/tests/test_env.sh.in b/qpid/cpp/src/tests/test_env.sh.in
index 5c07bcdc2e..cee36843ae 100644
--- a/qpid/cpp/src/tests/test_env.sh.in
+++ b/qpid/cpp/src/tests/test_env.sh.in
@@ -75,6 +75,7 @@ exportmodule XML_LIB xml.so
# Qpid options
export QPID_NO_MODULE_DIR=1 # Don't accidentally load installed modules
export QPID_DATA_DIR=
+export QPID_CONFIG=$srcdir/qpidd-empty.conf
# Use temporary directory if $HOME does not exist
if [ ! -e "$HOME" ]; then
diff --git a/qpid/cpp/src/tests/testagent.cpp b/qpid/cpp/src/tests/testagent.cpp
index 98520b424a..e6010a8e00 100644
--- a/qpid/cpp/src/tests/testagent.cpp
+++ b/qpid/cpp/src/tests/testagent.cpp
@@ -36,9 +36,12 @@
#include <sstream>
+namespace qpid {
+namespace tests {
+
static bool running = true;
-using namespace std;
+using std::string;
using qpid::management::ManagementAgent;
using qpid::management::ManagementObject;
using qpid::management::Manageable;
@@ -191,12 +194,14 @@ int main_int(int argc, char** argv)
return 0;
}
+}} // namespace qpid::tests
+
int main(int argc, char** argv)
{
try {
- return main_int(argc, argv);
+ return qpid::tests::main_int(argc, argv);
} catch(std::exception& e) {
- cerr << "Top Level Exception: " << e.what() << endl;
+ std::cerr << "Top Level Exception: " << e.what() << std::endl;
return 1;
}
}
diff --git a/qpid/cpp/src/tests/txjob.cpp b/qpid/cpp/src/tests/txjob.cpp
index a7a905c1b7..29394c3415 100644
--- a/qpid/cpp/src/tests/txjob.cpp
+++ b/qpid/cpp/src/tests/txjob.cpp
@@ -38,9 +38,9 @@ namespace tests {
struct Args : public qpid::TestOptions
{
- string workQueue;
- string source;
- string dest;
+ std::string workQueue;
+ std::string source;
+ std::string dest;
uint messages;
uint jobs;
bool quit;
diff --git a/qpid/cpp/src/tests/txshift.cpp b/qpid/cpp/src/tests/txshift.cpp
index 882d3716d8..bf85bee986 100644
--- a/qpid/cpp/src/tests/txshift.cpp
+++ b/qpid/cpp/src/tests/txshift.cpp
@@ -39,7 +39,7 @@ namespace tests {
struct Args : public qpid::TestOptions
{
- string workQueue;
+ std::string workQueue;
size_t workers;
Args() : workQueue("txshift-control"), workers(1)
diff --git a/qpid/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp b/qpid/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp
index 024f20b147..14f1e46606 100644
--- a/qpid/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp
+++ b/qpid/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp
@@ -32,7 +32,9 @@
#include <windows.h>
#include <iostream>
-namespace {
+namespace qpid {
+namespace tests {
+namespace windows {
// Instead of popping up a window for exceptions, just print something out
LONG _stdcall UnhandledExceptionFilter (PEXCEPTION_POINTERS pExceptionInfo)
@@ -73,4 +75,4 @@ redirect_errors_to_stderr::redirect_errors_to_stderr()
SetUnhandledExceptionFilter (&UnhandledExceptionFilter);
}
-} // namespace
+}}} // namespace
diff --git a/qpid/cpp/src/windows/QpiddBroker.cpp b/qpid/cpp/src/windows/QpiddBroker.cpp
index 42ba97bdb1..89a8945d00 100644
--- a/qpid/cpp/src/windows/QpiddBroker.cpp
+++ b/qpid/cpp/src/windows/QpiddBroker.cpp
@@ -32,11 +32,12 @@
#include <iostream>
#include <windows.h>
-using namespace qpid::broker;
+namespace qpid {
+namespace broker {
BootstrapOptions::BootstrapOptions(const char* argv0)
: qpid::Options("Options"),
- common("", QPIDD_CONF_FILE),
+ common("", QPIDD_CONF_FILE, QPIDC_CONF_FILE),
module(QPIDD_MODULE_DIR),
log(argv0)
{
@@ -314,7 +315,7 @@ struct QpiddWindowsOptions : public QpiddOptionsPrivate {
QpiddOptions::QpiddOptions(const char* argv0)
: qpid::Options("Options"),
- common("", QPIDD_CONF_FILE),
+ common("", QPIDD_CONF_FILE, QPIDC_CONF_FILE),
module(QPIDD_MODULE_DIR),
log(argv0)
{
@@ -451,6 +452,7 @@ int QpiddBroker::execute (QpiddOptions *options) {
return 0;
}
+}} // namespace qpid::broker
int main(int argc, char* argv[])
{
@@ -459,13 +461,13 @@ int main(int argc, char* argv[])
// the service is stopped.
SERVICE_TABLE_ENTRY dispatchTable[] =
{
- { "", (LPSERVICE_MAIN_FUNCTION)ServiceMain },
+ { "", (LPSERVICE_MAIN_FUNCTION)qpid::broker::ServiceMain },
{ NULL, NULL }
};
if (!StartServiceCtrlDispatcher(dispatchTable)) {
DWORD err = ::GetLastError();
if (err == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) // Run as console
- return run_broker(argc, argv);
+ return qpid::broker::run_broker(argc, argv);
throw QPID_WINDOWS_ERROR(err);
}
return 0;
diff --git a/qpid/cpp/src/windows/SCM.cpp b/qpid/cpp/src/windows/SCM.cpp
index 232bb04c17..2eeb143427 100644
--- a/qpid/cpp/src/windows/SCM.cpp
+++ b/qpid/cpp/src/windows/SCM.cpp
@@ -1,332 +1,332 @@
-/*
- *
- * 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.
- *
- */
-
-#include "qpid/log/Statement.h"
-#include "qpid/sys/windows/check.h"
-#include "SCM.h"
-
-#pragma comment(lib, "advapi32.lib")
-
-namespace {
-
-// Container that will close a SC_HANDLE upon destruction.
-class AutoServiceHandle {
-public:
- AutoServiceHandle(SC_HANDLE h_ = NULL) : h(h_) {}
- ~AutoServiceHandle() { if (h != NULL) ::CloseServiceHandle(h); }
- void release() { h = NULL; }
- void reset(SC_HANDLE newHandle)
- {
- if (h != NULL)
- ::CloseServiceHandle(h);
- h = newHandle;
- }
- operator SC_HANDLE() const { return h; }
-
-private:
- SC_HANDLE h;
-};
-
-}
-
-namespace qpid {
-namespace windows {
-
-SCM::SCM() : scmHandle(NULL)
-{
-}
-
-SCM::~SCM()
-{
- if (NULL != scmHandle)
- ::CloseServiceHandle(scmHandle);
-}
-
-/**
- * Install this executable as a service
- */
-void SCM::install(const string& serviceName,
- const string& serviceDesc,
- const string& args,
- DWORD startType,
- const string& account,
- const string& password,
- const string& depends)
-{
- // Handle dependent service name list; Windows wants a set of nul-separated
- // names ending with a double nul.
- string depends2 = depends;
- if (!depends2.empty()) {
- // CDL to null delimiter w/ trailing double null
- size_t p = 0;
- while ((p = depends2.find_first_of( ',', p)) != string::npos)
- depends2.replace(p, 1, 1, '\0');
- depends2.push_back('\0');
- depends2.push_back('\0');
- }
-
-#if 0
- // I'm nervous about adding a user/password check here. Is this a
- // potential attack vector, letting users check passwords without
- // control? -Steve Huston, Feb 24, 2011
-
- // Validate account, password
- HANDLE hToken = NULL;
- bool logStatus = false;
- if (!account.empty() && !password.empty() &&
- !(logStatus = ::LogonUserA(account.c_str(),
- "",
- password.c_str(),
- LOGON32_LOGON_NETWORK,
- LOGON32_PROVIDER_DEFAULT,
- &hToken ) != 0))
- std::cout << "warning: supplied account & password failed with LogonUser." << std::endl;
- if (logStatus)
- ::CloseHandle(hToken);
-#endif
-
- // Get fully qualified .exe name
- char myPath[MAX_PATH];
- DWORD myPathLength = ::GetModuleFileName(NULL, myPath, MAX_PATH);
- QPID_WINDOWS_CHECK_NOT(myPathLength, 0);
- string imagePath(myPath, myPathLength);
- if (!args.empty())
- imagePath += " " + args;
-
- // Ensure there's a handle to the SCM database.
- openSvcManager();
-
- // Create the service
- SC_HANDLE svcHandle;
- svcHandle = ::CreateService(scmHandle, // SCM database
- serviceName.c_str(), // name of service
- serviceDesc.c_str(), // name to display
- SERVICE_ALL_ACCESS, // desired access
- SERVICE_WIN32_OWN_PROCESS, // service type
- startType, // start type
- SERVICE_ERROR_NORMAL, // error cntrl type
- imagePath.c_str(), // path to service's binary w/ optional arguments
- NULL, // no load ordering group
- NULL, // no tag identifier
- depends2.empty() ? NULL : depends2.c_str(),
- account.empty() ? NULL : account.c_str(), // account name, or NULL for LocalSystem
- password.empty() ? NULL : password.c_str()); // password, or NULL for none
- QPID_WINDOWS_CHECK_NULL(svcHandle);
- ::CloseServiceHandle(svcHandle);
- QPID_LOG(info, "Service installed successfully");
-}
-
-/**
- *
- */
-void SCM::uninstall(const string& serviceName)
-{
- // Ensure there's a handle to the SCM database.
- openSvcManager();
- AutoServiceHandle svc(::OpenService(scmHandle,
- serviceName.c_str(),
- DELETE));
- QPID_WINDOWS_CHECK_NULL((SC_HANDLE)svc);
- QPID_WINDOWS_CHECK_NOT(::DeleteService(svc), 0);
- QPID_LOG(info, "Service deleted successfully.");
-}
-
-/**
- * Attempt to start the service.
- */
-void SCM::start(const string& serviceName)
-{
- // Ensure we have a handle to the SCM database.
- openSvcManager();
-
- // Get a handle to the service.
- AutoServiceHandle svc(::OpenService(scmHandle,
- serviceName.c_str(),
- SERVICE_ALL_ACCESS));
- QPID_WINDOWS_CHECK_NULL(svc);
-
- // Check the status in case the service is not stopped.
- DWORD state = waitForStateChangeFrom(svc, SERVICE_STOP_PENDING);
- if (state == SERVICE_STOP_PENDING)
- throw qpid::Exception("Timed out waiting for running service to stop.");
-
- // Attempt to start the service.
- QPID_WINDOWS_CHECK_NOT(::StartService(svc, 0, NULL), 0);
-
- QPID_LOG(info, "Service start pending...");
-
- // Check the status until the service is no longer start pending.
- state = waitForStateChangeFrom(svc, SERVICE_START_PENDING);
- // Determine whether the service is running.
- if (state == SERVICE_RUNNING) {
- QPID_LOG(info, "Service started successfully");
- }
- else {
- throw qpid::Exception(QPID_MSG("Service not yet running; state now " << state));
- }
-}
-
-/**
- *
- */
-void SCM::stop(const string& serviceName)
-{
- // Ensure a handle to the SCM database.
- openSvcManager();
-
- // Get a handle to the service.
- AutoServiceHandle svc(::OpenService(scmHandle,
- serviceName.c_str(),
- SERVICE_STOP | SERVICE_QUERY_STATUS |
- SERVICE_ENUMERATE_DEPENDENTS));
- QPID_WINDOWS_CHECK_NULL(svc);
-
- // Make sure the service is not already stopped; if it's stop-pending,
- // wait for it to finalize.
- DWORD state = waitForStateChangeFrom(svc, SERVICE_STOP_PENDING);
- if (state == SERVICE_STOPPED) {
- QPID_LOG(info, "Service is already stopped");
- return;
- }
-
- // If the service is running, dependencies must be stopped first.
- std::auto_ptr<ENUM_SERVICE_STATUS> deps;
- DWORD numDeps = getDependentServices(svc, deps);
- for (DWORD i = 0; i < numDeps; i++)
- stop(deps.get()[i].lpServiceName);
-
- // Dependents stopped; send a stop code to the service.
- SERVICE_STATUS_PROCESS ssp;
- if (!::ControlService(svc, SERVICE_CONTROL_STOP, (LPSERVICE_STATUS)&ssp))
- throw qpid::Exception(QPID_MSG("Stopping " << serviceName << ": " <<
- qpid::sys::strError(::GetLastError())));
-
- // Wait for the service to stop.
- state = waitForStateChangeFrom(svc, SERVICE_STOP_PENDING);
- if (state == SERVICE_STOPPED)
- QPID_LOG(info, QPID_MSG("Service " << serviceName <<
- " stopped successfully."));
-}
-
-/**
- *
- */
-void SCM::openSvcManager()
-{
- if (NULL != scmHandle)
- return;
-
- scmHandle = ::OpenSCManager(NULL, // local computer
- NULL, // ServicesActive database
- SC_MANAGER_ALL_ACCESS); // Rights
- QPID_WINDOWS_CHECK_NULL(scmHandle);
-}
-
-DWORD SCM::waitForStateChangeFrom(SC_HANDLE svc, DWORD originalState)
-{
- SERVICE_STATUS_PROCESS ssStatus;
- DWORD bytesNeeded;
- DWORD waitTime;
- if (!::QueryServiceStatusEx(svc, // handle to service
- SC_STATUS_PROCESS_INFO, // information level
- (LPBYTE)&ssStatus, // address of structure
- sizeof(ssStatus), // size of structure
- &bytesNeeded)) // size needed if buffer is too small
- throw QPID_WINDOWS_ERROR(::GetLastError());
-
- // Save the tick count and initial checkpoint.
- DWORD startTickCount = ::GetTickCount();
- DWORD oldCheckPoint = ssStatus.dwCheckPoint;
-
- // Wait for the service to change out of the noted state.
- while (ssStatus.dwCurrentState == originalState) {
- // Do not wait longer than the wait hint. A good interval is
- // one-tenth of the wait hint but not less than 1 second
- // and not more than 10 seconds.
- waitTime = ssStatus.dwWaitHint / 10;
- if (waitTime < 1000)
- waitTime = 1000;
- else if (waitTime > 10000)
- waitTime = 10000;
-
- ::Sleep(waitTime);
-
- // Check the status until the service is no longer stop pending.
- if (!::QueryServiceStatusEx(svc,
- SC_STATUS_PROCESS_INFO,
- (LPBYTE) &ssStatus,
- sizeof(ssStatus),
- &bytesNeeded))
- throw QPID_WINDOWS_ERROR(::GetLastError());
-
- if (ssStatus.dwCheckPoint > oldCheckPoint) {
- // Continue to wait and check.
- startTickCount = ::GetTickCount();
- oldCheckPoint = ssStatus.dwCheckPoint;
- } else {
- if ((::GetTickCount() - startTickCount) > ssStatus.dwWaitHint)
- break;
- }
- }
- return ssStatus.dwCurrentState;
-}
-
-/**
- * Get the services that depend on @arg svc. All dependent service info
- * is returned in an array of ENUM_SERVICE_STATUS structures via @arg deps.
- *
- * @retval The number of dependent services.
- */
-DWORD SCM::getDependentServices(SC_HANDLE svc,
- std::auto_ptr<ENUM_SERVICE_STATUS>& deps)
-{
- DWORD bytesNeeded;
- DWORD numEntries;
-
- // Pass a zero-length buffer to get the required buffer size.
- if (::EnumDependentServices(svc,
- SERVICE_ACTIVE,
- 0,
- 0,
- &bytesNeeded,
- &numEntries)) {
- // If the Enum call succeeds, then there are no dependent
- // services, so do nothing.
- return 0;
- }
-
- if (::GetLastError() != ERROR_MORE_DATA)
- throw QPID_WINDOWS_ERROR((::GetLastError()));
-
- // Allocate a buffer for the dependencies.
- deps.reset((LPENUM_SERVICE_STATUS)(new char[bytesNeeded]));
- // Enumerate the dependencies.
- if (!::EnumDependentServices(svc,
- SERVICE_ACTIVE,
- deps.get(),
- bytesNeeded,
- &bytesNeeded,
- &numEntries))
- throw QPID_WINDOWS_ERROR((::GetLastError()));
- return numEntries;
-}
-
-} } // namespace qpid::windows
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/log/Statement.h"
+#include "qpid/sys/windows/check.h"
+#include "SCM.h"
+
+#pragma comment(lib, "advapi32.lib")
+
+namespace qpid {
+namespace windows {
+
+namespace {
+
+// Container that will close a SC_HANDLE upon destruction.
+class AutoServiceHandle {
+public:
+ AutoServiceHandle(SC_HANDLE h_ = NULL) : h(h_) {}
+ ~AutoServiceHandle() { if (h != NULL) ::CloseServiceHandle(h); }
+ void release() { h = NULL; }
+ void reset(SC_HANDLE newHandle)
+ {
+ if (h != NULL)
+ ::CloseServiceHandle(h);
+ h = newHandle;
+ }
+ operator SC_HANDLE() const { return h; }
+
+private:
+ SC_HANDLE h;
+};
+
+}
+
+SCM::SCM() : scmHandle(NULL)
+{
+}
+
+SCM::~SCM()
+{
+ if (NULL != scmHandle)
+ ::CloseServiceHandle(scmHandle);
+}
+
+/**
+ * Install this executable as a service
+ */
+void SCM::install(const string& serviceName,
+ const string& serviceDesc,
+ const string& args,
+ DWORD startType,
+ const string& account,
+ const string& password,
+ const string& depends)
+{
+ // Handle dependent service name list; Windows wants a set of nul-separated
+ // names ending with a double nul.
+ string depends2 = depends;
+ if (!depends2.empty()) {
+ // CDL to null delimiter w/ trailing double null
+ size_t p = 0;
+ while ((p = depends2.find_first_of( ',', p)) != string::npos)
+ depends2.replace(p, 1, 1, '\0');
+ depends2.push_back('\0');
+ depends2.push_back('\0');
+ }
+
+#if 0
+ // I'm nervous about adding a user/password check here. Is this a
+ // potential attack vector, letting users check passwords without
+ // control? -Steve Huston, Feb 24, 2011
+
+ // Validate account, password
+ HANDLE hToken = NULL;
+ bool logStatus = false;
+ if (!account.empty() && !password.empty() &&
+ !(logStatus = ::LogonUserA(account.c_str(),
+ "",
+ password.c_str(),
+ LOGON32_LOGON_NETWORK,
+ LOGON32_PROVIDER_DEFAULT,
+ &hToken ) != 0))
+ std::cout << "warning: supplied account & password failed with LogonUser." << std::endl;
+ if (logStatus)
+ ::CloseHandle(hToken);
+#endif
+
+ // Get fully qualified .exe name
+ char myPath[MAX_PATH];
+ DWORD myPathLength = ::GetModuleFileName(NULL, myPath, MAX_PATH);
+ QPID_WINDOWS_CHECK_NOT(myPathLength, 0);
+ string imagePath(myPath, myPathLength);
+ if (!args.empty())
+ imagePath += " " + args;
+
+ // Ensure there's a handle to the SCM database.
+ openSvcManager();
+
+ // Create the service
+ SC_HANDLE svcHandle;
+ svcHandle = ::CreateService(scmHandle, // SCM database
+ serviceName.c_str(), // name of service
+ serviceDesc.c_str(), // name to display
+ SERVICE_ALL_ACCESS, // desired access
+ SERVICE_WIN32_OWN_PROCESS, // service type
+ startType, // start type
+ SERVICE_ERROR_NORMAL, // error cntrl type
+ imagePath.c_str(), // path to service's binary w/ optional arguments
+ NULL, // no load ordering group
+ NULL, // no tag identifier
+ depends2.empty() ? NULL : depends2.c_str(),
+ account.empty() ? NULL : account.c_str(), // account name, or NULL for LocalSystem
+ password.empty() ? NULL : password.c_str()); // password, or NULL for none
+ QPID_WINDOWS_CHECK_NULL(svcHandle);
+ ::CloseServiceHandle(svcHandle);
+ QPID_LOG(info, "Service installed successfully");
+}
+
+/**
+ *
+ */
+void SCM::uninstall(const string& serviceName)
+{
+ // Ensure there's a handle to the SCM database.
+ openSvcManager();
+ AutoServiceHandle svc(::OpenService(scmHandle,
+ serviceName.c_str(),
+ DELETE));
+ QPID_WINDOWS_CHECK_NULL((SC_HANDLE)svc);
+ QPID_WINDOWS_CHECK_NOT(::DeleteService(svc), 0);
+ QPID_LOG(info, "Service deleted successfully.");
+}
+
+/**
+ * Attempt to start the service.
+ */
+void SCM::start(const string& serviceName)
+{
+ // Ensure we have a handle to the SCM database.
+ openSvcManager();
+
+ // Get a handle to the service.
+ AutoServiceHandle svc(::OpenService(scmHandle,
+ serviceName.c_str(),
+ SERVICE_ALL_ACCESS));
+ QPID_WINDOWS_CHECK_NULL(svc);
+
+ // Check the status in case the service is not stopped.
+ DWORD state = waitForStateChangeFrom(svc, SERVICE_STOP_PENDING);
+ if (state == SERVICE_STOP_PENDING)
+ throw qpid::Exception("Timed out waiting for running service to stop.");
+
+ // Attempt to start the service.
+ QPID_WINDOWS_CHECK_NOT(::StartService(svc, 0, NULL), 0);
+
+ QPID_LOG(info, "Service start pending...");
+
+ // Check the status until the service is no longer start pending.
+ state = waitForStateChangeFrom(svc, SERVICE_START_PENDING);
+ // Determine whether the service is running.
+ if (state == SERVICE_RUNNING) {
+ QPID_LOG(info, "Service started successfully");
+ }
+ else {
+ throw qpid::Exception(QPID_MSG("Service not yet running; state now " << state));
+ }
+}
+
+/**
+ *
+ */
+void SCM::stop(const string& serviceName)
+{
+ // Ensure a handle to the SCM database.
+ openSvcManager();
+
+ // Get a handle to the service.
+ AutoServiceHandle svc(::OpenService(scmHandle,
+ serviceName.c_str(),
+ SERVICE_STOP | SERVICE_QUERY_STATUS |
+ SERVICE_ENUMERATE_DEPENDENTS));
+ QPID_WINDOWS_CHECK_NULL(svc);
+
+ // Make sure the service is not already stopped; if it's stop-pending,
+ // wait for it to finalize.
+ DWORD state = waitForStateChangeFrom(svc, SERVICE_STOP_PENDING);
+ if (state == SERVICE_STOPPED) {
+ QPID_LOG(info, "Service is already stopped");
+ return;
+ }
+
+ // If the service is running, dependencies must be stopped first.
+ std::auto_ptr<ENUM_SERVICE_STATUS> deps;
+ DWORD numDeps = getDependentServices(svc, deps);
+ for (DWORD i = 0; i < numDeps; i++)
+ stop(deps.get()[i].lpServiceName);
+
+ // Dependents stopped; send a stop code to the service.
+ SERVICE_STATUS_PROCESS ssp;
+ if (!::ControlService(svc, SERVICE_CONTROL_STOP, (LPSERVICE_STATUS)&ssp))
+ throw qpid::Exception(QPID_MSG("Stopping " << serviceName << ": " <<
+ qpid::sys::strError(::GetLastError())));
+
+ // Wait for the service to stop.
+ state = waitForStateChangeFrom(svc, SERVICE_STOP_PENDING);
+ if (state == SERVICE_STOPPED)
+ QPID_LOG(info, QPID_MSG("Service " << serviceName <<
+ " stopped successfully."));
+}
+
+/**
+ *
+ */
+void SCM::openSvcManager()
+{
+ if (NULL != scmHandle)
+ return;
+
+ scmHandle = ::OpenSCManager(NULL, // local computer
+ NULL, // ServicesActive database
+ SC_MANAGER_ALL_ACCESS); // Rights
+ QPID_WINDOWS_CHECK_NULL(scmHandle);
+}
+
+DWORD SCM::waitForStateChangeFrom(SC_HANDLE svc, DWORD originalState)
+{
+ SERVICE_STATUS_PROCESS ssStatus;
+ DWORD bytesNeeded;
+ DWORD waitTime;
+ if (!::QueryServiceStatusEx(svc, // handle to service
+ SC_STATUS_PROCESS_INFO, // information level
+ (LPBYTE)&ssStatus, // address of structure
+ sizeof(ssStatus), // size of structure
+ &bytesNeeded)) // size needed if buffer is too small
+ throw QPID_WINDOWS_ERROR(::GetLastError());
+
+ // Save the tick count and initial checkpoint.
+ DWORD startTickCount = ::GetTickCount();
+ DWORD oldCheckPoint = ssStatus.dwCheckPoint;
+
+ // Wait for the service to change out of the noted state.
+ while (ssStatus.dwCurrentState == originalState) {
+ // Do not wait longer than the wait hint. A good interval is
+ // one-tenth of the wait hint but not less than 1 second
+ // and not more than 10 seconds.
+ waitTime = ssStatus.dwWaitHint / 10;
+ if (waitTime < 1000)
+ waitTime = 1000;
+ else if (waitTime > 10000)
+ waitTime = 10000;
+
+ ::Sleep(waitTime);
+
+ // Check the status until the service is no longer stop pending.
+ if (!::QueryServiceStatusEx(svc,
+ SC_STATUS_PROCESS_INFO,
+ (LPBYTE) &ssStatus,
+ sizeof(ssStatus),
+ &bytesNeeded))
+ throw QPID_WINDOWS_ERROR(::GetLastError());
+
+ if (ssStatus.dwCheckPoint > oldCheckPoint) {
+ // Continue to wait and check.
+ startTickCount = ::GetTickCount();
+ oldCheckPoint = ssStatus.dwCheckPoint;
+ } else {
+ if ((::GetTickCount() - startTickCount) > ssStatus.dwWaitHint)
+ break;
+ }
+ }
+ return ssStatus.dwCurrentState;
+}
+
+/**
+ * Get the services that depend on @arg svc. All dependent service info
+ * is returned in an array of ENUM_SERVICE_STATUS structures via @arg deps.
+ *
+ * @retval The number of dependent services.
+ */
+DWORD SCM::getDependentServices(SC_HANDLE svc,
+ std::auto_ptr<ENUM_SERVICE_STATUS>& deps)
+{
+ DWORD bytesNeeded;
+ DWORD numEntries;
+
+ // Pass a zero-length buffer to get the required buffer size.
+ if (::EnumDependentServices(svc,
+ SERVICE_ACTIVE,
+ 0,
+ 0,
+ &bytesNeeded,
+ &numEntries)) {
+ // If the Enum call succeeds, then there are no dependent
+ // services, so do nothing.
+ return 0;
+ }
+
+ if (::GetLastError() != ERROR_MORE_DATA)
+ throw QPID_WINDOWS_ERROR((::GetLastError()));
+
+ // Allocate a buffer for the dependencies.
+ deps.reset((LPENUM_SERVICE_STATUS)(new char[bytesNeeded]));
+ // Enumerate the dependencies.
+ if (!::EnumDependentServices(svc,
+ SERVICE_ACTIVE,
+ deps.get(),
+ bytesNeeded,
+ &bytesNeeded,
+ &numEntries))
+ throw QPID_WINDOWS_ERROR((::GetLastError()));
+ return numEntries;
+}
+
+} } // namespace qpid::windows
diff --git a/qpid/cpp/xml/cluster.xml b/qpid/cpp/xml/cluster.xml
index 7b3f2fe63b..09434ea37b 100644
--- a/qpid/cpp/xml/cluster.xml
+++ b/qpid/cpp/xml/cluster.xml
@@ -179,6 +179,7 @@
<field name="position" type="sequence-no"/>
<field name="used-msg-credit" type="uint32"/>
<field name="used-byte-credit" type="uint32"/>
+ <field name="deliveryCount" type="uint32"/>
</control>
<!-- Delivery-record for outgoing messages sent but not yet accepted. -->
@@ -326,6 +327,12 @@
<field name="dequeueSincePurge" type="uint32"/>
</control>
+ <!-- Replicate the internal state for an object - e.g. Links, bridges, etc -->
+ <control name="internal-state" code="0x42">
+ <field name="type" type="str8"/> <!-- The type of object the state is for (e.g. 'link') -->
+ <field name="name" type="str8"/> <!-- Identifies the particular object to be updated -->
+ <field name="state" type="map"/> <!-- The internal state for the object -->
+ </control>
</class>
diff --git a/qpid/doc/book/Makefile b/qpid/doc/book/Makefile
index db160ac6f8..8a3d4974db 100644
--- a/qpid/doc/book/Makefile
+++ b/qpid/doc/book/Makefile
@@ -17,37 +17,22 @@
# under the License.
#
-#
-# This Makefile requires the following:
-#
-# Apache FOP, version 0.95 or higher
-# Docbook 4.5
-# Docbook XSL stylesheets - tested with xsl-stylesheets (1.75.2 and 1.65.1-2)
-# xsltproc
-# xmllint
-#
-
-all: cpp java programming
+DIRS = src/java-broker src/cpp-broker src/programming
-cpp: build/AMQP-Messaging-Broker-CPP-Book
-java: build/AMQP-Messaging-Broker-Java-Book
+.PHONY: all $(DIRS)
-programming: build/Programming-In-Apache-Qpid
+all: $(DIRS)
-qmf: build/QmfBook
-
-build/AMQP-Messaging-Broker-CPP-Book:
- ./build-book.sh AMQP-Messaging-Broker-CPP-Book
+clean:
+ rm -rf build
-build/AMQP-Messaging-Broker-Java-Book:
- ./build-book.sh AMQP-Messaging-Broker-Java-Book
+html: TARGET = html
+html: all
-build/Programming-In-Apache-Qpid:
- ./build-book.sh Programming-In-Apache-Qpid
+pdf: TARGET = pdf
+pdf: all
-build/QmfBook: src/QmfBook.xml src/QmfIntroduction.xml
- ./build-book.sh QmfBook
+$(DIRS):
+ $(MAKE) -C $@ $(TARGET) OUTPUTDIR=../../build/
-clean:
- rm -rf build
diff --git a/qpid/doc/book/README.txt b/qpid/doc/book/README.txt
deleted file mode 100644
index 1fc99c94a0..0000000000
--- a/qpid/doc/book/README.txt
+++ /dev/null
@@ -1,118 +0,0 @@
-The documentation in this directory is written in DocBook 4.5. The
-original content was taken from the Apache Qpid Wiki.
-
-1. Building the Documentation
-
-You need the following to build the documentation:
-
-- Apache FOP, version 0.95 or higher
-- Docbook 4.5
-- Docbook XSL stylesheets - Tested with xsl-stylesheets (1.75.2 & 1.65.1-2)
-- xsltproc
-- xmllint
-
-On many Linux machines, these can usually be installed from standard
-repos. For instance, on Fedora they can be installed as follows:
-
-$ sudo yum install fop docbook-dtds docbook-style-xsl libxslt libxml2
-
-After installing, use make to build the documentation:
-
-$ make
-
-The Makefile supports the following targets:
-
-all Builds the cpp, java, and programming targets.
-cpp Build html+pdf for the C++ broker.
-java Build html+pdf for the Java broker.
-programming Build html+pdf for Programming In Apache Qpid.
-clean Delete the build directory
-
-
-You will see quite a few error messages. Many of these are due to
-unresolved links, and these should go away. Many are due to the
-verbosity of Apache FOP, which generates many warnings.
-
-2. Editing Tools
-
-For Emacs, I like nxml-mode, especially if you learn how to use tag
-completion, outlining, etc. This is described in some detail in
-http://www.dpawson.co.uk/relaxng/nxml/info.html.
-
-For vi, the macros described in this Linux Journal article may be
-helpful: http://www.linuxjournal.com/article/7737.
-
-Commercial XML editors provide good support for DocBook. On Windows, I
-like Stylus Studio (http://www.stylusstudio.com/). On Linux, I like
-Oxygen (http://www.oxygenxml.com/).
-
-Here's a page on authoring tools for DocBook:
-http://wiki.docbook.org/topic/DocBookAuthoringTools
-
-
-3. File Structure
-
-The source files are in qpid/doc/book/src.
-
-The following XInclude tree shows the organization of files in the
-document.
-
-Book.xml
- Book-Info.xml
- Introduction.xml
- AMQP.xml
- Getting-Started.xml
- Download.xml
- AMQP-Messaging-Broker-CPP.xml
- Running-CPP-Broker.xml
- Cheat-Sheet-for-configuring-Queue-Options.xml
- Cheat-Sheet-for-configuring-Exchange-Options.xml
- Using-Broker-Federation.xml
- SSL.xml
- LVQ.xml
- queue-state-replication.xml
- Starting-a-cluster.xml
- ACL.xml
- Managing-CPP-Broker.xml
- QMan-Qpid-Management-bridge.xml
- Qpid-Management-Framework.xml
- Management-Design-notes.xml
- QMF-Python-Console-Tutorial.xml
- AMQP-Messaging-Broker-Java.xml
- Java-Broker-Feature-Guide.xml
- Qpid-Java-FAQ.xml
- Java-Environment-Variables.xml
- Qpid-Troubleshooting-Guide.xml
- Add-New-Users.xml
- Configure-ACLs.xml
- Configure-Java-Qpid-to-use-a-SSL-connection.xml
- Configure-Log4j-CompositeRolling-Appender.xml
- Configure-the-Broker-via-config.xml.xml
- Configure-the-Virtual-Hosts-via-virtualhosts.xml.xml
- Debug-using-log4j.xml
- How-to-Tune-M3-Java-Broker-Performance.xml
- Qpid-Java-Build-How-To.xml
- Use-Priority-Queues.xml
- Qpid-JMX-Management-Console.xml
- Configuring-Management-Users.xml
- Configuring-Qpid-JMX-Management-Console.xml
- Management-Console-Security.xml
- Qpid-JMX-Management-Console-FAQ.xml
- Qpid-JMX-Management-Console-User-Guide.xml
- Qpid-Management-Features.xml
- MessageStore-Tool.xml
- Qpid-Java-Broker-Management-CLI.xml
- AMQP-Java-JMS-Messaging-Client.xml
- System-Properties.xml
- Connection-URL-Format.xml
- Binding-URL-Format.xml
- AMQP-C++-Messaging-Client.xml
- AMQP-.NET-Messaging-Client.xml
- NET-User-Guide.xml
- Excel-AddIn.xml
- WCF.xml
- AMQP-Python-Messaging-Client.xml
- PythonBrokerTest.xml
- AMQP-Ruby-Messaging-Client.xml
- AMQP-Compatibility.xml
- Qpid-Interoperability-Documentation.xml
diff --git a/qpid/doc/book/build-book.sh b/qpid/doc/book/build-book.sh
deleted file mode 100755
index 5cc02b9fd8..0000000000
--- a/qpid/doc/book/build-book.sh
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/bin/bash -ex
-#
-# 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.
-#
-
-########################################################################
-#
-# Build a PDF and HTML for a single chapter or section
-#
-# Specify the name of the XML file on the command line, omitting
-# the file extension, e.g.:
-#
-# $ ./build-chapter.sh src/High-Level-API
-#
-########################################################################
-
-# DOCBOOK XSL STYLESHEET LOCATION
-# Fedora, RHEL:
-DOCBOOK_XSL=/usr/share/sgml/docbook/xsl-stylesheets
-# Ubuntu:
-# DOCBOOK_XSL=/usr/share/sgml/docbook/stylesheet/xsl/nwalsh
-# Ubuntu 10.4 installed it here:
-# DOCBOOK_XSL=/usr/share/xml/docbook/stylesheet/docbook-xsl-ns
-
-rm -rf build/$1
-mkdir -p build/$1
-mkdir -p build/$1/html-single
-mkdir -p build/$1/html
-mkdir -p build/$1/pdf
-cp -r src/images build/$1/html-single
-cp -r src/images build/$1/html
-cp -r src/css build/$1/html-single
-cp -r src/css build/$1/html
-
-# Create single-page .html
-xsltproc --xinclude --stringparam section.autolabel 1 --stringparam callout.graphics 0 --stringparam callout.unicode 0 --stringparam section.label.includes.component.label 1 ${DOCBOOK_XSL}/html/docbook.xsl src/$1.xml >build/$1/html-single/$1.html
-
-# Create chunked .html
-INFILE=$(readlink -f src/$1.xml)
-pushd build/$1/html
-xsltproc --xinclude --stringparam chunk.section.depth 1 --stringparam section.autolabel 1 --stringparam callout.graphics 0 --stringparam callout.unicode 0 --stringparam section.label.includes.component.label 1 --stringparam use.id.as.filename 1 --stringparam html.stylesheet css/style.css --stringparam section.autolabel.max.depth 3 --stringparam toc.section.depth 2 --stringparam chunker.output.encoding UTF-8 ${DOCBOOK_XSL}/html/chunk.xsl $INFILE
-popd
-
-# Create the .fo
-xsltproc --xinclude --stringparam section.autolabel 1 --stringparam callout.graphics 0 --stringparam callout.unicode 0 --stringparam section.label.includes.component.label 1 ${DOCBOOK_XSL}/fo/docbook.xsl src/$1.xml >build/$1/pdf/$1.fo
-
-# Use Apache FOP to create the PDF
-fop build/$1/pdf/$1.fo build/$1/pdf/$1.pdf
diff --git a/qpid/doc/book/build.xml b/qpid/doc/book/build.xml
deleted file mode 100644
index 5acd97f982..0000000000
--- a/qpid/doc/book/build.xml
+++ /dev/null
@@ -1,193 +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.
- -
- -->
-<!--
- Build Apache Qpid documentation.
-
- For documentation on using XSLT in ant, see http://ant.apache.org/manual/CoreTasks/style.html
-
- For documentation on using Apache FOP in ant, see http://xmlgraphics.apache.org/fop/0.94/anttask.html
-
- Note: Validation is currently off by default, too many dangling references. We will tighten this up as soon as we can.
- -->
-
-<project
- name="generate"
- basedir="."
- default="pdf">
-
- <property name="Qpid" value="http://qpid.apache.org"/>
-
-
-<!--
-###########################################################################
-#
-# Directories
-#
-# Change the directory paths in this section to the correct paths for
-# your machine.
-#
-###########################################################################
--->
-
- <property name="src.dir" location="src"/>
- <property name="build.dir" location="build"/>
- <property name="out.dir" location="out"/>
-
- <!-- Docbook schemas and stylesheets -->
- <property name="schema.dir" location="docbook"/>
- <property name="style.dir" location="docbook-xsl"/>
- <property name="fo.stylesheet" location="${style.dir}/fo/docbook.xsl" />
- <property name="html.stylesheet" location="${style.dir}/html/docbook.xsl" />
-
- <!-- ${lib.dir} has subdirectories for saxon and fop -->
- <property name="lib.dir" location="lib"/>
-
- <property name="xmllint" location="/usr/bin/xmllint"/>
-
-<!--
-###########################################################################
-#
-# Setting up tasks
-#
-# You shouldn't need to change anything in this section or following sections.
-#
-###########################################################################
--->
-
- <path id="saxon6.classpath">
- <pathelement location="${lib.dir}/saxon/resolver.jar"/>
- <pathelement location="${lib.dir}/saxon/xml-apis.jar"/>
- <pathelement location="${lib.dir}/saxon/xercesImpl.jar"/>
- <pathelement location="${lib.dir}/saxon/saxon.jar"/>
- </path>
-
-<taskdef name="fop"
- classname="org.apache.fop.tools.anttasks.Fop">
- <classpath>
- <fileset dir="${lib.dir}/fop-0.95/lib">
- <include name="*.jar"/>
- </fileset>
- <fileset dir="${lib.dir}/fop-0.95/build">
- <include name="fop.jar"/>
- <include name="fop-hyph.jar" />
- </fileset>
- </classpath>
-</taskdef>
-
-<!--
-###########################################################################
-#
-# Tasks
-#
-###########################################################################
--->
-
-<!--
- init
--->
-
-<target name="init">
- <mkdir dir="${build.dir}"/>
- <mkdir dir="${out.dir}"/>
-</target>
-
-<!--
- XInclude
--->
-
-<target name="xinclude" depends="init">
- <exec executable="${xmllint}">
- <arg value="-o"/>
- <arg value="${build.dir}/xinclude.xml"/>
- <arg value="--xinclude"/>
- <arg value="${src.dir}/Book.xml"/>
- </exec>
-</target>
-
-
-
-<!--
- FO
--->
-
- <target name="fo" depends="xinclude" description="Generates qpid-book.fo, which is needed to create a PDF">
-
- <xslt in="${build.dir}/xinclude.xml" out="${build.dir}/qpid-book.fo"
- style="${fo.stylesheet}" classpathref="saxon6.classpath">
- <param name="specdoc" expression="${spec.code}"/>
- <param name="uri" expression="${spec.uri}"/>
- </xslt>
- </target>
-
-
-<!--
- PDF
--->
-
-<target name="pdf" depends="fo" description="Generates qpid-book.pdf">
- <fop format="application/pdf"
- fofile="${build.dir}/qpid-book.fo"
- outfile="${out.dir}/qpid-book.pdf"/>
-</target
->
-<!--
- HTML
--->
-
- <target name="html" depends="xinclude" description="Generates qpid-book.html">
- <xslt in="${build.dir}/xinclude.xml" out="${out.dir}/qpid-book.html"
- style="${html.stylesheet}" classpathref="saxon6.classpath">
- </xslt>
- </target>
-
-<!--
- Validate
--->
-
-<target name="validate" depends="xinclude">
- <xmlvalidate file="${build.dir}/xinclude.xml" warn="true">
- <dtd publicId="-//OASIS//DTD DocBook V4.5//EN"
- location="docbook/docbook.dtd"/>
- </xmlvalidate>
-</target>
-
-<!--
- Clean
--->
-
-<target name="clean">
- <delete dir="${build.dir}"/>
-</target>
-
-<!--
- Check
--->
-
-<target name="check" depends="xinclude">
- <xmlvalidate file="${build.dir}/xinclude.xml" warn="false">
- <dtd publicId="-//OASIS//DTD DocBook V4.5//EN"
- location="docbook/docbook.dtd"/>
- </xmlvalidate>
-</target>
-
-
-</project>
-
diff --git a/qpid/doc/book/src/AMQP-Messaging-Broker-Java.xml b/qpid/doc/book/src/AMQP-Messaging-Broker-Java.xml
deleted file mode 100644
index 6cd7ce915e..0000000000
--- a/qpid/doc/book/src/AMQP-Messaging-Broker-Java.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-<?xml version="1.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.
-
--->
-
-<part id="Java-Broker">
- <title>AMQP Messaging Broker (Implemented in Java)</title>
- <partintro>
- <para>Qpid provides two AMQP messaging brokers:</para>
-
- <itemizedlist>
- <listitem><para>Implemented in C++ - high performance, low latency, and RDMA support.</para></listitem>
- <listitem><para>Implemented in Java - Fully JMS compliant, runs on any Java platform.</para></listitem>
- </itemizedlist>
-
- <para>Both AMQP messaging brokers support clients in multiple languages, as long as the messaging client and the messaging broker use the same version of AMQP. See <link linkend="AMQP-Compatibility"/> to see which messaging clients work with each broker.</para>
-
- <para>This section contains information specific to the broker that is implemented in Java.</para>
- </partintro>
-
-<chapter id="Java-General-User-Guides">
- <title>General User Guides</title>
-
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Java-Broker-Feature-Guide.xml"/>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Qpid-Java-FAQ.xml"/>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Java-Environment-Variables.xml"/>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Qpid-Troubleshooting-Guide.xml"/>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Broker-Configuration-Guide.xml"/>
-</chapter>
-
-<chapter id="Qpid-Java-Broker-HowTos">
-<title>How Tos</title>
-
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Add-New-Users.xml"/>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Configure-ACLs.xml"/>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Configure-Java-Qpid-to-use-a-SSL-connection.xml"/>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Configure-Log4j-CompositeRolling-Appender.xml"/>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Configure-the-Broker-via-config.xml.xml"/>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Configure-the-Virtual-Hosts-via-virtualhosts.xml.xml"/>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Debug-using-log4j.xml"/>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="How-to-Tune-M3-Java-Broker-Performance.xml"/>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Qpid-Java-Build-How-To.xml"/>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Use-Priority-Queues.xml"/>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="How-to-Use-SlowConsumerDisconnect.xml"/>
-</chapter>
-
-
-<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Qpid-JMX-Management-Console.xml"/>
-
-<chapter id="QpidJavaBroker-ManagementTools">
-<title>Management Tools</title>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="MessageStore-Tool.xml"/>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Qpid-Java-Broker-Management-CLI.xml"/>
-</chapter>
-</part>
diff --git a/qpid/doc/book/src/Active-Passive-Cluster.xml b/qpid/doc/book/src/Active-Passive-Cluster.xml
deleted file mode 100644
index 3eaadad51e..0000000000
--- a/qpid/doc/book/src/Active-Passive-Cluster.xml
+++ /dev/null
@@ -1,361 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-
-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.
-
--->
-
-<section id="chap-Messaging_User_Guide-Active_Passive_Cluster">
-
- <title>Active-passive Messaging Clusters (Preview)</title>
-
- <section>
- <title>Overview</title>
- <para>
- This release provides a preview of a new module for High Availability (HA). The new
- module is not yet complete or ready for production use, it being made available so
- that users can experiment with the new approach and provide feedback early in the
- development process. Feedback should go to <ulink
- url="mailto:user@qpid.apache.org">user@qpid.apache.org</ulink>.
- </para>
- <para>
- The old cluster module takes an <firstterm>active-active</firstterm> approach,
- i.e. all the brokers in a cluster are able to handle client requests
- simultaneously. The new HA module takes an <firstterm>active-passive</firstterm>,
- <firstterm>hot-standby</firstterm> approach.
- </para>
- <para>
- In an active-passive cluster, only one broker, known as the
- <firstterm>primary</firstterm>, is active and serving clients at a time. The other
- brokers are standing by as <firstterm>backups</firstterm>. Changes on the primary
- are immediately replicated to all the backups so they are always up-to-date or
- "hot". If the primary fails, one of the backups is promoted to be the new
- primary. Clients fail-over to the new primary automatically. If there are multiple
- backups, the backups also fail-over to become backups of the new primary.
- </para>
- <para>
- The new approach depends on an external <firstterm>cluster resource
- manager</firstterm> to detect failure of the primary and choose the new primary. The
- first supported resource manager will be <ulink
- url="https://fedorahosted.org/cluster/wiki/RGManager">rgmanager</ulink>, but it will
- be possible to add integration with other resource managers in the future. The
- preview version is not integrated with any resource manager, you can use the
- <command>qpid-ha</command> tool to simulate the actions of a resource manager or do
- your own integration.
- </para>
- <section>
- <title>Why the new approach?</title>
- The new active-passive approach has several advantages compared to the
- existing active-active cluster module.
- <itemizedlist>
- <listitem>
- It does not depend directly on openais or corosync. It does not use multicast
- which simplifies deployment.
- </listitem>
- <listitem>
- It is more portable: in environments that don't support corosync, it can be
- integrated with a resource manager available in that environment.
- </listitem>
- <listitem>
- Replication to a <firstterm>disaster recovery</firstterm> site can be handled as
- simply another node in the cluster, it does not require a separate replication
- mechanism.
- </listitem>
- <listitem>
- It can take advantage of features provided by the resource manager, for example
- virtual IP addresses.
- </listitem>
- <listitem>
- Improved performance and scalability due to better use of multiple CPU s
- </listitem>
- </itemizedlist>
- </section>
- <section>
-
- <title>Limitations</title>
-
- <para>
- There are a number of known limitations in the current preview implementation. These
- will be fixed in the production versions.
- </para>
-
- <itemizedlist>
- <listitem>
- Transactional changes to queue state are not replicated atomically. If the
- primary crashes during a transaction, it is possible that the backup could
- contain only part of the changes introduced by a transaction.
- </listitem>
- <listitem>
- During a fail-over one backup is promoted to primary and any other backups switch to
- the new primary. Messages sent to the new primary before all the backups have
- switched could be lost if the new primary itself fails before all the backups have
- switched.
- </listitem>
- <listitem>
- When used with a persistent store: if the entire cluster fails, there are no tools
- to help identify the most recent store.
- </listitem>
- <listitem>
- Acknowledgments are confirmed to clients before the message has been dequeued
- from replicas or indeed from the local store if that is asynchronous.
- </listitem>
- <listitem>
- A persistent broker must have its store erased before joining an existing cluster.
- In the production version a persistent broker will be able to load its store and
- avoid downloading messages that are in the store from the primary.
- </listitem>
- <listitem>
- Configuration changes (creating or deleting queues, exchanges and bindings) are
- replicated asynchronously. Management tools used to make changes will consider the
- change complete when it is complete on the primary, it may not yet be replicated
- to all the backups.
- </listitem>
- <listitem>
- Deletions made immediately after a failure (before all the backups are ready) may
- be lost on a backup. Queues, exchange or bindings that were deleted on the primary could
- re-appear if that backup is promoted to primary on a subsequent failure.
- </listitem>
- <listitem>
- Better control is needed over which queues/exchanges are replicated and which are not.
- </listitem>
- <listitem>
- There are some known issues affecting performance, both the throughput of
- replication and the time taken for backups to fail-over. Performance will improve
- in the production version.
- </listitem>
- <listitem>
- Federated links from the primary will be lost in fail over, they will not be
- re-connected on the new primary. Federation links to the primary can fail over.
- </listitem>
- <listitem>
- Only plain FIFO queues can be replicated. LVQ and ring queues are not yet supported.
- </listitem>
- </itemizedlist>
- </section>
- </section>
-
-
- <section>
- <title>Configuring the Brokers</title>
- <para>
- The broker must load the <filename>ha</filename> module, it is loaded by default
- when you start a broker. The following broker options are available for the HA module.
- </para>
- <table frame="all" id="ha-broker-options">
- <title>Options for High Availability Messaging Cluster</title>
- <tgroup align="left" cols="2" colsep="1" rowsep="1">
- <colspec colname="c1" colwidth="1*"/>
- <colspec colname="c2" colwidth="4*"/>
- <thead>
- <row>
- <entry align="center" nameend="c2" namest="c1">
- Options for High Availability Messaging Cluster
- </entry>
- </row>
- </thead>
- <tbody>
- <row>
- <entry>
- <command>--ha-cluster <replaceable>yes|no</replaceable></command>
- </entry>
- <entry>
- Set to "yes" to have the broker join a cluster.
- </entry>
- </row>
- <row>
- <entry>
- <command>--ha-brokers <replaceable>URL</replaceable></command>
- </entry>
- <entry>
- URL use by brokers to connect to each other. The URL lists the addresses of
- all the brokers in the cluster
- <footnote>
- <para>
- If the resource manager supports virtual IP addresses then the URL can
- contain just the single virtual IP.
- </para>
- </footnote>
- in the following form:
- <programlisting>
- url = ["amqp:"][ user ["/" password] "@" ] addr ("," addr)*
- addr = tcp_addr / rmda_addr / ssl_addr / ...
- tcp_addr = ["tcp:"] host [":" port]
- rdma_addr = "rdma:" host [":" port]
- ssl_addr = "ssl:" host [":" port]'
- </programlisting>
- </entry>
- </row>
- <row>
- <entry> <command>--ha-public-brokers <replaceable>URL</replaceable></command> </entry>
- <entry>
- URL used by clients to connect to the brokers in the same format as
- <command>--ha-brokers</command> above. Use this option if you want client
- traffic on a different network from broker replication traffic. If this
- option is not set, clients will use the same URL as brokers.
- </entry>
- </row>
- <row>
- <entry>
- <para><command>--ha-username <replaceable>USER</replaceable></command></para>
- <para><command>--ha-password <replaceable>PASS</replaceable></command></para>
- <para><command>--ha-mechanism <replaceable>MECH</replaceable></command></para>
- </entry>
- <entry>
- Brokers use <replaceable>USER</replaceable>,
- <replaceable>PASS</replaceable>, <replaceable>MECH</replaceable> to
- authenticate when connecting to each other.
- </entry>
- </row>
- </tbody>
- </tgroup>
- </table>
- <para>
- To configure a cluster you must set at least <command>ha-cluster</command> and <command>ha-brokers</command>
- </para>
- </section>
-
-
- <section>
- <title>Creating replicated queues and exchanges</title>
- <para>
- To create a replicated queue or exchange, pass the argument
- <command>qpid.replicate</command> when creating the queue or exchange. It should
- have one of the following three values:
- <itemizedlist>
- <listitem>
- <firstterm>all</firstterm>: Replicate the queue or exchange, messages and bindings.
- </listitem>
- <listitem>
- <firstterm>configuration</firstterm>: Replicate the existence of the queue or
- exchange and bindings but don't replicate messages.
- </listitem>
- <listitem>
- <firstterm>none</firstterm>: Don't replicate, this is the default.
- </listitem>
- </itemizedlist>
- </para>
- Bindings are automatically replicated if the queue and exchange being bound both have
- replication argument of <command>all</command> or <command>confguration</command>, they are
- not replicated otherwise.
-
- You can create replicated queues and exchanges with the <command>qpid-config</command>
- management tool like this:
- <programlisting>
- qpid-config add queue myqueue --replicate all
- </programlisting>
-
- To create replicated queues and exchangs via the client API, add a <command>node</command> entry to the address like this:
- <programlisting>
- "myqueue;{create:always,node:{x-declare:{arguments:{'qpid.replicate':all}}}}"
- </programlisting>
- </section>
-
-
-
- <section>
- <title>Client Fail-over</title>
- <para>
- Clients can only connect to the single primary broker. All other brokers in the
- cluster are backups, and they automatically reject any attempt by a client to
- connect.
- </para>
- <para>
- Clients are configured with the addreses of all of the brokers in the cluster.
- <footnote>
- <para>
- If the resource manager supports virtual IP addresses then the clients
- can be configured with a single virtual IP address.
- </para>
- </footnote>
- When the client tries to connect initially, it will try all of its addresses until it
- successfully connects to the primary. If the primary fails, clients will try to
- try to re-connect to all the known brokers until they find the new primary.
- </para>
- <para>
- Suppose your cluster has 3 nodes: <command>node1</command>, <command>node2</command> and <command>node3</command> all using the default AMQP port.
- </para>
- <para>
- With the C++ client, you specify all the cluster addresses in a single URL, for example:
- <programlisting>
- qpid::messaging::Connection c("node1:node2:node3");
- </programlisting>
- </para>
- <para>
- With the python client, you specify <command>reconnect=True</command> and a list of <replaceable>host:port</replaceable> addresses as <command>reconnect_urls</command> when calling <command>establish</command> or <command>open</command>
- <programlisting>
- connection = qpid.messaging.Connection.establish("node1", reconnect=True, "reconnect_urls=["node1", "node2", "node3"])
- </programlisting>
- </para>
- </section>
-
- <section>
- <title>Broker fail-over</title>
- <para>
- Broker fail-over is managed by a <firstterm>cluster resource
- manager</firstterm>. The initial preview version of HA is not integrated with a
- resource manager, the production version will be integrated with <ulink
- url="https://fedorahosted.org/cluster/wiki/RGManager">rgmanager</ulink> and it may
- be integrated with other resource managers in the future.
- </para>
- <para>
- The resource manager is responsible for ensuring that there is exactly one broker
- is acting as primary at all times. It selects the initial primary broker when the
- cluster is started, detects failure of the primary, and chooses the backup to
- promote as the new primary.
- </para>
- <para>
- You can simulate the actions of a resource manager, or indeed do your own
- integration with a resource manager using the <command>qpid-ha</command> tool. The
- command
- <programlisting>
- qpid-ha promote -b <replaceable>host</replaceable>:<replaceable>port</replaceable>
- </programlisting>
- will promote the broker listening on
- <replaceable>host</replaceable>:<replaceable>port</replaceable> to be the primary.
- You should only promote a broker to primary when there is no other primary in the
- cluster. The brokers will not detect multiple primaries, they rely on the resource
- manager to do that.
- </para>
- <para>
- A clustered broker always starts initially in <firstterm>discovery</firstterm>
- mode. It uses the addresses configured in the <command>ha-brokers</command>
- configuration option and tries to connect to each in turn until it finds to the
- primary. The resource manager is responsible for choosing on of the backups to
- promote as the initial primary.
- </para>
- <para>
- If the primary fails, all the backups are disconnected and return to discovery mode.
- The resource manager chooses one to promote as the new primary. The other backups
- will eventually discover the new primary and reconnect.
- </para>
- </section>
- <section>
- <title>Broker Administration</title>
- <para>
- You can connect to a backup broker with the administrative tool
- <command>qpid-ha</command>. You can also connect with the tools
- <command>qpid-config</command>, <command>qpid-route</command> and
- <command>qpid-stat</command> if you pass the flag <command>--ha-admin</command> on the
- command line. If you do connect to a backup you should not modify any of the
- replicated queues, as this will disrupt the replication and may result in
- message loss.
- </para>
- </section>
-</section>
-<!-- LocalWords: scalability rgmanager multicast RGManager mailto LVQ
--->
diff --git a/qpid/doc/book/src/Makefile.inc b/qpid/doc/book/src/Makefile.inc
new file mode 100644
index 0000000000..12cab54f8a
--- /dev/null
+++ b/qpid/doc/book/src/Makefile.inc
@@ -0,0 +1,63 @@
+#
+# 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.
+#
+
+BOOK=$(wildcard *Book.xml)
+XML=$(wildcard *.xml) $(wildcard ../common/*.xml)
+IMAGES=$(wildcard images/*.png)
+CSS=$(wilcard ../common/css/*.css)
+
+OUTPUTDIR=output
+OUTPUT= $(BOOK:%.xml=$(OUTPUTDIR)/%/)
+
+all: html pdf
+
+pdf: $(OUTPUT)/pdf $(BOOK:%.xml=$(OUTPUT)/pdf/%.pdf)
+
+html: $(OUTPUT)/html $(BOOK:%.xml=$(OUTPUT)/html/index.html)
+
+$(OUTPUT)/html/images: $(IMAGES)
+ -mkdir -p $(OUTPUT)/html/images
+ -cp images/*.png $(OUTPUT)/html/images/
+
+$(OUTPUT)/html/css: $(CSS)
+ -mkdir -p $(OUTPUT)/html/css
+ -cp ../common/css/*.css $(OUTPUT)/html/css
+
+$(OUTPUT)/html:
+ -mkdir -p $(OUTPUT)/html
+
+$(OUTPUT)/pdf:
+ -mkdir -p $(OUTPUT)/pdf
+
+$(OUTPUT)/html/index.html: $(BOOK) $(OUTPUT)/html/css $(OUTPUT)/html/images $(XML)
+ xsltproc -o $(OUTPUT)/html/ --xinclude --stringparam chunk.section.depth 1 --stringparam section.autolabel 1 --stringparam callout.graphics 0 --stringparam callout.unicode 0 --stringparam section.label.includes.component.label 1 --stringparam use.id.as.filename 1 --stringparam html.stylesheet css/style.css --stringparam section.autolabel.max.depth 3 --stringparam toc.section.depth 2 --stringparam chunker.output.encoding UTF-8 --stringparam css.decoration 0 ../../xsl/html-custom.xsl $<
+
+%.fo: %.xml
+ xsltproc --xinclude --stringparam section.autolabel 1 --stringparam callout.graphics 0 --stringparam callout.unicode 0 --stringparam section.label.includes.component.label 1 http://docbook.sourceforge.net/release/xsl/current/fo/docbook.xsl $< > $@
+
+
+%.pdf: %.fo
+ fop $< $@
+
+$(OUTPUT)/pdf/%.pdf: %.pdf
+ @mv $< $@
+
+clean:
+ -rm -rf $(OUTPUT) *.fo
+
diff --git a/qpid/doc/book/src/amqp-advanced-message-queueing-protocol.html b/qpid/doc/book/src/amqp-advanced-message-queueing-protocol.html
deleted file mode 100644
index 9d46429d03..0000000000
--- a/qpid/doc/book/src/amqp-advanced-message-queueing-protocol.html
+++ /dev/null
@@ -1,237 +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.
-
--->
-
-
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<HTML>
- <HEAD>
- <LINK type="text/css" rel="stylesheet" href="resources/space.css">
- <STYLE type="text/css">
- .footer {
- background-image: url('http://cwiki.apache.org/confluence/images/border/border_bottom.gif');
- background-repeat: repeat-x;
- background-position: left top;
- padding-top: 4px;
- color: #666;
- clear: both;
- }
- .left {
- padding-top: 5px;
- float : left;
- width : 15em;
- }
- .pagecontent {
- float: left;
- width: 70%;
- }
- </STYLE>
- <SCRIPT type="text/javascript" language="javascript">
- var hide = null;
- var show = null;
- var children = null;
-
- function init() {
- /* Search form initialization */
- var form = document.forms['search'];
- if (form != null) {
- form.elements['domains'].value = location.hostname;
- form.elements['sitesearch'].value = location.hostname;
- }
-
- /* Children initialization */
- hide = document.getElementById('hide');
- show = document.getElementById('show');
- children = document.all != null ?
- document.all['children'] :
- document.getElementById('children');
- if (children != null) {
- children.style.display = 'none';
- show.style.display = 'inline';
- hide.style.display = 'none';
- }
- }
-
- function showChildren() {
- children.style.display = 'block';
- show.style.display = 'none';
- hide.style.display = 'inline';
- }
-
- function hideChildren() {
- children.style.display = 'none';
- show.style.display = 'inline';
- hide.style.display = 'none';
- }
- </SCRIPT>
- <TITLE>Apache Qpid: Open Source AMQP Messaging - AMQP (Advanced Message Queueing Protocol)</TITLE>
- <META http-equiv="Content-Type" content="text/html;charset=UTF-8"></HEAD>
- <BODY onload="init()">
- <TABLE border="0" cellpadding="2" cellspacing="0" width="100%">
- <TR class="topBar">
- <TD align="left" valign="middle" class="topBarDiv" align="left" nowrap="">
- &nbsp;<A href="index.html" title="Apache Qpid">Apache Qpid</A>&nbsp;&gt;&nbsp;<A href="index.html" title="Index">Index</A>&nbsp;&gt;&nbsp;<A href="" title="AMQP (Advanced Message Queueing Protocol)">AMQP (Advanced Message Queueing Protocol)</A>
- </TD>
- <TD align="right" valign="middle" nowrap="">
- <FORM name="search" action="http://www.google.com/search" method="get">
- <INPUT type="hidden" name="ie" value="UTF-8">
- <INPUT type="hidden" name="oe" value="UTF-8">
- <INPUT type="hidden" name="domains" value="">
- <INPUT type="hidden" name="sitesearch" value="">
- <INPUT type="text" name="q" maxlength="255" value="">
- <INPUT type="submit" name="btnG" value="Google Search">
- </FORM>
- </TD>
- </TR>
- </TABLE>
-
- <DIV id="PageContent">
- <DIV class="pageheader" style="padding: 6px 0px 0px 0px;">
-<DIV>
-<TABLE border="0" width="90%">
-<TR>
-<TD align="left">
-<A href="http://qpid.apache.org/">
-<IMG src="http://qpid.apache.org/images/qpid-logo.png" height="69" width="225" border="0"></A>
-</TD>
-<TD>
-</TD>
-<TD align="right"> <A href="http://www.apache.org/">
- <IMG src="http://qpid.apache.org/images/asf-logo.png" height="69" width="225" border="0"></A></TD>
-</TR>
-</TABLE>
-</DIV>
-
- </DIV>
-
-<!--
-
-
- <div class="pagesubheading" style="margin: 0px 10px 0px 10px;">
- Added by <a href="/confluence/display/~jonathan.robie@redhat.com">Jonathan Robie</a>, last edited by <a href="/confluence/display/~jonathan.robie@redhat.com">Jonathan Robie</a> on Feb 18, 2009
- &nbsp;(<a class="noprint" href="/confluence/pages/diffpages.action?pageId=110693&originalId=110695">view change</a>)
-
- </div>
--->
-
- <DIV class="left">
-
-
-
-
- <DIV class="panel" style="background-color: E0E0FF;border-color: #202080;border-style: solid;border-width: 1px;"><DIV class="panelContent" style="background-color: E0E0FF;">
-<H3><A name="Navigation-ApacheQpid"></A>Apache Qpid</H3>
-<P> <A href="index.html" title="Index">Home</A><BR>
- <A href="download.html" title="Download">Download</A><BR>
- <A href="getting-started.html" title="Getting Started">Getting Started</A> <BR>
- <A href="documentation.html" title="Documentation">Documentation</A><BR>
- <A href="mailing-lists.html" title="Mailing Lists">Mailing Lists</A><BR>
- <A href="http://issues.apache.org/jira/browse/qpid" rel="nofollow">Issue Reporting</A><BR>
- <A href="faq.html" title="FAQ">FAQ/How to</A></P>
-
-<H3><A name="Navigation-Resources"></A>Resources</H3>
-<P> <A href="getting-involved.html" title="Getting Involved">Getting Involved</A><BR>
- <A href="qpid-integrations.html" title="Qpid Integrations">Qpid Integrated with..</A><BR>
- <A href="source-repository.html" title="Source Repository">Source Repository</A><BR>
- <A href="building.html" title="Building">Building Qpid</A><BR>
- <A href="developer-pages.html" title="Developer Pages">Developer Pages</A><BR>
- <A href="qpid-management-framework.html" title="Qpid Management Framework">QMF</A></P>
-
-<H3><A name="Navigation-AboutQpid"></A>About Qpid</H3>
-<P> <A href="people.html" title="People">People</A><BR>
- <A href="license.html" title="License">License</A><BR>
- <A href="project-status.html" title="Project Status">Project Status</A><BR>
- <A href="acknowledgments.html" title="Acknowledgments">Acknowledgments</A></P>
-
-<H3><A name="Navigation-AboutAMQP"></A>About AMQP</H3>
-<P> <A href="" title="AMQP (Advanced Message Queueing Protocol)">What is AMQP ?</A><BR>
- <A href="" title="AMQP (Advanced Message Queueing Protocol)">AMQP Specification Download</A></P>
-
-<P><IMG src="navigation.data/AMQP_logo_71px-small.jpg" align="absmiddle" border="0"></P>
-</DIV></DIV>
- </DIV>
-
- <DIV class="pagecontent">
- <DIV class="wiki-content">
- <H2><A name="AMQP%28AdvancedMessageQueueingProtocol%29-WhatisAMQP%3F"></A>What is AMQP?</H2>
-
-<P>AMQP <A href="http://www.amqp.org/" rel="nofollow">Advanced Message Queuing Protocol</A> is an open standard designed to support reliable, high-performance messaging over the Internet. AMQP can be used for any distributed or business application, and supports common messaging paradigms like point-to-point, fanout, publish-subscribe, and request-response.</P>
-
-<P>Apache Qpid implements AMQP, including transaction management, queuing, clustering, federation, security, management and multi-platform support.</P>
-
-
-<P>Apache Qpid implements the latest AMQP specification, providing transaction management, queuing, distribution, security, management, clustering, federation and heterogeneous multi-platform support and a lot more. </P>
-
-<P>Apache Qpid is highly optimized, and <A href="amqp-compatibility.html" title="AMQP compatibility">aims to be 100% AMQP Compliant</A>.</P>
-
-<H2><A name="AMQP%28AdvancedMessageQueueingProtocol%29-DownloadtheAMQPSpecifications"></A>Download the AMQP Specifications</H2>
-
-<H3><A name="AMQP%28AdvancedMessageQueueingProtocol%29-AMQPversion010"></A>AMQP version 0-10</H3>
-
-
-<UL>
- <LI><A href="https://jira.amqp.org/confluence/download/attachments/720900/amqp.0-10.pdf?version=1" rel="nofollow">AMQP 0-10 Specification (PDF) </A></LI>
- <LI><A href="https://jira.amqp.org/confluence/download/attachments/720900/amqp.0-10.xml?version=1" rel="nofollow">AMQP 0-10 Protocol Definition XML </A></LI>
- <LI><A href="https://jira.amqp.org/confluence/download/attachments/720900/amqp.0-10.dfd?version=1" rel="nofollow">AMQP 0-10 Protocol Definition DTD </A></LI>
-</UL>
-
-
-<H3><A name="AMQP%28AdvancedMessageQueueingProtocol%29-AMQPversion091"></A>AMQP version 0-9-1</H3>
-
-<UL>
- <LI><A href="https://jira.amqp.org/confluence/download/attachments/720900/amqp0-9-1.pdf?version=1" rel="nofollow">AMQP 0-9-1 Specification (PDF) </A></LI>
- <LI><A href="https://jira.amqp.org/confluence/download/attachments/720900/amqp0-9-1.xml?version=1" rel="nofollow">AMQP 0-9-1 Protocol Documentation (PDF) </A></LI>
- <LI><A href="https://jira.amqp.org/confluence/download/attachments/720900/amqp0-9-1.dtd?version=1" rel="nofollow">AMQP 0-9-1 Protocol Definitions (XML) </A></LI>
-</UL>
-
-
-<H3><A name="AMQP%28AdvancedMessageQueueingProtocol%29-AMQPversion09"></A>AMQP version 0-9</H3>
-
-<UL>
- <LI><A href="https://jira.amqp.org/confluence/download/attachments/720900/amqp0-9.pdf?version=1" rel="nofollow">AMQP 0-9 Specification (PDF) </A></LI>
- <LI><A href="https://jira.amqp.org/confluence/download/attachments/720900/amqp0-9.xml?version=1" rel="nofollow">AMQP 0-9 Protocol Documentation (PDF) </A></LI>
- <LI><A href="https://jira.amqp.org/confluence/download/attachments/720900/amqp0-9.dtd?version=1" rel="nofollow">AMQP 0-9 Protocol Definitions (XML) </A></LI>
-</UL>
-
-
-<H3><A name="AMQP%28AdvancedMessageQueueingProtocol%29-AMQPversion08"></A>AMQP version 0-8</H3>
-
-<UL>
- <LI><A href="https://jira.amqp.org/confluence/download/attachments/720900/amqp0-8.pdf?version=1" rel="nofollow">AMQP 0-8 Specification (PDF) </A></LI>
- <LI><A href="https://jira.amqp.org/confluence/download/attachments/720900/amqp0-8.dtd?version=1" rel="nofollow">AMQP 0-8 Protocol Documentation (PDF) </A></LI>
- <LI><A href="https://jira.amqp.org/confluence/download/attachments/720900/amqp0-8.xml?version=1" rel="nofollow">AMQP 0-8 Protocol Definitions (XML) </A></LI>
-</UL>
-
- </DIV>
-
-<!--
- -->
-
- </DIV>
-
- <DIV class="footer">
- Apache Qpid, Enterprise AMQP Messaging
- &nbsp;
- &copy; 2004-2008 The Apache Software Foundation.
- &nbsp;
- (<A href="http://cwiki.apache.org/confluence/pages/editpage.action?pageId=110693">edit this page</A>)
- </DIV>
- </BODY>
-</HTML>
diff --git a/qpid/doc/book/src/common/css/style.css b/qpid/doc/book/src/common/css/style.css
new file mode 100644
index 0000000000..c681596592
--- /dev/null
+++ b/qpid/doc/book/src/common/css/style.css
@@ -0,0 +1,279 @@
+/*
+ *
+ * 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.
+ *
+ */
+ul {
+ list-style-type:square;
+}
+
+th {
+ font-weight: bold;
+}
+
+.navfooter td {
+ font-size:10pt;
+}
+
+.navheader td {
+ font-size:10pt;
+}
+
+body {
+ margin:0;
+ background:#FFFFFF;
+ font-family:"Verdana", sans-serif;
+ font-size:10pt;
+}
+
+.container {
+ width:950px;
+ margin:0 auto;
+}
+
+body a {
+ color:#000000;
+}
+
+
+div.book {
+ margin-left:10pt;
+ margin-right:10pt;
+}
+
+div.preface {
+ margin-left:10pt;
+ margin-right:10pt;
+}
+
+div.chapter {
+ margin-left:10pt;
+ margin-right:10pt;
+}
+
+div.section {
+ margin-left:10pt;
+ margin-right:10pt;
+}
+
+div.titlepage {
+ margin-left:-10pt;
+ margin-right:-10pt;
+}
+
+.calloutlist td {
+ font-size:10pt;
+}
+
+.table-contents table {
+ border-spacing: 0px;
+}
+
+.table-contents td {
+ font-size:10pt;
+ padding-left:6px;
+ padding-right:6px;
+}
+
+div.breadcrumbs {
+ font-size:9pt;
+ margin-right:10pt;
+ padding-bottom:16px;
+}
+
+.chapter h2.title {
+ font-size:20pt;
+ color:#0c3b82;
+}
+
+.chapter .section h2.title {
+ font-size:18pt;
+ color:#0c3b82;
+}
+
+.section h2.title {
+ font-size:16pt;
+ color:#0c3b82;
+}
+
+.section h3.title {
+ font-size:14pt;
+ color:#0c3b82;
+}
+
+.section h4.title {
+ font-size:12pt;
+ color:#0c3b82;
+}
+
+.section h5.title {
+ font-size:12pt;
+ color:#0c3b82;
+}
+
+.section h6.title {
+ font-size:12pt;
+ color:#0c3b82;
+}
+
+.toc a {
+ font-size:9pt;
+}
+
+.header {
+ height:100px;
+ width:950px;
+ background:url(http://qpid.apache.org/images/header.png)
+}
+
+.logo {
+ text-align:center;
+ font-weight:600;
+ padding:0 0 0 0;
+ font-size:14px;
+ font-family:"Verdana", cursive;
+}
+
+.logo a {
+ color:#000000;
+ text-decoration:none;
+}
+
+.main_text_area {
+ margin-left:200px;
+}
+
+.main_text_area_top {
+ height:14px;
+ font-size:1px;
+}
+
+.main_text_area_bottom {
+ display:none;
+/* height:14px;
+ margin-bottom:4px;*/
+}
+
+.main_text_area_body {
+ padding:5px 24px;
+}
+
+.main_text_area_body p {
+ text-align:justify;
+}
+
+.main_text_area br {
+ line-height:10px;
+}
+
+.main_text_area h1 {
+ font-size:28px;
+ font-weight:600;
+ margin:0 0 24px 0;
+ color:#0c3b82;
+ font-family:"Verdana", Times, serif;
+}
+
+.main_text_area h2 {
+ font-size:24px;
+ font-weight:600;
+ margin:24px 0 8px 0;
+ color:#0c3b82;
+ font-family:"Verdana",Times, serif;
+}
+
+.main_text_area ol, .main_text_area ul {
+ padding:0;
+ margin:10px 0;
+ margin-left:20px;
+}
+
+.main_text_area li {
+/* margin-left:40px; */
+}
+
+.main_text_area, .menu_box {
+ font-size:13px;
+ line-height:17px;
+ color:#000000;
+}
+
+.main_text_area {
+ font-size:14px;
+}
+
+.main_text_area a {
+ color:#000000;
+}
+
+.main_text_area a:hover {
+ color:#000000;
+}
+
+.menu_box {
+ width:196px;
+ float:left;
+ margin-left:4px;
+}
+
+.menu_box_top {
+ background:url(http://qpid.apache.org/images/menu_top.png) no-repeat;
+ height:14px;
+ font-size:1px;
+}
+
+.menu_box_body {
+ background:url(http://qpid.apache.org/images/menu_body.png) repeat-y;
+ padding:5px 24px 5px 24px;
+}
+
+.menu_box_bottom {
+ background:url(http://qpid.apache.org/images/menu_bottom.png) no-repeat;
+ height:14px;
+ font-size:1px;
+ margin-bottom:1px;
+}
+
+.menu_box h3 {
+ font-size:20px;
+ font-weight:500;
+ margin:0 0 8px 0;
+ color:#0c3b82;
+ font-family:"Verdana",Times, serif;
+}
+
+.menu_box ul {
+ margin:12px;
+ padding:0px;
+}
+
+.menu_box li {
+ list-style:square;
+}
+
+.menu_box a {
+ color:#000000;
+ text-decoration:none;
+}
+
+.menu_box a:hover {
+ color:#000000;
+ text-decoration:underline;
+}
+
+
diff --git a/qpid/doc/book/src/AMQP-Compatibility.xml b/qpid/doc/book/src/cpp-broker/AMQP-Compatibility.xml
index e5aa98cf96..e5aa98cf96 100644
--- a/qpid/doc/book/src/AMQP-Compatibility.xml
+++ b/qpid/doc/book/src/cpp-broker/AMQP-Compatibility.xml
diff --git a/qpid/doc/book/src/AMQP-Messaging-Broker-CPP-Book.xml b/qpid/doc/book/src/cpp-broker/AMQP-Messaging-Broker-CPP-Book.xml
index 228c6a5e15..228c6a5e15 100644
--- a/qpid/doc/book/src/AMQP-Messaging-Broker-CPP-Book.xml
+++ b/qpid/doc/book/src/cpp-broker/AMQP-Messaging-Broker-CPP-Book.xml
diff --git a/qpid/doc/book/src/Active-Active-Cluster.xml b/qpid/doc/book/src/cpp-broker/Active-Active-Cluster.xml
index 28db3876e2..28db3876e2 100644
--- a/qpid/doc/book/src/Active-Active-Cluster.xml
+++ b/qpid/doc/book/src/cpp-broker/Active-Active-Cluster.xml
diff --git a/qpid/doc/book/src/cpp-broker/Active-Passive-Cluster.xml b/qpid/doc/book/src/cpp-broker/Active-Passive-Cluster.xml
new file mode 100644
index 0000000000..d00464c92c
--- /dev/null
+++ b/qpid/doc/book/src/cpp-broker/Active-Passive-Cluster.xml
@@ -0,0 +1,672 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+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
+h"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.
+
+-->
+
+<section id="chap-Messaging_User_Guide-Active_Passive_Cluster">
+
+ <title>Active-passive Messaging Clusters</title>
+
+ <section>
+ <title>Overview</title>
+ <para>
+ This release provides a preview of a new module for High Availability (HA).
+ This module is intended to eventually replace the existing cluster module.
+ </para>
+ <para>
+ The old cluster module takes an <firstterm>active-active</firstterm> approach, i.e. all the
+ brokers in a cluster are able to handle client requests simultaneously. The new HA module
+ takes an <firstterm>active-passive</firstterm>, <firstterm>hot-standby</firstterm> approach.
+ </para>
+ <para>
+ In an active-passive cluster only one broker, known as the <firstterm>primary</firstterm>, is
+ active and serving clients at a time. The other brokers are standing by as
+ <firstterm>backups</firstterm>. Changes on the primary are immediately replicated to all the
+ backups so they are always up-to-date or "hot". If the primary fails, one of the backups is
+ promoted to take over as the new primary. Clients fail-over to the new primary
+ automatically. If there are multiple backups, the backups also fail-over to become backups of
+ the new primary. Backup brokers reject connection attempts, to enforce the requirement that
+ only the primary be active. Clients fail-over till the successfully connect to the primary broker.
+ </para>
+ <para>
+ This approach requires on an external <firstterm>cluster resource
+ manager</firstterm> to detect failures and choose the new primary. <ulink
+ url="https://fedorahosted.org/cluster/wiki/RGManager">Rgmanager</ulink> is
+ supported initially, but others may be supported in the future.
+ </para>
+ <section>
+ <title>Why the new approach?</title>
+ <para>
+ The new active-passive approach has several advantages compared to the
+ existing active-active cluster module.
+ <itemizedlist>
+ <listitem>
+ It does not depend directly on openais or corosync. It does not use multicast
+ which simplifies deployment.
+ </listitem>
+ <listitem>
+ It is more portable: in environments that don't support corosync, it can be
+ integrated with a resource manager available in that environment.
+ </listitem>
+ <listitem>
+ Replication to a <firstterm>disaster recovery</firstterm> site can be handled as
+ simply another node in the cluster, it does not require a separate replication
+ mechanism.
+ </listitem>
+ <listitem>
+ It can take advantage of features provided by the resource manager, for example
+ virtual IP addresses.
+ </listitem>
+ <listitem>
+ Improved performance and scalability due to better use of multiple CPUs
+ </listitem>
+ </itemizedlist>
+ </para>
+ </section>
+ <section>
+ <title>Limitations</title>
+ <para>
+ There are a number of known limitations in the current preview implementation. These
+ will be fixed in the production versions.
+ </para>
+ <itemizedlist>
+ <listitem>
+ Transactional changes to queue state are not replicated atomically. If the primary crashes
+ during a transaction, it is possible that the backup could contain only part of the
+ changes introduced by a transaction.
+ </listitem>
+ <listitem>
+ Not yet integrated with the persistent store. A persistent broker must have its
+ store erased before joining an existing cluster. If the entire cluster fails,
+ there are no tools to help identify the most recent store. In the future a
+ persistent broker will be able to use its stored messages to avoid downloading
+ messages from the primary when joining a cluster.
+ </listitem>
+ <listitem>
+ Configuration changes (creating or deleting queues, exchanges and bindings) are
+ replicated asynchronously. Management tools used to make changes will consider the
+ change complete when it is complete on the primary, it may not yet be replicated
+ to all the backups.
+ </listitem>
+ <listitem>
+ Deletions made immediately after a failure (before all the backups are ready) may
+ be lost on a backup. Queues, exchange or bindings that were deleted on the primary could
+ re-appear if that backup is promoted to primary on a subsequent failure.
+ </listitem>
+ <listitem>
+ Federated links from the primary will be lost in fail over, they will not be
+ re-connected on the new primary. Federation links to the primary can fail over.
+ </listitem>
+ </itemizedlist>
+ </section>
+ </section>
+
+ <section>
+ <title>Virtual IP Addresses</title>
+ <para>
+ Some resource managers (including <command>rgmanager</command>) support
+ <firstterm>virtual IP addresses</firstterm>. A virtual IP address is an IP
+ address that can be relocated to any of the nodes in a cluster. The
+ resource manager associates this address with the primary node in the
+ cluster, and relocates it to the new primary when there is a failure. This
+ simplifies configuration as you can publish a single IP address rather
+ than a list.
+ </para>
+ <para>
+ A virtual IP address can be used by clients and backup brokers to connect
+ to the primary. The following sections will explain how to configure
+ virtual IP addresses for clients or brokers.
+ </para>
+ </section>
+
+ <section>
+ <title>Configuring the Brokers</title>
+ <para>
+ The broker must load the <filename>ha</filename> module, it is loaded by
+ default. The following broker options are available for the HA module.
+ </para>
+ <table frame="all" id="ha-broker-options">
+ <title>Options for High Availability Messaging Cluster</title>
+ <tgroup align="left" cols="2" colsep="1" rowsep="1">
+ <colspec colname="c1" colwidth="1*"/>
+ <colspec colname="c2" colwidth="3*"/>
+ <thead>
+ <row>
+ <entry align="center" nameend="c2" namest="c1">
+ Options for High Availability Messaging Cluster
+ </entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <literal>--ha-cluster <replaceable>yes|no</replaceable></literal>
+ </entry>
+ <entry>
+ Set to "yes" to have the broker join a cluster.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <literal>--ha-brokers-url <replaceable>URL</replaceable></literal>
+ </entry>
+ <entry>
+ <para>
+ The URL
+ <footnote>
+ <para>
+ The full format of the URL is given by this grammar:
+ <programlisting>
+ url = ["amqp:"][ user ["/" password] "@" ] addr ("," addr)*
+ addr = tcp_addr / rmda_addr / ssl_addr / ...
+ tcp_addr = ["tcp:"] host [":" port]
+ rdma_addr = "rdma:" host [":" port]
+ ssl_addr = "ssl:" host [":" port]'
+ </programlisting>
+ </para>
+ </footnote>
+ used by cluster brokers to connect to each other. The URL can
+ contain a list of all the brokers' addresses or it can contain a single
+ virtual IP address. If a list is used it is comma separated, for example
+ <literal>amqp:node1.exaple.com,node2.exaple.com,node3.exaple.com</literal>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry><literal>--ha-public-url <replaceable>URL</replaceable></literal> </entry>
+ <entry>
+ <para>
+ The URL that is advertized to clients. This defaults to the
+ <literal>--ha-brokers-url</literal> URL above, and has the same format. A
+ virtual IP address is recommended for the public URL as it simplifies
+ deployment and hides changes to the cluster membership from clients.
+ </para>
+ <para>
+ This option allows you to put client traffic on a different network from
+ broker traffic, which is recommended.
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry><literal>--ha-replicate</literal></entry>
+ <foo/>
+ <entry>
+ <para>
+ Specifies whether queues and exchanges are replicated by default.
+ For details see <xref linkend="ha-creating-replicated"/>
+ </para>
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <para><literal>--ha-username <replaceable>USER</replaceable></literal></para>
+ <para><literal>--ha-password <replaceable>PASS</replaceable></literal></para>
+ <para><literal>--ha-mechanism <replaceable>MECH</replaceable></literal></para>
+ </entry>
+ <entry>
+ Authentication settings used by brokers to connect to each other.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ <para>
+ To configure a HA cluster you must set at least <literal>ha-cluster</literal> and
+ <literal>ha-brokers-url</literal>.
+ </para>
+ </section>
+
+ <section>
+ <title>The Cluster Resource Manager</title>
+ <para>
+ Broker fail-over is managed by a <firstterm>cluster resource
+ manager</firstterm>. An integration with <ulink
+ url="https://fedorahosted.org/cluster/wiki/RGManager">rgmanager</ulink> is
+ provided, but it is possible to integrate with other resource managers.
+ </para>
+ <para>
+ The resource manager is responsible for starting the <command>qpidd</command> broker
+ on each node in the cluster. The resource manager then <firstterm>promotes</firstterm>
+ one of the brokers to be the primary. The other brokers connect to the primary as
+ backups, using the URL provided in the <literal>ha-brokers-url</literal> configuration
+ option.
+ </para>
+ <para>
+ Once connected, the backup brokers synchronize their state with the
+ primary. When a backup is synchronized, or "hot", it is ready to take
+ over if the primary fails. Backup brokers continually receive updates
+ from the primary in order to stay synchronized.
+ </para>
+ <para>
+ If the primary fails, backup brokers go into fail-over mode. The resource
+ manager must detect the failure and promote one of the backups to be the
+ new primary. The other backups connect to the new primary and synchronize
+ their state with it.
+ </para>
+ <para>
+ The resource manager is also responsible for protecting the cluster from
+ <firstterm>split-brain</firstterm> conditions resulting from a network partition. A
+ network partition divide a cluster into two sub-groups which cannot see each other.
+ Usually a <firstterm>quorum</firstterm> voting algorithm is used that disables nodes
+ in the inquorate sub-group.
+ </para>
+ </section>
+
+ <section>
+ <title>Configuring <command>rgmanager</command> as resource manager</title>
+ <para>
+ This section assumes that you are already familiar with setting up and configuring
+ clustered services using <command>cman</command> and
+ <command>rgmanager</command>. It will show you how to configure an active-passive,
+ hot-standby <command>qpidd</command> HA cluster with <command>rgmanager</command>.
+ </para>
+ <para>
+ You must provide a <literal>cluster.conf</literal> file to configure
+ <command>cman</command> and <command>rgmanager</command>. Here is
+ an example <literal>cluster.conf</literal> file for a cluster of 3 nodes named
+ node1, node2 and node3. We will go through the configuration step-by-step.
+ </para>
+ <programlisting>
+ <![CDATA[
+<?xml version="1.0"?>
+<!--
+This is an example of a cluster.conf file to run qpidd HA under rgmanager.
+This example assumes a 3 node cluster, with nodes named node1, node2 and node3.
+
+NOTE: fencing is not shown, you must configure fencing appropriately for your cluster.
+-->
+
+<cluster name="qpid-test" config_version="18">
+ <!-- The cluster has 3 nodes. Each has a unique nodid and one vote
+ for quorum. -->
+ <clusternodes>
+ <clusternode name="node1.example.com" nodeid="1"/>
+ <clusternode name="node2.example.com" nodeid="2"/>
+ <clusternode name="node3.example.com" nodeid="3"/>
+ </clusternodes>
+ <!-- Resouce Manager configuration. -->
+ <rm>
+ <!--
+ There is a failoverdomain for each node containing just that node.
+ This lets us stipulate that the qpidd service should always run on each node.
+ -->
+ <failoverdomains>
+ <failoverdomain name="node1-domain" restricted="1">
+ <failoverdomainnode name="node1.example.com"/>
+ </failoverdomain>
+ <failoverdomain name="node2-domain" restricted="1">
+ <failoverdomainnode name="node2.example.com"/>
+ </failoverdomain>
+ <failoverdomain name="node3-domain" restricted="1">
+ <failoverdomainnode name="node3.example.com"/>
+ </failoverdomain>
+ </failoverdomains>
+
+ <resources>
+ <!-- This script starts a qpidd broker acting as a backup. -->
+ <script file="/etc/init.d/qpidd" name="qpidd"/>
+
+ <!-- This script promotes the qpidd broker on this node to primary. -->
+ <script file="/etc/init.d/qpidd-primary" name="qpidd-primary"/>
+
+ <!-- This is a virtual IP address for broker replication traffic. -->
+ <ip address="20.0.10.200" monitor_link="1"/>
+
+ <!-- This is a virtual IP address on a seprate network for client traffic. -->
+ <ip address="20.0.20.200" monitor_link="1"/>
+ </resources>
+
+ <!-- There is a qpidd service on each node, it should be restarted if it fails. -->
+ <service name="node1-qpidd-service" domain="node1-domain" recovery="restart">
+ <script ref="qpidd"/>
+ </service>
+ <service name="node2-qpidd-service" domain="node2-domain" recovery="restart">
+ <script ref="qpidd"/>
+ </service>
+ <service name="node3-qpidd-service" domain="node3-domain" recovery="restart">
+ <script ref="qpidd"/>
+ </service>
+
+ <!-- There should always be a single qpidd-primary service, it can run on any node. -->
+ <service name="qpidd-primary-service" autostart="1" exclusive="0" recovery="relocate">
+ <script ref="qpidd-primary"/>
+ <!-- The primary has the IP addresses for brokers and clients to connect. -->
+ <ip ref="20.0.10.200"/>
+ <ip ref="20.0.20.200"/>
+ </service>
+ </rm>
+</cluster>
+ ]]>
+ </programlisting>
+
+ <para>
+ There is a <literal>failoverdomain</literal> for each node containing just that
+ one node. This lets us stipulate that the qpidd service should always run on all
+ nodes.
+ </para>
+ <para>
+ The <literal>resources</literal> section defines the <command>qpidd</command>
+ script used to start the <command>qpidd</command> service. It also defines the
+ <command>qpid-primary</command> script which does not
+ actually start a new service, rather it promotes the existing
+ <command>qpidd</command> broker to primary status.
+ </para>
+ <para>
+ The <literal>resources</literal> section also defines a pair of virtual IP
+ addresses on different sub-nets. One will be used for broker-to-broker
+ communication, the other for client-to-broker.
+ </para>
+ <para>
+ To take advantage of the virtual IP addresses, <filename>qpidd.conf</filename>
+ should contain these lines:
+ </para>
+ <programlisting>
+ ha-cluster=yes
+ ha-brokers-url=20.0.20.200
+ ha-public-url=20.0.10.200
+ </programlisting>
+ <para>
+ This configuration specifies that backup brokers will use 20.0.20.200
+ to connect to the primary and will advertise 20.0.10.200 to clients.
+ Clients should connect to 20.0.10.200.
+ </para>
+ <para>
+ The <literal>service</literal> section defines 3 <literal>qpidd</literal>
+ services, one for each node. Each service is in a restricted fail-over
+ domain containing just that node, and has the <literal>restart</literal>
+ recovery policy. The effect of this is that rgmanager will run
+ <command>qpidd</command> on each node, restarting if it fails.
+ </para>
+ <para>
+ There is a single <literal>qpidd-primary-service</literal> using the
+ <command>qpidd-primary</command> script which is not restricted to a
+ domain and has the <literal>relocate</literal> recovery policy. This means
+ rgmanager will start <command>qpidd-primary</command> on one of the nodes
+ when the cluster starts and will relocate it to another node if the
+ original node fails. Running the <literal>qpidd-primary</literal> script
+ does not start a new broker process, it promotes the existing broker to
+ become the primary.
+ </para>
+ </section>
+
+ <section>
+ <title>Broker Administration Tools</title>
+ <para>
+ Normally, clients are not allowed to connect to a backup broker. However management tools are
+ allowed to connect to a backup brokers. If you use these tools you <emphasis>must
+ not</emphasis> add or remove messages from replicated queues, or delete replicated queues or
+ exchanges as this will corrupt the replication process and may cause message loss.
+ </para>
+ <para>
+ <command>qpid-ha</command> allows you to view and change HA configuration settings.
+ </para>
+ <para>
+ The tools <command>qpid-config</command>, <command>qpid-route</command> and
+ <command>qpid-stat</command> will connect to a backup if you pass the flag <command>--ha-admin</command> on the
+ command line.
+ </para>
+ </section>
+
+ <section id="ha-creating-replicated">
+ <title>Creating replicated queues and exchanges</title>
+ <para>
+ By default, queues and exchanges are not replicated automatically. You can change
+ the default behavior by setting the <literal>ha-replicate</literal> configuration
+ option. It has one of the following values:
+ <itemizedlist>
+ <listitem>
+ <firstterm>all</firstterm>: Replicate everything automatically: queues,
+ exchanges, bindings and messages.
+ </listitem>
+ <listitem>
+ <firstterm>configuration</firstterm>: Replicate the existence of queues,
+ exchange and bindings but don't replicate messages.
+ </listitem>
+ <listitem>
+ <firstterm>none</firstterm>: Don't replicate anything, this is the default.
+ </listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ You can over-ride the default for a particular queue or exchange by passing the
+ argument <literal>qpid.replicate</literal> when creating the queue or exchange. It
+ takes the same values as <literal>ha-replicate</literal>
+ </para>
+ <para>
+ Bindings are automatically replicated if the queue and exchange being bound both
+ have replication <literal>all</literal> or <literal>configuration</literal>, they
+ are not replicated otherwise.
+ </para>
+ <para>
+ You can create replicated queues and exchanges with the
+ <command>qpid-config</command> management tool like this:
+ </para>
+ <programlisting>
+ qpid-config add queue myqueue --replicate all
+ </programlisting>
+ <para>
+ To create replicated queues and exchanges via the client API, add a
+ <literal>node</literal> entry to the address like this:
+ </para>
+ <programlisting>
+ "myqueue;{create:always,node:{x-declare:{arguments:{'qpid.replicate':all}}}}"
+ </programlisting>
+ </section>
+
+ <section>
+ <title>Client Connection and Fail-over</title>
+ <para>
+ Clients can only connect to the primary broker. Backup brokers
+ automatically reject any connection attempt by a client.
+ </para>
+ <para>
+ Clients are configured with the URL for the cluster (details below for
+ each type of client). There are two possibilities
+ <itemizedlist>
+ <listitem>
+ The URL contains multiple addresses, one for each broker in the cluster.
+ </listitem>
+ <listitem>
+ The URL contains a single <firstterm>virtual IP address</firstterm>
+ that is assigned to the primary broker by the resource manager.
+ <footnote><para>Only if the resource manager supports virtual IP addresses</para></footnote>
+ </listitem>
+ </itemizedlist>
+ In the first case, clients will repeatedly re-try each address in the URL
+ until they successfully connect to the primary. In the second case the
+ resource manager will assign the virtual IP address to the primary broker,
+ so clients only need to re-try on a single address.
+ </para>
+ <para>
+ When the primary broker fails, clients re-try all known cluster addresses
+ until they connect to the new primary. The client re-sends any messages
+ that were previously sent but not acknowledged by the broker at the time
+ of the failure. Similarly messages that have been sent by the broker, but
+ not acknowledged by the client, are re-queued.
+ </para>
+ <para>
+ TCP can be slow to detect connection failures. A client can configure a
+ connection to use a <firstterm>heartbeat</firstterm> to detect connection
+ failure, and can specify a time interval for the heartbeat. If heartbeats
+ are in use, failures will be detected no later than twice the heartbeat
+ interval. The following sections explain how to enable heartbeat in each
+ client.
+ </para>
+ <para>
+ See &#34;Cluster Failover&#34; in <citetitle>Programming in Apache
+ Qpid</citetitle> for details on how to keep the client aware of cluster
+ membership.
+ </para>
+ <para>
+ Suppose your cluster has 3 nodes: <literal>node1</literal>, <literal>node2</literal>
+ and <literal>node3</literal> all using the default AMQP port. To connect a client you
+ need to specify the address(es) and set the <literal>reconnect</literal> property to
+ <literal>true</literal>. Here's how to connect each type of client:
+ </para>
+ <section>
+ <title>C++ clients</title>
+ <para>
+ With the C++ client, you specify multiple cluster addresses in a single URL
+ <footnote>
+ <para>
+ The full grammar for the URL is:
+ </para>
+ <programlisting>
+ url = ["amqp:"][ user ["/" password] "@" ] addr ("," addr)*
+ addr = tcp_addr / rmda_addr / ssl_addr / ...
+ tcp_addr = ["tcp:"] host [":" port]
+ rdma_addr = "rdma:" host [":" port]
+ ssl_addr = "ssl:" host [":" port]'
+ </programlisting>
+ </footnote>
+ You also need to specify the connection option
+ <literal>reconnect</literal> to be true. For example:
+ </para>
+ <programlisting>
+ qpid::messaging::Connection c("node1,node2,node3","{reconnect:true}");
+ </programlisting>
+ <para>
+ Heartbeats are disabled by default. You can enable them by specifying a
+ heartbeat interval (in seconds) for the connection via the
+ <literal>heartbeat</literal> option. For example:
+ <programlisting>
+ qpid::messaging::Connection c("node1,node2,node3","{reconnect:true,heartbeat:10}");
+ </programlisting>
+ </para>
+ </section>
+ <section>
+ <title>Python clients</title>
+ <para>
+ With the python client, you specify <literal>reconnect=True</literal>
+ and a list of <replaceable>host:port</replaceable> addresses as
+ <literal>reconnect_urls</literal> when calling
+ <literal>Connection.establish</literal> or
+ <literal>Connection.open</literal>
+ </para>
+ <programlisting>
+ connection = qpid.messaging.Connection.establish("node1", reconnect=True, reconnect_urls=["node1", "node2", "node3"])
+ </programlisting>
+ <para>
+ Heartbeats are disabled by default. You can
+ enable them by specifying a heartbeat interval (in seconds) for the
+ connection via the &#39;heartbeat&#39; option. For example:
+ </para>
+ <programlisting>
+ connection = qpid.messaging.Connection.establish("node1", reconnect=True, reconnect_urls=["node1", "node2", "node3"], heartbeat=10)
+ </programlisting>
+ </section>
+ <section>
+ <title>Java JMS Clients</title>
+ <para>
+ In Java JMS clients, client fail-over is handled automatically if it is
+ enabled in the connection. You can configure a connection to use
+ fail-over using the <command>failover</command> property:
+ </para>
+
+ <screen>
+ connectionfactory.qpidConnectionfactory = amqp://guest:guest@clientid/test?brokerlist=&#39;tcp://localhost:5672&#39;&amp;failover=&#39;failover_exchange&#39;
+ </screen>
+ <para>
+ This property can take three values:
+ </para>
+ <variablelist>
+ <title>Fail-over Modes</title>
+ <varlistentry>
+ <term>failover_exchange</term>
+ <listitem>
+ <para>
+ If the connection fails, fail over to any other broker in the cluster.
+ </para>
+
+ </listitem>
+
+ </varlistentry>
+ <varlistentry>
+ <term>roundrobin</term>
+ <listitem>
+ <para>
+ If the connection fails, fail over to one of the brokers specified in the <command>brokerlist</command>.
+ </para>
+
+ </listitem>
+
+ </varlistentry>
+ <varlistentry>
+ <term>singlebroker</term>
+ <listitem>
+ <para>
+ Fail-over is not supported; the connection is to a single broker only.
+ </para>
+
+ </listitem>
+
+ </varlistentry>
+
+ </variablelist>
+ <para>
+ In a Connection URL, heartbeat is set using the <command>idle_timeout</command> property, which is an integer corresponding to the heartbeat period in seconds. For instance, the following line from a JNDI properties file sets the heartbeat time out to 3 seconds:
+ </para>
+
+ <screen>
+ connectionfactory.qpidConnectionfactory = amqp://guest:guest@clientid/test?brokerlist=&#39;tcp://localhost:5672&#39;,idle_timeout=3
+ </screen>
+ </section>
+ </section>
+
+ <section>
+ <title>Integrating with other Cluster Resource Managers</title>
+ <para>
+ To integrate with a different resource manager you must configure it to:
+ <itemizedlist>
+ <listitem>Start a qpidd process on each node of the cluster.</listitem>
+ <listitem>Restart qpidd if it crases.</listitem>
+ <listitem>Promote exactly one of the brokers to primary.</listitem>
+ <listitem>Detect a failure and promote a new primary.</listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ The <command>qpid-ha</command> command allows you to check if a broker is primary,
+ and to promote a backup to primary.
+ </para>
+ <para>
+ To test if a broker is the primary:
+ <programlisting>
+ qpid-ha -b <replaceable>broker-address</replaceable> status --expect=primary
+ </programlisting>
+ This command will return 0 if the broker at <replaceable>broker-address</replaceable>
+ is the primary, non-0 otherwise.
+ </para>
+ <para>
+ To promote a broker to primary:
+ <programlisting>
+ qpid-ha -b <replaceable>broker-address</replaceable> promote
+ </programlisting>
+ </para>
+ <para>
+ <command>qpid-ha --help</command> gives information on other commands and options available.
+ You can also use <command>qpid-ha</command> to manually examine and promote brokers. This
+ can be useful for testing failover scenarios without having to set up a full resource manager,
+ or to simulate a cluster on a single node. For deployment, a resource manager is required.
+ </para>
+ </section>
+</section>
+
+<!-- LocalWords: scalability rgmanager multicast RGManager mailto LVQ qpidd IP dequeued Transactional username
+-->
diff --git a/qpid/doc/book/src/Cheat-Sheet-for-configuring-Exchange-Options.xml b/qpid/doc/book/src/cpp-broker/Cheat-Sheet-for-configuring-Exchange-Options.xml
index fccdae1b9a..fccdae1b9a 100644
--- a/qpid/doc/book/src/Cheat-Sheet-for-configuring-Exchange-Options.xml
+++ b/qpid/doc/book/src/cpp-broker/Cheat-Sheet-for-configuring-Exchange-Options.xml
diff --git a/qpid/doc/book/src/Cheat-Sheet-for-configuring-Queue-Options.xml b/qpid/doc/book/src/cpp-broker/Cheat-Sheet-for-configuring-Queue-Options.xml
index d50948e0cc..d50948e0cc 100644
--- a/qpid/doc/book/src/Cheat-Sheet-for-configuring-Queue-Options.xml
+++ b/qpid/doc/book/src/cpp-broker/Cheat-Sheet-for-configuring-Queue-Options.xml
diff --git a/qpid/doc/book/src/HA-Queue-Replication.xml b/qpid/doc/book/src/cpp-broker/HA-Queue-Replication.xml
index b7c533e4cb..b7c533e4cb 100644
--- a/qpid/doc/book/src/HA-Queue-Replication.xml
+++ b/qpid/doc/book/src/cpp-broker/HA-Queue-Replication.xml
diff --git a/qpid/doc/book/src/LVQ.xml b/qpid/doc/book/src/cpp-broker/LVQ.xml
index b57c6268be..b57c6268be 100644
--- a/qpid/doc/book/src/LVQ.xml
+++ b/qpid/doc/book/src/cpp-broker/LVQ.xml
diff --git a/qpid/doc/book/build-chapter.sh b/qpid/doc/book/src/cpp-broker/Makefile
index a2c2ad3b0c..0266a0f54d 100755..100644
--- a/qpid/doc/book/build-chapter.sh
+++ b/qpid/doc/book/src/cpp-broker/Makefile
@@ -1,4 +1,3 @@
-#!/bin/bash -ex
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
@@ -18,27 +17,4 @@
# under the License.
#
-########################################################################
-#
-# Build a PDF and HTML for a single chapter or section
-#
-# Specify the name of the XML file on the command line, omitting
-# the file extension, e.g.:
-#
-# $ ./build-chapter.sh src/High-Level-API
-#
-########################################################################
-
-rm -rf build
-mkdir -p build
-mkdir -p pdf
-
-
-# Create the .html
-xsltproc --stringparam section.autolabel 1 /usr/share/sgml/docbook/xsl-stylesheets/html/docbook.xsl src/$1.xml >build/$1.html
-
-# Create the .fo
-xsltproc --stringparam section.autolabel 1 /usr/share/sgml/docbook/xsl-stylesheets/fo/docbook.xsl src/$1.xml >build/$1.fo
-
-# Use Apache FOP to create the PDF
-fop build/$1.fo pdf/$1.pdf
+include ../Makefile.inc
diff --git a/qpid/doc/book/src/Managing-CPP-Broker.xml b/qpid/doc/book/src/cpp-broker/Managing-CPP-Broker.xml
index d2abea4296..d2abea4296 100644
--- a/qpid/doc/book/src/Managing-CPP-Broker.xml
+++ b/qpid/doc/book/src/cpp-broker/Managing-CPP-Broker.xml
diff --git a/qpid/doc/book/src/QMF-Python-Console-Tutorial.xml b/qpid/doc/book/src/cpp-broker/QMF-Python-Console-Tutorial.xml
index 2cb802671b..2cb802671b 100644
--- a/qpid/doc/book/src/QMF-Python-Console-Tutorial.xml
+++ b/qpid/doc/book/src/cpp-broker/QMF-Python-Console-Tutorial.xml
diff --git a/qpid/doc/book/src/Qpid-Interoperability-Documentation.xml b/qpid/doc/book/src/cpp-broker/Qpid-Interoperability-Documentation.xml
index 74546693df..74546693df 100644
--- a/qpid/doc/book/src/Qpid-Interoperability-Documentation.xml
+++ b/qpid/doc/book/src/cpp-broker/Qpid-Interoperability-Documentation.xml
diff --git a/qpid/doc/book/src/Qpid-Management-Framework.xml b/qpid/doc/book/src/cpp-broker/Qpid-Management-Framework.xml
index 89bfe9d95e..89bfe9d95e 100644
--- a/qpid/doc/book/src/Qpid-Management-Framework.xml
+++ b/qpid/doc/book/src/cpp-broker/Qpid-Management-Framework.xml
diff --git a/qpid/doc/book/src/Running-CPP-Broker.xml b/qpid/doc/book/src/cpp-broker/Running-CPP-Broker.xml
index 7dba5b41ce..7dba5b41ce 100644
--- a/qpid/doc/book/src/Running-CPP-Broker.xml
+++ b/qpid/doc/book/src/cpp-broker/Running-CPP-Broker.xml
diff --git a/qpid/doc/book/src/Security.xml b/qpid/doc/book/src/cpp-broker/Security.xml
index 49abfbebca..f28b72c71d 100644
--- a/qpid/doc/book/src/Security.xml
+++ b/qpid/doc/book/src/cpp-broker/Security.xml
@@ -336,55 +336,6 @@ acl deny all all
acl allow rajith@QPID all all
</programlisting>
<para>
- In deny mode, denying rights to an action is redundant and has no effect.
- </para>
-
-<programlisting>
-acl allow rajith@QPID all all
-acl deny jonathan@QPID all all # This rule is redundant, and has no effect
-acl deny all all
-</programlisting>
- <para>
- If the last line in an ACL file is <literal>acl allow all all</literal>, ACL uses <firstterm>allow mode</firstterm>, and all rights are granted except those that are explicitly denied. The following ACL file allows everyone else to perform any action, but denies <literal>jonathan@QPID</literal> all permissions.
- </para>
-
-<programlisting>
-acl deny jonathan@QPID all all
-acl allow all all
-</programlisting>
- <para>
- In allow mode, allowing rights to an action is redundant and has no effect.
- </para>
-
-<programlisting>
-acl allow rajith@QPID all all # This rule is redundant, and has no effect
-acl deny jonathan@QPID all all
-acl allow all all
-</programlisting>
- <important>
- <title>Important</title>
- <para>
- ACL processing ends when one of the following lines is encountered:
- </para>
-
-<programlisting>
-acl allow all all
-</programlisting>
-
-<programlisting>
-acl deny all all
-</programlisting>
- <para>
- Any lines that occur after one of these statements will be ignored:
- </para>
-
-<programlisting>
-acl allow all all
-acl deny jonathan@QPID all all # This line is ignored !!!
-</programlisting>
-
- </important>
- <para>
ACL syntax allows fine-grained access rights for specific actions:
</para>
@@ -408,8 +359,25 @@ acl deny all all
<title>ACL Syntax</title>
<para>
ACL rules must be on a single line and follow this syntax:
-<programlisting>acl permission {&#60;group-name&#62;|&#60;user-name&#62;|&#34;all&#34;} {action|&#34;all&#34;} [object|&#34;all&#34;] [property=&#60;property-value&#62;]
-</programlisting>
+<programlisting><![CDATA[
+user = username[/domain[@realm]]
+user-list = user1 user2 user3 ...
+group-name-list = group1 group2 group3 ...
+
+group <group-name> = [user-list] [group-name-list]
+
+permission = [allow|allow-log|deny|deny-log]
+action = [consume|publish|create|access|bind|unbind|delete|purge|update]
+object = [virtualhost|queue|exchange|broker|link|route|method]
+property = [name|durable|owner|routingkey|autodelete|exclusive|
+ type|alternate|queuename|schemapackage|schemaclass|
+ queuemaxsizelowerlimit|queuemaxsizeupperlimit|
+ queuemaxcountlowerlimit|queuemaxcountupperlimit]
+
+acl permission {<group-name>|<user-name>|"all"} {action|"all"} [object|"all"
+ [property=<property-value> ...]]
+]]></programlisting>
+
ACL rules can also include a single object name (or the keyword <parameter>all</parameter>) and one or more property name value pairs in the form <command>property=value</command>
</para>
<para>
@@ -665,142 +633,102 @@ acl deny all all
<para>
Wild cards can be used on properties that are a string. The following properties are supported: --> <table id="tabl-Messaging_User_Guide-ACL_Syntax-ACL_Rulesproperty">
<title>ACL Rules:property</title>
- <tgroup cols="2">
- <tbody>
- <row>
- <entry>
- <command>name</command>
- </entry>
- <entry>
- <para>
- String. Object name, such as a queue name or exchange name.
- </para>
-
- </entry>
-
- </row>
- <row>
- <entry>
- <command>durable</command>
- </entry>
- <entry>
- <para>
- Boolean. Indicates the object is durable
- </para>
-
- </entry>
-
- </row>
- <row>
- <entry>
- <command>routingkey</command>
- </entry>
- <entry>
- <para>
- Sring. Specifies routing key
- </para>
-
- </entry>
-
- </row>
- <row>
- <entry>
- <command>passive</command>
- </entry>
- <entry>
- <para>
- Boolean. Indicates the presence of a <parameter>passive</parameter> flag
- </para>
-
- </entry>
-
- </row>
- <row>
- <entry>
- <command>autodelete</command>
- </entry>
- <entry>
- <para>
- Boolean. Indicates whether or not the object gets deleted when the connection is closed
- </para>
-
- </entry>
-
- </row>
- <row>
- <entry>
- <command>exclusive</command>
- </entry>
- <entry>
- <para>
- Boolean. Indicates the presence of an <parameter>exclusive</parameter> flag
- </para>
-
- </entry>
-
- </row>
- <row>
- <entry>
- <command>type</command>
- </entry>
- <entry>
- <para>
- String. Type of object, such as topic, fanout, or xml
- </para>
-
- </entry>
-
- </row>
- <row>
- <entry>
- <command>alternate</command>
- </entry>
- <entry>
- <para>
- String. Name of the alternate exchange
- </para>
-
- </entry>
-
- </row>
- <row>
- <entry>
- <command>queuename</command>
- </entry>
- <entry>
- <para>
- String. Name of the queue (used only when the object is something other than <parameter>queue</parameter>
- </para>
-
- </entry>
-
- </row>
- <row>
- <entry>
- <command>schemapackage</command>
- </entry>
- <entry>
- <para>
- String. QMF schema package name
- </para>
-
- </entry>
-
- </row>
- <row>
- <entry>
- <command>schemaclass</command>
- </entry>
- <entry>
- <para>
- String. QMF schema class name
- </para>
-
- </entry>
-
- </row>
-
- </tbody>
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Property</entry>
+ <entry>Type</entry>
+ <entry>Description</entry>
+ <entry>Usage</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry> <command>name</command> </entry>
+ <entry>String</entry>
+ <entry>Object name, such as a queue name or exchange name.</entry>
+ <entry>.</entry>
+ </row>
+ <row>
+ <entry> <command>durable</command> </entry>
+ <entry>Boolean</entry>
+ <entry>Indicates the object is durable</entry>
+ <entry>CREATE QUEUE, CREATE EXCHANGE</entry>
+ </row>
+ <row>
+ <entry> <command>routingkey</command> </entry>
+ <entry>String</entry>
+ <entry>Specifies routing key</entry>
+ <entry>BIND EXCHANGE, UNBIND EXCHANGE, ACCESS EXCHANGE</entry>
+ </row>
+ <row>
+ <entry> <command>autodelete</command> </entry>
+ <entry>Boolean</entry>
+ <entry>Indicates whether or not the object gets deleted when the connection is closed</entry>
+ <entry>CREATE QUEUE</entry>
+ </row>
+ <row>
+ <entry> <command>exclusive</command> </entry>
+ <entry>Boolean</entry>
+ <entry>Indicates the presence of an <parameter>exclusive</parameter> flag</entry>
+ <entry>CREATE QUEUE</entry>
+ </row>
+ <row>
+ <entry> <command>type</command> </entry>
+ <entry>String</entry>
+ <entry>Type of exchange, such as topic, fanout, or xml</entry>
+ <entry>CREATE EXCHANGE</entry>
+ </row>
+ <row>
+ <entry> <command>alternate</command> </entry>
+ <entry>String</entry>
+ <entry>Name of the alternate exchange</entry>
+ <entry>CREATE EXCHANGE, CREATE QUEUE</entry>
+ </row>
+ <row>
+ <entry> <command>queuename</command> </entry>
+ <entry>String</entry>
+ <entry>Name of the queue</entry>
+ <entry>ACCESS EXCHANGE</entry>
+ </row>
+ <row>
+ <entry> <command>schemapackage</command> </entry>
+ <entry>String</entry>
+ <entry>QMF schema package name</entry>
+ <entry>ACCESS METHOD</entry>
+ </row>
+ <row>
+ <entry> <command>schemaclass</command> </entry>
+ <entry>String</entry>
+ <entry>QMF schema class name</entry>
+ <entry>ACCESS METHOD</entry>
+ </row>
+ <row>
+ <entry> <command>queuemaxsizelowerlimit</command> </entry>
+ <entry>Integer</entry>
+ <entry>Minimum value for queue.max_size</entry>
+ <entry>CREATE QUEUE</entry>
+ </row>
+ <row>
+ <entry> <command>queuemaxsizeupperlimit</command> </entry>
+ <entry>Integer</entry>
+ <entry>Maximum value for queue.max_size</entry>
+ <entry>CREATE QUEUE</entry>
+ </row>
+ <row>
+ <entry> <command>queuemaxcountlowerlimit</command> </entry>
+ <entry>Integer</entry>
+ <entry>Minimum value for queue.max_count</entry>
+ <entry>CREATE QUEUE</entry>
+ </row>
+ <row>
+ <entry> <command>queuemaxcountupperlimit</command> </entry>
+ <entry>Integer</entry>
+ <entry>Maximum value for queue.max_count</entry>
+ <entry>CREATE QUEUE</entry>
+ </row>
+
+ </tbody>
</tgroup>
@@ -821,19 +749,19 @@ acl deny all all
</listitem>
<listitem>
<para>
- Empty lines and lines that contain only whitespace are ignored
+ Empty lines and lines that contain only whitespace (' ', '\f', '\n', '\r', '\t', '\v') are ignored.
</para>
</listitem>
<listitem>
<para>
- All tokens are case sensitive. <parameter>name1</parameter> is not the same as <parameter>Name1</parameter> and <parameter>create</parameter> is not the same as <parameter>CREATE</parameter>
+ All tokens are case sensitive. <parameter>name1</parameter> is not the same as <parameter>Name1</parameter> and <parameter>create</parameter> is not the same as <parameter>CREATE</parameter>.
</para>
</listitem>
<listitem>
<para>
- Group lists can be extended to the following line by terminating the line with the <command>\</command> character
+ Group lists can be extended to the following line by terminating the line with the <command>\</command> character.
</para>
</listitem>
@@ -845,31 +773,37 @@ acl deny all all
</listitem>
<listitem>
<para>
- All ACL rules are limited to a single line
+ All ACL rules are limited to a single line of at most 1024 characters.
+ </para>
+
+ </listitem>
+ <listitem>
+ <para>
+ Rules are interpreted from the top of the file down until a matching rule is obtained. The matching rule then controls the allow or deny decision.
</para>
</listitem>
<listitem>
<para>
- Rules are interpreted from the top of the file down until the name match is obtained; at which point processing stops.
+ The keyword <parameter>all</parameter> is reserved and may be used in ACL rules to match all individuals and groups, all actions, or all objects.
</para>
</listitem>
<listitem>
<para>
- The keyword <parameter>all</parameter> matches all individuals, groups and actions
+ By default ACL files are in 'Deny Mode' and deny all actions by all users. That is, there is an implicit <parameter>acl deny all all</parameter> rule appended to the ACL rule list.
</para>
</listitem>
<listitem>
<para>
- The last line of the file - whether present or not - will be assumed to be <command>acl deny all all</command>. If present in the file, all lines below it are ignored.
+ Group names may contain only <parameter>a-z</parameter>, <parameter>A-Z</parameter>, <parameter>0-9</parameter>, <parameter>- hyphen</parameter> and <parameter>_ underscore</parameter>.
</para>
</listitem>
<listitem>
<para>
- Names and group names may contain only <parameter>a-z</parameter>, <parameter>A-Z</parameter>, <parameter>0-9</parameter>, <parameter>-</parameter> and <parameter>_</parameter>
+ Individual user names may contain only <parameter>a-z</parameter>, <parameter>A-Z</parameter>, <parameter>0-9</parameter>, <parameter>- hyphen</parameter>, <parameter>_ underscore</parameter>, <parameter>. period</parameter>, <parameter>@ ampersand</parameter>, and <parameter>/ slash</parameter>.
</para>
</listitem>
@@ -886,6 +820,75 @@ acl deny all all
</section>
+ <section id="sect-Messaging_User_Guide-Authorization-ACL_Rule_Matching">
+ <title>ACL Rule Matching</title>
+ <para>
+ The minimum matching criteria for ACL rules are:
+ <itemizedlist>
+ <listitem>An actor (individually named or group member)</listitem>
+ <listitem>An action</listitem>
+ <listitem>An object</listitem>
+ </itemizedlist>
+ </para>
+ <para>
+ If a rule does not match the minimum criteria then that rule does not control the ACL allow or deny decision.
+ </para>
+ <para>
+ ACL rules optionally specify object names and property name=value pairs. If an ACL rule specifies an object name or property values than all of them must match to cause the rule to match.
+ </para>
+ <para>
+ The following illustration shows how ACL rules are processed to find matching rules.
+<programlisting><![CDATA[
+# Example of rule matching
+#
+# Using this ACL file content:
+
+(1) acl deny bob create exchange name=test durable=true passive=true
+(2) acl deny bob create exchange name=myEx type=direct
+(3) acl allow all all
+
+#
+# Lookup 1. id:bob action:create objectType:exchange name=test
+# {durable=false passive=false type=direct alternate=}
+#
+# ACL Match Processing:
+# 1. Rule 1 passes minimum criteria with user bob, action create,
+# and object exchange.
+# 2. Rule 1 matches name=test.
+# 3. Rule 1 does not match the rule's durable=true with the requested
+# lookup of durable=false.
+# 4. Rule 1 does not control the decision and processing continues
+# to Rule 2.
+# 5. Rule 2 passes minimum criteria with user bob, action create,
+# and object exchange.
+# 6. Rule 2 does not match the rule's name=myEx with the requested
+# lookup of name=test.
+# 7. Rule 2 does not control the decision and processing continues
+# to Rule 3.
+# 8. Rule 3 matches everything and the decision is 'allow'.
+#
+# Lookup 2. id:bob action:create objectType:exchange name=myEx
+# {durable=true passive=true type=direct alternate=}
+#
+# ACL Match Processing:
+# 1. Rule 1 passes minimum criteria with user bob, action create,
+# and object exchange.
+# 6. Rule 1 does not match the rule's name=test with the requested
+# lookup of name=myEx.
+# 4. Rule 1 does not control the decision and processing continues
+# to Rule 2.
+# 5. Rule 2 passes minimum criteria with user bob, action create,
+# and object exchange.
+# 2. Rule 2 matches name=myEx.
+# 3. Rule 2 matches the rule's type=direct with the requested
+# lookup of type=direct.
+# 8. Rule 2 is the matching rule and the decision is 'deny'.
+#
+]]></programlisting>
+ </para>
+
+ </section>
+
<section id="sect-Messaging_User_Guide-Authorization-Specifying_ACL_Permissions">
<title>Specifying ACL Permissions</title>
<para>
@@ -925,6 +928,20 @@ acl deny all all
<para>
In the previous example, the last line, <literal>acl deny all all</literal>, denies all authorizations that have not been specifically granted. This is the default, but it is useful to include it explicitly on the last line for the sake of clarity. If you want to grant all rights by default, you can specify <literal>acl allow all all</literal> in the last line.
</para>
+ <para>
+ ACL allows specification of conflicting rules. Be sure to specify the most specific rules first followed by more general rules. Here is an example:
+ </para>
+ <para>
+<programlisting>
+group users alice@QPID bob@QPID charlie@QPID
+acl deny charlie@QPID create queue
+acl allow users create queue
+acl deny all all
+</programlisting>
+ </para>
+ <para>
+ In this example users alice and bob would be able to create queues due to their membership in the users group. However, user charlie is denied from creating a queue despite his membership in the users group because a deny rule for him is stated before the allow rule for the users group.
+ </para>
<para>
Do not allow <parameter>guest</parameter> to access and log QMF management methods that could cause security breaches:
</para>
@@ -940,6 +957,30 @@ acl allow all all
</section>
+ <section id="sect-Messaging_User_Guide-Authorization-Specifying_ACL_Connection_Limits">
+ <title>Specifying ACL Connection Limits</title>
+ <para>
+ The ACL module creates two broker command line switches that set limits on the number of connections allowed per user or per client host address. These settings are not specified in the ACL file.
+ </para>
+ <para>
+<programlisting>
+--acl-max-connect-per-user N_USER
+--acl-max-connect-per-ip N_IP
+</programlisting>
+ </para>
+ <para>
+ If either of these switches is not specified or the value specified is zero then the corresponding connection limit is not enforced.
+ </para>
+ <para>
+ If a limit is set for user connections then all users are limited to that number of connections regardless of the client IP address the users are coming from.
+ </para>
+ <para>
+ If a limit is set for IP connections then connections for a given IP address are limited regardless of the user credentials presented with the connection.
+ </para>
+ <para>
+ Note that addresses using different transports are counted separately even though the host is actually the same physical machine. In the setting illustrated above a host would allow N_IP connections from [::1] IPv6 transport localhost and another N_IP connections from [127.0.0.1] IPv4 transport localhost.
+ </para>
+ </section>
</section>
diff --git a/qpid/doc/book/src/Using-Broker-Federation.xml b/qpid/doc/book/src/cpp-broker/Using-Broker-Federation.xml
index f5fedf814c..f5fedf814c 100644
--- a/qpid/doc/book/src/Using-Broker-Federation.xml
+++ b/qpid/doc/book/src/cpp-broker/Using-Broker-Federation.xml
diff --git a/qpid/doc/book/src/Using-message-groups.xml b/qpid/doc/book/src/cpp-broker/Using-message-groups.xml
index 9b904d9f18..9b904d9f18 100644
--- a/qpid/doc/book/src/Using-message-groups.xml
+++ b/qpid/doc/book/src/cpp-broker/Using-message-groups.xml
diff --git a/qpid/doc/book/src/producer-flow-control.xml b/qpid/doc/book/src/cpp-broker/producer-flow-control.xml
index fd44f51e81..fd44f51e81 100644
--- a/qpid/doc/book/src/producer-flow-control.xml
+++ b/qpid/doc/book/src/cpp-broker/producer-flow-control.xml
diff --git a/qpid/doc/book/src/queue-state-replication.xml b/qpid/doc/book/src/cpp-broker/queue-state-replication.xml
index 3ffac805eb..3ffac805eb 100644
--- a/qpid/doc/book/src/queue-state-replication.xml
+++ b/qpid/doc/book/src/cpp-broker/queue-state-replication.xml
diff --git a/qpid/doc/book/src/css/style.css b/qpid/doc/book/src/css/style.css
deleted file mode 100644
index 2a1bee8623..0000000000
--- a/qpid/doc/book/src/css/style.css
+++ /dev/null
@@ -1,129 +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.
- *
- */
-ul {
- list-style-type:square;
-}
-
-th {
- font-weight: bold;
-}
-
-.navfooter td {
- font-size:10pt;
-}
-
-.navheader td {
- font-size:10pt;
-}
-
-body {
- width:950px;
- margin-left:100px;
- margin-top:40px;
-
- background:#FFFFFF;
- font-family:"Verdana", sans-serif;
- font-size:10pt;
-}
-
-body a {
- color:#000000;
-}
-
-
-div.book {
- margin-left:10pt;
- margin-right:10pt;
-}
-
-div.preface {
- margin-left:10pt;
- margin-right:10pt;
-}
-
-div.chapter {
- margin-left:10pt;
- margin-right:10pt;
-}
-
-div.section {
- margin-left:10pt;
- margin-right:10pt;
-}
-
-div.titlepage {
- margin-left:-10pt;
- margin-right:-10pt;
-}
-
-.calloutlist td {
- font-size:10pt;
-}
-
-.table-contents table {
- border-spacing: 0px;
-}
-
-.table-contents td {
- font-size:10pt;
- padding-left:6px;
- padding-right:6px;
-}
-
-.chapter h2.title {
- font-size:20pt;
- color:#0c3b82;
-}
-
-.chapter .section h2.title {
- font-size:18pt;
- color:#0c3b82;
-}
-
-.section h2.title {
- font-size:16pt;
- color:#0c3b82;
-}
-
-.section h3.title {
- font-size:14pt;
- color:#0c3b82;
-}
-
-.section h4.title {
- font-size:12pt;
- color:#0c3b82;
-}
-
-.section h5.title {
- font-size:12pt;
- color:#0c3b82;
-}
-
-.section h6.title {
- font-size:12pt;
- color:#0c3b82;
-}
-
-.toc a {
- font-size:9pt;
-}
-
diff --git a/qpid/doc/book/src/AMQP-Messaging-Broker-Java-Book.xml b/qpid/doc/book/src/java-broker/AMQP-Messaging-Broker-Java-Book.xml
index 0bb40052cd..d4eb71da2b 100644
--- a/qpid/doc/book/src/AMQP-Messaging-Broker-Java-Book.xml
+++ b/qpid/doc/book/src/java-broker/AMQP-Messaging-Broker-Java-Book.xml
@@ -31,7 +31,7 @@
<listitem><para>Implemented in Java - Fully JMS compliant, runs on any Java platform.</para></listitem>
</itemizedlist>
- <para>Both AMQP messaging brokers support clients in multiple languages, as long as the messaging client and the messaging broker use the same version of AMQP. See <link linkend="AMQP-Compatibility"/> to see which messaging clients work with each broker.</para>
+ <para>Both AMQP messaging brokers support clients in multiple languages, as long as the messaging client and the messaging broker use the same version of AMQP.</para>
<para>This manual contains information specific to the broker that is implemented in Java.</para>
</preface>
@@ -44,6 +44,7 @@
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Java-Environment-Variables.xml"/>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Qpid-Troubleshooting-Guide.xml"/>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Broker-Configuration-Guide.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="HA-Guide.xml"/>
</chapter>
<chapter id="Qpid-Java-Broker-HowTos">
@@ -67,7 +68,6 @@
<chapter id="QpidJavaBroker-ManagementTools">
<title>Management Tools</title>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="MessageStore-Tool.xml"/>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Qpid-Java-Broker-Management-CLI.xml"/>
</chapter>
</book>
diff --git a/qpid/doc/book/src/Add-New-Users.xml b/qpid/doc/book/src/java-broker/Add-New-Users.xml
index dc34bcc5c9..dc34bcc5c9 100644
--- a/qpid/doc/book/src/Add-New-Users.xml
+++ b/qpid/doc/book/src/java-broker/Add-New-Users.xml
diff --git a/qpid/doc/book/src/Broker-Configuration-Guide.xml b/qpid/doc/book/src/java-broker/Broker-Configuration-Guide.xml
index 63d2748eee..558d17c63c 100644
--- a/qpid/doc/book/src/Broker-Configuration-Guide.xml
+++ b/qpid/doc/book/src/java-broker/Broker-Configuration-Guide.xml
@@ -23,6 +23,6 @@
<section id="Java-Broker-Configuration-Guide">
<title>Broker Configuration Guide </title>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"
- href="java/broker/configuration/Topic-Configuration.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Producer-Flow-Control.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Topic-Configuration.xml"/>
</section>
diff --git a/qpid/doc/book/src/Configure-ACLs.xml b/qpid/doc/book/src/java-broker/Configure-ACLs.xml
index e82f2a86d0..e82f2a86d0 100644
--- a/qpid/doc/book/src/Configure-ACLs.xml
+++ b/qpid/doc/book/src/java-broker/Configure-ACLs.xml
diff --git a/qpid/doc/book/src/Configure-Java-Qpid-to-use-a-SSL-connection.xml b/qpid/doc/book/src/java-broker/Configure-Java-Qpid-to-use-a-SSL-connection.xml
index 838b899337..838b899337 100644
--- a/qpid/doc/book/src/Configure-Java-Qpid-to-use-a-SSL-connection.xml
+++ b/qpid/doc/book/src/java-broker/Configure-Java-Qpid-to-use-a-SSL-connection.xml
diff --git a/qpid/doc/book/src/Configure-Log4j-CompositeRolling-Appender.xml b/qpid/doc/book/src/java-broker/Configure-Log4j-CompositeRolling-Appender.xml
index f52bc55399..f52bc55399 100644
--- a/qpid/doc/book/src/Configure-Log4j-CompositeRolling-Appender.xml
+++ b/qpid/doc/book/src/java-broker/Configure-Log4j-CompositeRolling-Appender.xml
diff --git a/qpid/doc/book/src/Configure-the-Broker-via-config.xml.xml b/qpid/doc/book/src/java-broker/Configure-the-Broker-via-config.xml.xml
index 6a7729acd8..6a7729acd8 100644
--- a/qpid/doc/book/src/Configure-the-Broker-via-config.xml.xml
+++ b/qpid/doc/book/src/java-broker/Configure-the-Broker-via-config.xml.xml
diff --git a/qpid/doc/book/src/Configure-the-Virtual-Hosts-via-virtualhosts.xml.xml b/qpid/doc/book/src/java-broker/Configure-the-Virtual-Hosts-via-virtualhosts.xml.xml
index 804970b923..804970b923 100644
--- a/qpid/doc/book/src/Configure-the-Virtual-Hosts-via-virtualhosts.xml.xml
+++ b/qpid/doc/book/src/java-broker/Configure-the-Virtual-Hosts-via-virtualhosts.xml.xml
diff --git a/qpid/doc/book/src/Configuring-Management-Users.xml b/qpid/doc/book/src/java-broker/Configuring-Management-Users.xml
index a2a8d46d88..a2a8d46d88 100644
--- a/qpid/doc/book/src/Configuring-Management-Users.xml
+++ b/qpid/doc/book/src/java-broker/Configuring-Management-Users.xml
diff --git a/qpid/doc/book/src/Configuring-Qpid-JMX-Management-Console.xml b/qpid/doc/book/src/java-broker/Configuring-Qpid-JMX-Management-Console.xml
index 72e4ba8969..72e4ba8969 100644
--- a/qpid/doc/book/src/Configuring-Qpid-JMX-Management-Console.xml
+++ b/qpid/doc/book/src/java-broker/Configuring-Qpid-JMX-Management-Console.xml
diff --git a/qpid/doc/book/src/Debug-using-log4j.xml b/qpid/doc/book/src/java-broker/Debug-using-log4j.xml
index 615fd9e560..615fd9e560 100644
--- a/qpid/doc/book/src/Debug-using-log4j.xml
+++ b/qpid/doc/book/src/java-broker/Debug-using-log4j.xml
diff --git a/qpid/doc/book/src/java-broker/HA-Guide.xml b/qpid/doc/book/src/java-broker/HA-Guide.xml
new file mode 100644
index 0000000000..429f2f76c2
--- /dev/null
+++ b/qpid/doc/book/src/java-broker/HA-Guide.xml
@@ -0,0 +1,990 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE urls [
+<!ENTITY oracleBdbProductOverviewUrl "http://www.oracle.com/technetwork/products/berkeleydb/overview/index-093405.html">
+<!ENTITY oracleBdbProductVersion "5.0.48">
+<!ENTITY oracleBdbRepGuideUrl "http://oracle.com/cd/E17277_02/html/ReplicationGuide/">
+<!ENTITY oracleBdbJavaDocUrl "http://docs.oracle.com/cd/E17277_02/html/java/">
+<!ENTITY oracleJdkDocUrl "http://oracle.com/javase/6/docs/api/">
+]>
+<!--
+
+ 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.
+
+-->
+<section>
+ <title>High Availability</title>
+ <section role="h3" id="HAGeneralIntroduction">
+ <title>General Introduction</title>
+ <para>The term High Availability (HA) usually refers to having a number of instances of a service such as a Message Broker
+ available so that should a service unexpectedly fail, or requires to be shutdown for maintenance, users may quickly connect
+ to another instance and continue their work with minimal interuption. HA is one way to make a overall system more resilient
+ by eliminating a single point of failure from a system.</para>
+ <para>HA offerings are usually categorised as <emphasis role="bold">Active/Active</emphasis> or <emphasis role="bold">Active/Passive</emphasis>.
+ An Active/Active system is one where all nodes within the cluster are usuaully available for use by clients all of the time. In an
+ Active/Passive system, one only node within the cluster is available for use by clients at any one time, whilst the others are in
+ some kind of standby state, awaiting to quickly step-in in the event the active node becomes unavailable.
+ </para>
+ </section>
+ <section role="h3" id="HAOfferingsOfJavaBroker">
+ <title>HA offerings of the Java Broker</title>
+ <para>The Java Broker's HA offering became available at release <emphasis role="bold">0.18</emphasis>. HA is provided by way of the HA
+ features built into the <ulink url="&oracleBdbProductOverviewUrl;">Java Edition of the Berkley Database (BDB JE)</ulink> and as such
+ is currently only available to Java Broker users who use the optional BDB JE based persistence store. This
+ <emphasis role="bold">optional</emphasis> store requires the use of BDB JE which is licensed under the Sleepycat Licence, which is
+ not compatible with the Apache Licence and thus BDB JE is not distributed with Qpid. Users who elect to use this optional store for
+ the broker have to provide this dependency.</para>
+ <para>HA in the Java Broker provides an <emphasis role="bold">Active/Passive</emphasis> mode of operation with Virtual hosts being
+ the unit of replication. The Active node (referred to as the <emphasis role="bold">Master</emphasis>) accepts all work from all the clients.
+ The Passive nodes (referred to as <emphasis role="bold">Replicas</emphasis>) are unavailable for work: the only task they must perform is
+ to remain in synch with the Master node by consuming a replication stream containing all data and state.</para>
+ <para>If the Master node fails, a Replica node is elected to become the new Master node. All clients automatically failover
+ <footnote><para>The automatic failover feature is available only for AMQP connections from the Java client. Management connections (JMX)
+ do not current offer this feature.</para></footnote> to the new Master and continue their work.</para>
+ <para>The Java Broker HA solution is incompatible with the HA solution offered by the CPP Broker. It is not possible to co-locate Java and CPP
+ Brokers within the same cluster.</para>
+ <para>HA is not currently available for those using the the <emphasis role="bold">Derby Store</emphasis> or <emphasis role="bold">Memory
+ Message Store</emphasis>.</para>
+ </section>
+ <section role="h3" id="HATwoNodeCluster">
+ <title>Two Node Cluster</title>
+ <section role="h4">
+ <title>Overview</title>
+ <para>In this HA solution, a cluster is formed with two nodes. one node serves as
+ <emphasis role="bold">master</emphasis> and the other is a <emphasis role="bold">replica</emphasis>.
+ </para>
+ <para>All data and state required for the operation of the virtual host is automatically sent from the
+ master to the replica. This is called the replication stream. The master virtual host confirms each
+ message is on the replica before the client transaction completes. The exact way the client awaits
+ for the master and replica is gorverned by the <link linkend="HADurabilityGuarantee">durability</link>
+ configuration, which is discussed later. In this way, the replica remains ready to take over the
+ role of the master if the master becomes unavailable.
+ </para>
+ <para>It is important to note that there is an inherent limitation of two node clusters is that
+ the replica node cannot make itself master automatically in the event of master failure. This
+ is because the replica has no way to distinguish between a network partition (with potentially
+ the master still alive on the other side of the partition) and the case of genuine master failure.
+ (If the replica were to elect itself as master, the cluster would run the risk of a
+ <ulink url="http://en.wikipedia.org/wiki/Split-brain_(computing)">split-brain</ulink> scenario).
+ In the event of a master failure, a third party must designate the replica as primary. This process
+ is described in more detail later.
+ </para>
+ <para>Clients connect to the cluster using a <link linkend="HAClientFailover">failover url</link>.
+ This allows the client to maintain a connection to the master in a way that is transparent
+ to the client application.</para>
+ </section>
+ <section role="h4">
+ <title>Depictions of cluster operation</title>
+ <para>In this section, the operation of the cluster is depicted through a series of figures
+ supported by explanatory text.</para>
+ <figure>
+ <title>Key for figures</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="images/HA-2N-Key.png" format="PNG" scalefit="1"/>
+ </imageobject>
+ <textobject>
+ <phrase>Key to figures</phrase>
+ </textobject>
+ </mediaobject>
+ </figure>
+ <section role="h5" id="HATwoNodeNormalOperation">
+ <title>Normal Operation</title>
+ <para>The figure below illustrates normal operation. Clients connecting to the cluster by way
+ of the failover URL achieve a connection to the master. As clients perform work (message
+ production, consumption, queue creation etc), the master additionally sends this data to the
+ replica over the network.</para>
+ <figure>
+ <title>Normal operation of a two-node cluster</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="images/HA-2N-Normal.png" format="PNG" scalefit="1"/>
+ </imageobject>
+ <textobject>
+ <phrase>Normal operation</phrase>
+ </textobject>
+ </mediaobject>
+ </figure>
+ </section>
+ <section role="h5" id="HATwoNodeMasterFailure">
+ <title>Master Failure and Recovery</title>
+ <para>The figure below illustrates a sequence of events whereby the master suffers a failure
+ and the replica is made the master to allow the clients to continue to work. Later the
+ old master is repaired and comes back on-line in replica role.</para>
+ <para>The item numbers in this list apply to the numbered boxes in the figure below.</para>
+ <orderedlist>
+ <listitem>
+ <para>System operating normally</para>
+ </listitem>
+ <listitem>
+ <para>Master suffers a failure and disconnects all clients. Replica realises that it is no
+ longer in contact with master. Clients begin to try to reconnect to the cluster, although these
+ connection attempts will fail at this point.</para>
+ </listitem>
+ <listitem>
+ <para>A third-party (an operator, a script or a combination of the two) verifies that the master has truely
+ failed <emphasis role="bold">and is no longer running</emphasis>. If it has truely failed, the decision is made
+ to designate the replica as primary, allowing it to assume the role of master despite the other node being down.
+ This primary designation is performed using <link linkend="HAJMXAPI">JMX</link>.</para>
+ </listitem>
+ <listitem>
+ <para>Client connections to the new master succeed and the <emphasis role="bold">service is restored
+ </emphasis>, albeit without a replica.</para>
+ </listitem>
+ <listitem>
+ <para>The old master is repaired and brought back on-line. It automatically rejoins the cluster
+ in the <emphasis role="bold">replica</emphasis> role.</para>
+ </listitem>
+ </orderedlist>
+ <figure>
+ <title>Failure of master and recovery sequence</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="images/HA-2N-MasterFail.png" format="PNG" scalefit="1"/>
+ </imageobject>
+ <textobject>
+ <phrase>Failure of master and subsequent recovery sequence</phrase>
+ </textobject>
+ </mediaobject>
+ </figure>
+ </section>
+ <section role="h5" id="HATwoNodeReplicaFailure">
+ <title>Replica Failure and Recovery</title>
+ <para>The figure that follows illustrates a sequence of events whereby the replica suffers a failure
+ leaving the master to continue processing alone. Later the replica is repaired and is restarted.
+ It rejoins the cluster so that it is once again ready to take over in the event of master failure.</para>
+ <para>The behavior of the replica failure case is governed by the <varname>designatedPrimary</varname>
+ configuration item. If set true on the master, the master will continue to operate solo without outside
+ intervention when the replica fails. If false, a third-party must designate the master as primary in order
+ for it to continue solo.</para>
+ <para>The item numbers in this list apply to the numbered boxes in the figure below. This example assumes
+ that <varname>designatedPrimary</varname> is true on the original master node.</para>
+ <orderedlist>
+ <listitem>
+ <para>System operating normally</para>
+ </listitem>
+ <listitem>
+ <para>Replica suffers a failure. Master realises that replica longer in contact but as
+ <varname>designatedPrimary</varname> is true, master continues processing solo and thus client
+ connections are uninterrupted by the loss of the replica. System continues operating normally, albeit
+ with a single node.</para>
+ </listitem>
+ <listitem>
+ <para>Replica is repaired.</para>
+ </listitem>
+ <listitem>
+ <para>After catching up with missed work, replica is once again ready to take over in the event of master failure.</para>
+ </listitem>
+ </orderedlist>
+ <figure>
+ <title>Failure of replica and subsequent recovery sequence</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="images/HA-2N-ReplicaFail.png" format="PNG" scalefit="1"/>
+ </imageobject>
+ <textobject>
+ <phrase>Failure of replica and subsequent recovery sequence</phrase>
+ </textobject>
+ </mediaobject>
+ </figure>
+ </section>
+ <section role="h5" id="HATwoNodeNetworkPartition">
+ <title>Network Partition and Recovery</title>
+ <para>The figure below illustrates the sequence of events that would occur if the network between
+ master and replica were to suffer a partition, and the nodes were out of contact with one and other.</para>
+ <para>As with <link linkend="HATwoNodeReplicaFailure">Replica Failure and Recovery</link>, the
+ behaviour is governed by the <varname>designatedPrimary</varname>.
+ Only if <varname>designatedPrimary</varname> is true on the master, will the master continue solo.</para>
+ <para>The item numbers in this list apply to the numbered boxes in the figure below. This example assumes
+ that <varname>designatedPrimary</varname> is true on the original master node.</para>
+ <orderedlist>
+ <listitem>
+ <para>System operating normally</para>
+ </listitem>
+ <listitem>
+ <para>Network suffers a failure. Master realises that replica longer in contact but as
+ <varname>designatedPrimary</varname> is true, master continues processing solo and thus client
+ connections are uninterrupted by the network partition between master and replica.</para>
+ </listitem>
+ <listitem>
+ <para>Network is repaired.</para>
+ </listitem>
+ <listitem>
+ <para>After catching up with missed work, replica is once again ready to take over in the event of master failure.
+ System operating normally again.</para>
+ </listitem>
+ </orderedlist>
+ <figure>
+ <title>Partition of the network separating master and replica</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="images/HA-2N-NetworkPartition.png" format="PNG" scalefit="1"/>
+ </imageobject>
+ <textobject>
+ <phrase>Network Partition and Recovery</phrase>
+ </textobject>
+ </mediaobject>
+ </figure>
+ </section>
+ <section role="h5" id="HATwoNodeSplitBrain">
+ <title>Split Brain</title>
+ <para>A <ulink url="http://en.wikipedia.org/wiki/Split-brain_(computing)">split-brain</ulink>
+ is a situation where the two node cluster has two masters. BDB normally strives to prevent
+ this situation arising by preventing two nodes in a cluster being master at the same time.
+ However, if the network suffers a partition, and the third-party intervenes incorrectly
+ and makes the replica a second master a split-brain will be formed and both masters will
+ proceed to perform work <emphasis role="bold">independently</emphasis> of one and other.</para>
+ <para>There is no automatic recovery from a split-brain.</para>
+ <para>Manual intervention will be required to choose which store will be retained as master
+ and which will be discarded. Manual intervention will be required to identify and repeat the
+ lost business transactions.</para>
+ <para>The item numbers in this list apply to the numbered boxes in the figure below.</para>
+ <orderedlist>
+ <listitem>
+ <para>System operating normally</para>
+ </listitem>
+ <listitem>
+ <para>Network suffers a failure. Master realises that replica longer in contact but as
+ <varname>designatedPrimary</varname> is true, master continues processing solo. Client
+ connections are uninterrupted by the network partition.</para>
+ <para>A third-party <emphasis role="bold">erroneously</emphasis> designates the replica as primary while the
+ original master continues running (now solo).</para>
+ </listitem>
+ <listitem>
+ <para>As the nodes cannot see one and other, both behave as masters. Clients may perform work against
+ both master nodes.</para>
+ </listitem>
+ </orderedlist>
+ <figure>
+ <title>Split Brain</title>
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="images/HA-2N-SplitBrain.png" format="PNG" scalefit="1"/>
+ </imageobject>
+ <textobject>
+ <phrase>Split Brain</phrase>
+ </textobject>
+ </mediaobject>
+ </figure>
+ </section>
+ </section>
+ </section>
+ <section role="h3" id="HAMultiNodeCluster">
+ <title>Multi Node Cluster</title>
+ <para>Multi node clusters, that is clusters where the number of nodes is three or more, are not yet
+ ready for use.</para>
+ </section>
+ <section role="h3" id="HAConfiguration">
+ <title>Configuring a Virtual Host to be a node</title>
+ <para>To configure a virtualhost as a cluster node, configure the virtualhost.xml in the following manner:</para>
+ <para>
+ <programlisting language="xml"><![CDATA[
+<virtualhost>
+ <name>myhost</name>
+ <myvhost>
+ <store>
+ <class>org.apache.qpid.server.store.berkeleydb.BDBHAMessageStore</class>
+ <environment-path>${work}/bdbhastore</environment-path>
+ <highAvailability>
+ <groupName>myclustername</groupName>
+ <nodeName>mynode1</nodeName>
+ <nodeHostPort>node1host:port</nodeHostPort>
+ <helperHostPort>node1host:port</helperHostPort>
+ <durability>NO_SYNC\,NO_SYNC\,SIMPLE_MAJORITY</durability>
+ <coalescingSync>true|false</coalescingSync>
+ <designatedPrimary>true|false</designatedPrimary>
+ </highAvailability>
+ </store>
+ ...
+ </myvhost>
+</virtualhost>]]></programlisting>
+ </para>
+ <para>The <varname>groupName</varname> is the name of logical name of the cluster. All nodes within the
+ cluster must use the same <varname>groupName</varname> in order to be considered part of the cluster.</para>
+ <para>The <varname>nodeName</varname> is the logical name of the node. All nodes within the cluster must have a
+ unique name. It is recommended that the node name should be chosen from a different nomenclature from that of
+ the servers on which they are hosted, in case the need arises to move node to a new server in the future.</para>
+ <para>The <varname>nodeHostPort</varname> is the hostname and port number used by this node to communicate with the
+ the other nodes in the cluster. For the hostname, an IP address, hostname or fully qualified hostname may be used.
+ For the port number, any free port can be used. It is important that this address is stable over time, as BDB
+ records and uses this address internally.</para>
+ <para>The <varname>helperHostPort</varname> is the hostname and port number that new nodes use to discover other
+ nodes within the cluster when they are newly introduced to the cluster. When configuring the first node, set the
+ <varname>helperHostPort</varname> to its own <varname>nodeHostPort</varname>. For the second and subsequent nodes,
+ set their <varname>helperHostPort</varname> to that of the first node.</para>
+ <para><varname>durability</varname> controls the <link linkend="HADurabilityGuarantee">durability</link>
+ guarantees made by the cluster. It is important that all nodes use the same value for this property. The default value is
+ NO_SYNC\,NO_SYNC\,SIMPLE_MAJORITY. Owing to the internal use of Apache Commons Config, it is currently necessary
+ to escape the commas within the durability string.</para>
+ <para><varname>coalescingSync</varname> controls the <link linkend="HADurabilityGuarantee_CoalescingSync">coalescing-sync</link>
+ mode of Qpid. It is important that all nodes use the same value. If omitted, it defaults to true.</para>
+ <para>The <varname>designatedPrimary</varname> is applicable only to the <link linkend="HATwoNodeCluster">two-node
+ case.</link> It governs the behaviour of a node when the other node fails or becomes uncontactable. If true,
+ the node will be designated as primary at startup and will be able to continue operating as a single node master.
+ If false, the node will transition to an unavailable state until a third-party manually designates the node as
+ primary or the other node is restored. It is suggested that the node that normally fulfils the role of master is
+ set true in config file and the node that is normally replica is set false. Be aware that setting both nodes to
+ true will lead to a failure to start up, as both cannot be designated at the point of contact. Designating both
+ nodes as primary at runtime (using the JMX interface) will lead to a <link linkend="HATwoNodeSplitBrain">split-brain</link>
+ in the case of network partition and must be avoided.</para>
+ <note><para>Usage of domain names in <varname>helperHostPort</varname> and <varname>nodeHostPort</varname> is more preferebale
+ over IP addresses due to the tendency of more frequent changes of the last over the former.
+ If server IP address changes but domain name remains the same the HA cluster can continue working as normal
+ in case when domain names are used in cluster configuration. In case when IP addresses are used and they are changed with the time
+ than Qpid <link linkend="HAJMXAPI">JMX API for HA</link> can be used to change the addresses or remove the nodes from the cluster.</para></note>
+
+ <section role="h4" id="HAConfiguration_BDBEnvVars">
+ <title>Passing BDB environment and replication configuration options</title>
+ <para>It is possible to pass BDB <ulink url="&oracleBdbJavaDocUrl;com/sleepycat/je/EnvironmentConfig.html">
+ environment</ulink> and <ulink url="&oracleBdbJavaDocUrl;com/sleepycat/je/rep/ReplicationConfig.html">
+ replication</ulink> configuration options from the virtualhost.xml. Environment configuration options are passed using
+ the <varname>envConfig</varname> element, and replication config using <varname>repConfig</varname>.</para>
+ <para>For example, to override the BDB environment configuration options <varname>je.cleaner.threads</varname> and
+ <varname>je.txn.timeout</varname></para>
+ <programlisting language="xml"><![CDATA[
+ ...
+ </highAvailability>
+ <envConfig>
+ <name>je.cleaner.threads</name>
+ <value>2</value>
+ </envConfig>
+ <envConfig>
+ <name>je.txn.timeout</name>
+ <value>15 min</value>
+ </envConfig>
+ ...
+ </store>]]></programlisting>
+ <para>And to override the BDB replication configuration options <varname>je.rep.insufficientReplicasTimeout</varname>.</para>
+ <programlisting language="xml"><![CDATA[
+ ...
+ </highAvailability>
+ ...
+ <repConfig>
+ <name>je.rep.insufficientReplicasTimeout</name>
+ <value>2</value>
+ </envConfig>
+ <envConfig>
+ <name>je.txn.timeout</name>
+ <value>10 s</value>
+ </envConfig>
+ ...
+ </store>]]></programlisting>
+ </section>
+ </section>
+ <section role="h3" id="HADurabilityGuarantee">
+ <title>Durability Guarantees</title>
+ <para>The term <ulink url="http://en.wikipedia.org/wiki/ACID#Durability">durability</ulink> is used to mean that once a
+ transaction is committed, it remains committed regardless of subsequent failures. A highly durable system is one where
+ loss of a committed transaction is extermely unlikely, whereas with a less durable system loss of a transaction is likely
+ in a greater number of scenarios. Typically, the more highly durable a system the slower and more costly it will be.</para>
+ <para>Qpid exposes the all the
+ <ulink url="&oracleBdbRepGuideUrl;txn-management.html#durabilitycontrols">durability controls</ulink>
+ offered by by BDB JE JA and a Qpid specific optimisation called <emphasis role="bold">coalescing-sync</emphasis> which defaults
+ to enabled.</para>
+ <section role="h4" id="HADurabilityGuarantee_BDBControls">
+ <title>BDB Durability Controls</title>
+ <para>BDB expresses durability as a triplet with the following form:</para>
+ <programlisting><![CDATA[<master sync policy>,<replica sync policy>,<replica acknowledgement policy>]]></programlisting>
+ <para>The sync polices controls whether the thread performing the committing thread awaits the successful completion of the
+ write, or the write and sync before continuing. The master sync policy and replica sync policy need not be the same.</para>
+ <para>For master and replic sync policies, the available values are:
+ <ulink url="&oracleBdbJavaDocUrl;com/sleepycat/je/Durability.SyncPolicy.html#SYNC">SYNC</ulink>,
+ <ulink url="&oracleBdbJavaDocUrl;com/sleepycat/je/Durability.SyncPolicy.html#WRITE_NO_SYNC">WRITE_NO_SYNC</ulink>,
+ <ulink url="&oracleBdbJavaDocUrl;com/sleepycat/je/Durability.SyncPolicy.html#NO_SYNC">NO_SYNC</ulink>. SYNC
+ is offers the highest durability whereas NO_SYNC the lowest.</para>
+ <para>Note: the combination of a master sync policy of SYNC and <link linkend="HADurabilityGuarantee_CoalescingSync">coalescing-sync</link>
+ true would result in poor performance with no corresponding increase in durability guarantee. It cannot not be used.</para>
+ <para>The acknowledgement policy defines whether when a master commits a transaction, it also awaits for the replica(s) to
+ commit the same transaction before continuing. For the two-node case, ALL and SIMPLE_MAJORITY are equal.</para>
+ <para>For acknowledgement policy, the available value are:
+ <ulink url="&oracleBdbJavaDocUrl;com/sleepycat/je/Durability.ReplicaAckPolicy.html#ALL">ALL</ulink>,
+ <ulink url="&oracleBdbJavaDocUrl;com/sleepycat/je/Durability.ReplicaAckPolicy.html#SIMPLE_MAJORITY">SIMPLE_MAJORITY</ulink>
+ <ulink url="&oracleBdbJavaDocUrl;com/sleepycat/je/Durability.ReplicaAckPolicy.html#NONE">NONE</ulink>.</para>
+ </section>
+ <section role="h4" id="HADurabilityGuarantee_CoalescingSync">
+ <title>Coalescing-sync</title>
+ <para>If enabled (the default) Qpid works to reduce the number of separate
+ <ulink url="&oracleJdkDocUrl;java/io/FileDescriptor.html#sync()">file-system sync</ulink> operations
+ performed by the <emphasis role="bold">master</emphasis> on the underlying storage device thus improving performance. It does
+ this coalescing separate sync operations arising from the different client commits operations occuring at approximately the same time.
+ It does this in such a manner not to reduce the ACID guarantees of the system.</para>
+ <para>Coalescing-sync has no effect on the behaviour of the replicas.</para>
+ </section>
+ <section role="h4" id="HADurabilityGuarantee_Default">
+ <title>Default</title>
+ <para>The default durability guarantee is <constant>NO_SYNC, NO_SYNC, SIMPLE_MAJORITY</constant> with coalescing-sync enabled. The effect
+ of this combination is described in the table below. It offers a good compromise between durability guarantee and performance
+ with writes being guaranteed on the master and the additional guarantee that a majority of replicas have received the
+ transaction.</para>
+ </section>
+ <section role="h4" id="HADurabilityGuarantee_Examples">
+ <title>Examples</title>
+ <para>Here are some examples illustrating the effects of the durability and coalescing-sync settings.</para>
+ <para>
+ <table>
+ <title>Effect of different durability guarantees</title>
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry/>
+ <entry>Durability</entry>
+ <entry>Coalescing-sync</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>1</entry>
+ <entry>NO_SYNC, NO_SYNC, SIMPLE_MAJORITY</entry>
+ <entry>true</entry>
+ <entry>Before the commit returns to the client, the transaction will be written/sync'd to the Master's disk (effect of
+ coalescing-sync) and a majority of the replica(s) will have acknowledged the <emphasis role="bold">receipt</emphasis>
+ of the transaction. The replicas will write and sync the transaction to their disk at a point in the future governed by
+ <ulink url="&oracleBdbJavaDocUrl;com/sleepycat/je/rep/ReplicationMutableConfig.html#LOG_FLUSH_TASK_INTERVAL">ReplicationMutableConfig#LOG_FLUSH_INTERVAL</ulink>.
+ </entry>
+ </row>
+ <row>
+ <entry>2</entry>
+ <entry>NO_SYNC, WRITE_NO_SYNC, SIMPLE_MAJORITY</entry>
+ <entry>true</entry>
+ <entry>Before the commit returns to the client, the transaction will be written/sync'd to the Master's disk (effect of
+ coalescing-sync and a majority of the replica(s) will have acknowledged the <emphasis role="bold">write</emphasis> of
+ the transaction to their disk. The replicas will sync the transaction to disk at a point in the future with an upper bound governed by
+ ReplicationMutableConfig#LOG_FLUSH_INTERVAL.</entry>
+ </row>
+ <row>
+ <entry>3</entry>
+ <entry>NO_SYNC, NO_SYNC, NONE</entry>
+ <entry>false</entry>
+ <entry>After the commit returns to the client, the transaction is neither guaranteed to be written to the disk of the master
+ nor received by any of the replicas. The master and replicas will write and sync the transaction to their disk at a point
+ in the future with an upper bound governed by ReplicationMutableConfig#LOG_FLUSH_INTERVAL. This offers the weakest durability guarantee.</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </para>
+ </section>
+ </section>
+ <section id="HAClientFailover">
+ <title>Client failover configuration</title>
+ <para>The details about format of Qpid connection URLs can be found at section
+ <ulink url="../../Programming-In-Apache-Qpid/html/QpidJNDI.html">Connection URLs</ulink>
+ of book <ulink url="../../Programming-In-Apache-Qpid/html/">Programming In Apache Qpid</ulink>.</para>
+ <para>The failover policy option in the connection URL for the HA Cluster should be set to <emphasis>roundrobin</emphasis>.
+ The Master broker should be put into a first place in <emphasis>brokerlist</emphasis> URL option.
+ The recommended value for <emphasis>connectdelay</emphasis> option in broker URL should be set to
+ the value greater than 1000 milliseconds. If it is desired that clients re-connect automatically after a
+ master to replica failure, <varname>cyclecount</varname> should be tuned so that the retry period is longer than
+ the expected length of time to perform the failover.</para>
+ <example><title>Example of connection URL for the HA Cluster</title><![CDATA[
+amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672?connectdelay='2000'&retries='3';tcp://localhost:5671?connectdelay='2000'&retries='3';tcp://localhost:5673?connectdelay='2000'&retries='3''&failover='roundrobin?cyclecount='30''
+ ]]></example>
+ </section>
+ <section role="h3" id="HAJMXAPI">
+ <title>Qpid JMX API for HA</title>
+ <para>Qpid exposes the BDB HA store information via its JMX interface and provides APIs to remove a Node from
+ the group, update a Node IP address, and assign a Node as the designated primary.</para>
+ <para>An instance of the <classname>BDBHAMessageStore</classname> MBean is instantiated by the broker for the each virtualhost using the HA store.</para>
+ <para>The reference to this MBean can be obtained via JMX API using an ObjectName like <emphasis>org.apache.qpid:type=BDBHAMessageStore,name=&lt;virtualhost name&gt;</emphasis>
+ where &lt;virtualhost name&gt; is the name of a specific virtualhost on the broker.</para>
+ <table border="1">
+ <title>Mbean <classname>BDBHAMessageStore</classname> attributes</title>
+ <thead>
+ <tr>
+ <td>Name</td>
+ <td>Type</td>
+ <td>Accessibility</td>
+ <td>Description</td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>GroupName</td>
+ <td>String</td>
+ <td>Read only</td>
+ <td>Name identifying the group</td>
+ </tr>
+ <tr>
+ <td>NodeName</td>
+ <td>String</td>
+ <td>Read only</td>
+ <td>Unique name identifying the node within the group</td>
+ </tr>
+ <tr>
+ <td>NodeHostPort</td>
+ <td>String</td>
+ <td>Read only</td>
+ <td>Host/port used to replicate data between this node and others in the group</td>
+ </tr>
+ <tr>
+ <td>HelperHostPort</td>
+ <td>String</td>
+ <td>Read only</td>
+ <td>Host/port used to allow a new node to discover other group members</td>
+ </tr>
+ <tr>
+ <td>NodeState</td>
+ <td>String</td>
+ <td>Read only</td>
+ <td>Current state of the node</td>
+ </tr>
+ <tr>
+ <td>ReplicationPolicy</td>
+ <td>String</td>
+ <td>Read only</td>
+ <td>Node replication durability</td>
+ </tr>
+ <tr id="JMXDesignatedPrimary">
+ <td>DesignatedPrimary</td>
+ <td>boolean</td>
+ <td>Read/Write</td>
+ <td>Designated primary flag. Applicable to the two node case.</td>
+ </tr>
+ <tr>
+ <td>CoalescingSync</td>
+ <td>boolean</td>
+ <td>Read only</td>
+ <td>Coalescing sync flag. Applicable to the master sync policies NO_SYNC and WRITE_NO_SYNC only.</td>
+ </tr>
+ <tr>
+ <td>getAllNodesInGroup</td>
+ <td>TabularData</td>
+ <td>Read only</td>
+ <td>Get all nodes within the group, regardless of whether currently attached or not</td>
+ </tr>
+ </tbody>
+ </table>
+ <table border="1">
+ <title>Mbean <classname>BDBHAMessageStore</classname> operations</title>
+ <thead>
+ <tr>
+ <td>Operation</td>
+ <td>Parameters</td>
+ <td>Returns</td>
+ <td>Description</td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>removeNodeFromGroup</td>
+ <td>
+ <para><emphasis>nodeName</emphasis>, name of node, string</para>
+ </td>
+ <td>void</td>
+ <td>Remove an existing node from the group</td>
+ </tr>
+ <tr>
+ <td>updateAddress</td>
+ <td>
+ <itemizedlist>
+ <itemizedlist>
+ <para><emphasis>nodeName</emphasis>, name of node, string</para>
+ </itemizedlist>
+ <itemizedlist>
+ <para><emphasis>newHostName</emphasis>, new host name, string</para>
+ </itemizedlist>
+ <itemizedlist>
+ <para><emphasis>newPort</emphasis>, new port number, int</para>
+ </itemizedlist>
+ </itemizedlist>
+ </td>
+ <td>void</td>
+ <td>Update the address of another node. The node must be in a STOPPED state.</td>
+ </tr>
+ </tbody>
+ </table>
+ <figure>
+ <title>BDBHAMessageStore view from jconsole.</title>
+ <graphic fileref="images/HA-BDBHAMessageStore-MBean-jconsole.png"/>
+ </figure>
+ <example>
+ <title>Example of java code to get the node state value</title>
+ <programlisting language="java"><![CDATA[
+Map<String, Object> environment = new HashMap<String, Object>();
+
+// credentials: user name and password
+environment.put(JMXConnector.CREDENTIALS, new String[] {"admin","admin"});
+JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:9001/jmxrmi");
+JMXConnector jmxConnector = JMXConnectorFactory.connect(url, environment);
+MBeanServerConnection mbsc = jmxConnector.getMBeanServerConnection();
+
+ObjectName queueObjectName = new ObjectName("org.apache.qpid:type=BDBHAMessageStore,name=test");
+String state = (String)mbsc.getAttribute(queueObjectName, "NodeState");
+
+System.out.println("Node state:" + state);
+ ]]></programlisting>
+ <para>Example system output:</para>
+ <screen><![CDATA[Node state:MASTER]]></screen>
+ </example>
+ </section>
+ <section id="BDB-HA-Monitoring-cluster">
+ <title>Monitoring cluster</title>
+ <para>In order to discover potential issues with HA Cluster early, all nodes in the Cluster should be monitored on regular basis
+ using the following techniques:</para>
+ <itemizedlist>
+ <listitem>
+ <para>Broker log files scrapping for WARN or ERROR entries and operational log entries like:</para>
+ <itemizedlist>
+ <listitem>
+ <para><emphasis>MST-1007 :</emphasis> Store Passivated. It can indicate that Master virtual host has gone down.</para>
+ </listitem>
+ <listitem>
+ <para><emphasis>MST-1006 :</emphasis> Recovery Complete. It can indicate that a former Replica virtual host is up and became the Master.</para>
+ </listitem>
+ </itemizedlist>
+ </listitem>
+ <listitem>
+ <para>Disk space usage and system load using system tools.</para>
+ </listitem>
+ <listitem>
+ <para>Berkeley HA node status using <ulink url="&oracleBdbJavaDocUrl;com/sleepycat/je/rep/util/DbPing.html"><classname>DbPing</classname></ulink> utility.</para>
+ <example><title>Using <classname>DbPing</classname> utility for monitoring HA nodes.</title><command>
+java -jar je-&oracleBdbProductVersion;.jar DbPing -groupName TestClusterGroup -nodeName Node-5001 -nodeHost localhost:5001 -socketTimeout 10000
+</command><screen>
+Current state of node: Node-5001 from group: TestClusterGroup
+ Current state: MASTER
+ Current master: Node-5001
+ Current JE version: &oracleBdbProductVersion;
+ Current log version: 8
+ Current transaction end (abort or commit) VLSN: 165
+ Current master transaction end (abort or commit) VLSN: 0
+ Current active feeders on node: 0
+ Current system load average: 0.35
+</screen></example>
+ <para>In the example above <classname>DbPing</classname> utility requested status of Cluster node with name
+ <emphasis>Node-5001</emphasis> from replication group <emphasis>TestClusterGroup</emphasis> running on host <emphasis>localhost:5001</emphasis>.
+ The state of the node was reported into a system output.
+ </para>
+ </listitem>
+ <listitem>
+ <para>Using Qpid broker JMX interfaces.</para>
+ <para>Mbean <classname>BDBHAMessageStore</classname> can be used to request the following node information:</para>
+ <itemizedlist>
+ <listitem>
+ <para><emphasis>NodeState</emphasis> indicates whether node is a Master or Replica.</para>
+ </listitem>
+ <listitem>
+ <para><emphasis>Durability</emphasis> replication durability.</para>
+ </listitem>
+ <listitem>
+ <para><emphasis>DesignatedPrimary</emphasis> indicates whether Master node is designated primary.</para>
+ </listitem>
+ <listitem>
+ <para><emphasis>GroupName</emphasis> replication group name.</para>
+ </listitem>
+ <listitem>
+ <para><emphasis>NodeName</emphasis> node name.</para>
+ </listitem>
+ <listitem>
+ <para><emphasis>NodeHostPort</emphasis> node host and port.</para>
+ </listitem>
+ <listitem>
+ <para><emphasis>HelperHostPort</emphasis> helper host and port.</para>
+ </listitem>
+ <listitem>
+ <para><emphasis>AllNodesInGroup</emphasis> lists of all nodes in the replication group including their names, hosts and ports.</para>
+ </listitem>
+ </itemizedlist>
+ <para>For more details about <classname>BDBHAMessageStore</classname> MBean please refer section <link linkend="HAJMXAPI">Qpid JMX API for HA</link></para>
+ </listitem>
+ </itemizedlist>
+ </section>
+ <section id="HADiskSpace">
+ <title>Disk space requirements</title>
+ <para>Disk space is a critical resource for the HA Qpid broker.</para>
+ <para>In case when a Replica goes down (or falls behind the Master in 2 node cluster where the Master is designated primary)
+ and the Master continues running, the non-replicated store files are kept on the Masters disk for the period of time
+ as specified in <emphasis>je.rep.repStreamTimeout</emphasis> JE setting in order to replicate this data later
+ when the Replica is back. This setting is set to 1 hour by default by the broker. The setting can be overridden as described in
+ <xref linkend="HAConfiguration_BDBEnvVars"/>.</para>
+ <para>Depending from the application publishing/consuming rates and message sizes,
+ the disk space might become overfull during this period of time due to preserved logs.
+ Please, make sure to allocate enough space on your disk to avoid this from happening.
+ </para>
+ </section>
+ <section id="BDB-HA-Network-Requirements">
+ <title>Network Requirements</title>
+ <para>The HA Cluster performance depends on the network bandwidth, its use by existing traffic, and quality of service.</para>
+ <para>In order to achieve the best performance it is recommended to use a separate network infrastructure for the Qpid HA Nodes
+ which might include installation of dedicated network hardware on Broker hosts, assigning a higher priority to replication ports,
+ installing a cluster in a separate network not impacted by any other traffic.</para>
+ </section>
+ <section id="BDB-HA-Security">
+ <title>Security</title>
+ <para>At the moment Berkeley replication API supports only TCP/IP protocol to transfer replication data between Master and Replicas.</para>
+ <para>As result, the replicated data is unprotected and can be intercepted by anyone having access to the replication network.</para>
+ <para>Also, anyone who can access to this network can introduce a new node and therefore receive a copy of the data.</para>
+ <para>In order to reduce the security risks the entire HA cluster is recommended to run in a separate network protected from general access.</para>
+ </section>
+ <section id="BDB-HA-Backup">
+ <title>Backups</title>
+ <para>In order to protect the entire cluster from some cataclysms which might destroy all cluster nodes,
+ backups of the Master store should be taken on a regular basis.</para>
+ <para>Qpid Broker distribution includes the "hot" backup utility <emphasis>backup.sh</emphasis> which can be found at broker bin folder.
+ This utility can perform the backup when broker is running.</para>
+ <para><emphasis>backup.sh</emphasis> script invokes <classname>org.apache.qpid.server.store.berkeleydb.BDBBackup</classname> to do the job.</para>
+ <para>You can also run this class from command line like in an example below:</para>
+ <example><title>Performing store backup by using <classname>BDBBackup</classname> class directly</title><command>
+ java -cp qpid-bdbstore-0.18.jar org.apache.qpid.server.store.berkeleydb.BDBBackup -fromdir path/to/store/folder -todir path/to/backup/foldeAr</command>
+ </example>
+ <para>In the example above BDBBackup utility is called from qpid-bdbstore-0.18.jar to backup the store at <emphasis>path/to/store/folder</emphasis> and copy store logs into <emphasis>path/to/backup/folder</emphasis>.</para>
+ <para>Linux and Unix users can take advantage of <emphasis>backup.sh</emphasis> bash script by running this script in a similar way.</para>
+ <example><title>Performing store backup by using <classname>backup.sh</classname> bash script</title>
+ <command>backup.sh -fromdir path/to/store/folder -todir path/to/backup/folder</command>
+ </example>
+ <note>
+ <para>Do not forget to ensure that the Master store is being backed up, in the event the Node elected Master changes during
+ the lifecycle of the cluster.</para>
+ </note>
+ </section>
+ <section id="HAMigrationFromNonHA">
+ <title>Migration of a non-HA store to HA</title>
+ <para>Non HA stores starting from schema version 4 (0.14 Qpid release) can be automatically converted into HA store on broker startup if replication is first enabled with the <ulink url="&oracleBdbJavaDocUrl;com/sleepycat/je/rep/util/DbEnableReplication.html"><classname>DbEnableReplication</classname></ulink> utility from the BDB JE jar.</para>
+ <para>DbEnableReplication converts a non HA store into an HA store and can be used as follows:</para>
+ <example><title>Enabling replication</title><command>
+java -jar je-&oracleBdbProductVersion;.jar DbEnableReplication -h /path/to/store -groupName MyReplicationGroup -nodeName MyNode1 -nodeHostPort localhost:5001
+ </command></example>
+ <para>In the examples above, je jar of version &oracleBdbProductVersion; is used to convert store at <emphasis>/path/to/store</emphasis> into HA store having replication group name <emphasis>MyReplicationGroup</emphasis>, node name <emphasis>MyNode1</emphasis> and running on host <emphasis>localhost</emphasis> and port <emphasis>5001</emphasis>.</para>
+ <para>After running DbEnableReplication and updating the virtual host store to configuration to be an HA message store, like in example below,
+ on broker start up the store schema will be upgraded to the most recent version and the broker can be used as normal.</para>
+ <example>
+ <title>Example of XML configuration for HA message store</title>
+ <programlisting language="xml"><![CDATA[
+<store>
+ <class>org.apache.qpid.server.store.berkeleydb.BDBHAMessageStore</class>
+ <environment-path>/path/to/store</environment-path>
+ <highAvailability>
+ <groupName>MyReplicationGroup</groupName>
+ <nodeName>MyNode1</nodeName>
+ <nodeHostPort>localhost:5001</nodeHostPort>
+ <helperHostPort>localhost:5001</helperHostPort>
+ </highAvailability>
+</store>]]></programlisting>
+ </example>
+ <para>The Replica nodes can be started with empty stores. The data will be automatically copied from Master to Replica on Replica start-up.
+ This will take a period of time determined by the size of the Masters store and the network bandwidth between the nodes.</para>
+ <note>
+ <para>Due to existing caveats in Berkeley JE with copying of data from Master into Replica it is recommended to restart the Master node after store schema upgrade is finished before starting the Replica nodes.</para>
+ </note>
+ </section>
+ <section id="HADisasterRecovery">
+ <title>Disaster Recovery</title>
+ <para>This section describes the steps required to restore HA broker cluster from backup.</para>
+ <para>The detailed instructions how to perform backup on replicated environment can be found <link linkend="BDB-HA-Backup">here</link>.</para>
+ <para>At this point we assume that backups are collected on regular basis from Master node.</para>
+ <para>Replication configuration of a cluster is stored internally in HA message store.
+ This information includes IP addresses of the nodes.
+ In case when HA message store needs to be restored on a different host with a different IP address
+ the cluster replication configuration should be reseted in this case</para>
+ <para>Oracle provides a command line utility <ulink url="&oracleBdbJavaDocUrl;com/sleepycat/je/rep/util/DbResetRepGroup.html"><classname>DbResetRepGroup</classname></ulink>
+ to reset the members of a replication group and replace the group with a new group consisting of a single new member
+ as described by the arguments supplied to the utility</para>
+ <para>Cluster can be restored with the following steps:</para>
+ <itemizedlist>
+ <listitem><para>Copy log files into the store folder from backup</para></listitem>
+ <listitem>
+ <para>Use <classname>DbResetRepGroup</classname> to reset an existing environment. See an example below</para>
+ <example>
+ <title>Reseting of replication group with <classname>DbResetRepGroup</classname></title><command>
+java -cp je-&oracleBdbProductVersion;.jar com.sleepycat.je.rep.util.DbResetRepGroup -h ha-work/Node-5001/bdbstore -groupName TestClusterGroup -nodeName Node-5001 -nodeHostPort localhost:5001</command>
+ </example>
+ <para>In the example above <classname>DbResetRepGroup</classname> utility from Berkeley JE of version &oracleBdbProductVersion; is used to reset the store
+ at location <emphasis>ha-work/Node-5001/bdbstore</emphasis> and set a replication group to <emphasis>TestClusterGroup</emphasis>
+ having a node <emphasis>Node-5001</emphasis> which runs at <emphasis>localhost:5001</emphasis>.</para>
+ </listitem>
+ <listitem><para>Start a broker with HA store configured as specified on running of <classname>DbResetRepGroup</classname> utility.</para></listitem>
+ <listitem><para>Start replica nodes having the same replication group and a helper host port pointing to a new master. The store content will be copied into Replicas from Master on their start up.</para></listitem>
+ </itemizedlist>
+ </section>
+
+ <section id="HAPerformance">
+ <title>Performance</title>
+ <para>The aim of this section is not to provide exact performance metrics relating to HA, as this depends heavily on the test
+ environment, but rather showing an impact of HA on Qpid broker performance in comparison with the Non HA case.</para>
+ <para>For testing of impact of HA on a broker performance a special test script was written using Qpid performance test framework.
+ The script opened a number of connections to the Qpid broker, created producers and consumers on separate connections,
+ and published test messages with concurrent producers into a test queue and consumed them with concurrent consumers.
+ The table below shows the number of producers/consumers used in the tests.
+ The overall throughput was collected for each configuration.
+ </para>
+ <table border="1">
+ <title>Number of producers/consumers in performance tests</title>
+ <thead>
+ <tr>
+ <th>Test</th>
+ <th>Number of producers</th>
+ <th>Number of consumers</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>1</td>
+ <td>1</td>
+ <td>1</td>
+ </tr>
+ <tr>
+ <td>2</td>
+ <td>2</td>
+ <td>2</td>
+ </tr>
+ <tr>
+ <td>3</td>
+ <td>4</td>
+ <td>4</td>
+ </tr>
+ <tr>
+ <td>4</td>
+ <td>8</td>
+ <td>8</td>
+ </tr>
+ <tr>
+ <td>5</td>
+ <td>16</td>
+ <td>16</td>
+ </tr>
+ <tr>
+ <td>6</td>
+ <td>32</td>
+ <td>32</td>
+ </tr>
+ <tr>
+ <td>7</td>
+ <td>64</td>
+ <td>64</td>
+ </tr>
+ </tbody>
+ </table>
+ <para>The test was run against the following Qpid Broker configurations</para>
+ <itemizedlist>
+ <listitem>
+ <para>Non HA Broker</para>
+ </listitem>
+ <listitem>
+ <para>HA 2 Nodes Cluster with durability <emphasis>SYNC,SYNC,ALL</emphasis></para>
+ </listitem>
+ <listitem>
+ <para>HA 2 Nodes Cluster with durability <emphasis>WRITE_NO_SYNC,WRITE_NO_SYNC,ALL</emphasis></para>
+ </listitem>
+ <listitem>
+ <para>HA 2 Nodes Cluster with durability <emphasis>WRITE_NO_SYNC,WRITE_NO_SYNC,ALL</emphasis> and <emphasis>coalescing-sync</emphasis> Qpid mode</para>
+ </listitem>
+ <listitem>
+ <para>HA 2 Nodes Cluster with durability <emphasis>WRITE_NO_SYNC,NO_SYNC,ALL</emphasis> and <emphasis>coalescing-sync</emphasis> Qpid mode</para>
+ </listitem>
+ <listitem>
+ <para>HA 2 Nodes Cluster with durability <emphasis>NO_SYNC,NO_SYNC,ALL</emphasis> and <emphasis>coalescing-sync</emphasis> Qpid option</para>
+ </listitem>
+ </itemizedlist>
+ <para>The evironment used in testing consisted of 2 servers with 4 CPU cores (2x Intel(r) Xeon(R) CPU 5150@2.66GHz), 4GB of RAM
+ and running under OS Red Hat Enterprise Linux AS release 4 (Nahant Update 4). Network bandwidth was 1Gbit.
+ </para>
+ <para>We ran Master node on the first server and Replica and clients(both consumers and producers) on the second server.</para>
+ <para>In non-HA case Qpid Broker was run on a first server and clients were run on a second server.</para>
+ <para>The table below contains the test results we measured on this environment for different Broker configurations.</para>
+ <para>Each result is represented by throughput value in KB/second and difference in % between HA configuration and non HA case for the same number of clients.</para>
+ <table border="1">
+ <title>Performance Comparison</title>
+ <thead>
+ <tr>
+ <td>Test/Broker</td>
+ <td>No HA</td>
+ <td>SYNC, SYNC, ALL</td>
+ <td>WRITE_NO_SYNC, WRITE_NO_SYNC, ALL</td>
+ <td>WRITE_NO_SYNC, WRITE_NO_SYNC, ALL - coalescing-sync</td>
+ <td>WRITE_NO_SYNC, NO_SYNC,ALL - coalescing-sync</td>
+ <td>NO_SYNC, NO_SYNC, ALL - coalescing-sync</td>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>1 (1/1)</td>
+ <td>0.0%</td>
+ <td>-61.4%</td>
+ <td>117.0%</td>
+ <td>-16.02%</td>
+ <td>-9.58%</td>
+ <td>-25.47%</td>
+ </tr>
+ <tr>
+ <td>2 (2/2)</td>
+ <td>0.0%</td>
+ <td>-75.43%</td>
+ <td>67.87%</td>
+ <td>-66.6%</td>
+ <td>-69.02%</td>
+ <td>-30.43%</td>
+ </tr>
+ <tr>
+ <td>3 (4/4)</td>
+ <td>0.0%</td>
+ <td>-84.89%</td>
+ <td>24.19%</td>
+ <td>-71.02%</td>
+ <td>-69.37%</td>
+ <td>-43.67%</td>
+ </tr>
+ <tr>
+ <td>4 (8/8)</td>
+ <td>0.0%</td>
+ <td>-91.17%</td>
+ <td>-22.97%</td>
+ <td>-82.32%</td>
+ <td>-83.42%</td>
+ <td>-55.5%</td>
+ </tr>
+ <tr>
+ <td>5 (16/16)</td>
+ <td>0.0%</td>
+ <td>-91.16%</td>
+ <td>-21.42%</td>
+ <td>-86.6%</td>
+ <td>-86.37%</td>
+ <td>-46.99%</td>
+ </tr>
+ <tr>
+ <td>6 (32/32)</td>
+ <td>0.0%</td>
+ <td>-94.83%</td>
+ <td>-51.51%</td>
+ <td>-92.15%</td>
+ <td>-92.02%</td>
+ <td>-57.59%</td>
+ </tr>
+ <tr>
+ <td>7 (64/64)</td>
+ <td>0.0%</td>
+ <td>-94.2%</td>
+ <td>-41.84%</td>
+ <td>-89.55%</td>
+ <td>-89.55%</td>
+ <td>-50.54%</td>
+ </tr>
+ </tbody>
+ </table>
+ <para>The figure below depicts the graphs for the performance test results</para>
+ <figure>
+ <title>Test results</title>
+ <graphic fileref="images/HA-perftests-results.png"/>
+ </figure>
+ <para>On using durability <emphasis>SYNC,SYNC,ALL</emphasis> (without coalescing-sync) the performance drops significantly (by 62-95%) in comparison with non HA broker.</para>
+ <para>Whilst, on using durability <emphasis>WRITE_NO_SYNC,WRITE_NO_SYNC,ALL</emphasis> (without coalescing-sync) the performance drops by only half, but with loss of durability guarantee, so is not recommended.</para>
+ <para>In order to have better performance with HA, Qpid Broker comes up with the special mode called <link linkend="HADurabilityGuarantee_CoalescingSync">coalescing-sync</link>,
+ With this mode enabled, Qpid broker batches the concurrent transaction commits and syncs transaction data into Master disk in one go.
+ As result, the HA performance only drops by 25-60% for durability <emphasis>NO_SYNC,NO_SYNC,ALL</emphasis> and by 10-90% for <emphasis>WRITE_NO_SYNC,WRITE_NO_SYNC,ALL</emphasis>.</para>
+ </section>
+</section>
diff --git a/qpid/doc/book/src/How-to-Tune-M3-Java-Broker-Performance.xml b/qpid/doc/book/src/java-broker/How-to-Tune-M3-Java-Broker-Performance.xml
index f7fffbaceb..f7fffbaceb 100644
--- a/qpid/doc/book/src/How-to-Tune-M3-Java-Broker-Performance.xml
+++ b/qpid/doc/book/src/java-broker/How-to-Tune-M3-Java-Broker-Performance.xml
diff --git a/qpid/doc/book/src/How-to-Use-SlowConsumerDisconnect.xml b/qpid/doc/book/src/java-broker/How-to-Use-SlowConsumerDisconnect.xml
index 4e0ce0f7e0..4e0ce0f7e0 100644
--- a/qpid/doc/book/src/How-to-Use-SlowConsumerDisconnect.xml
+++ b/qpid/doc/book/src/java-broker/How-to-Use-SlowConsumerDisconnect.xml
diff --git a/qpid/doc/book/src/Java-Broker-Feature-Guide.xml b/qpid/doc/book/src/java-broker/Java-Broker-Feature-Guide.xml
index bbc2a1aaf0..bbc2a1aaf0 100644
--- a/qpid/doc/book/src/Java-Broker-Feature-Guide.xml
+++ b/qpid/doc/book/src/java-broker/Java-Broker-Feature-Guide.xml
diff --git a/qpid/doc/book/src/Java-Environment-Variables.xml b/qpid/doc/book/src/java-broker/Java-Environment-Variables.xml
index 12703190f2..12703190f2 100644
--- a/qpid/doc/book/src/Java-Environment-Variables.xml
+++ b/qpid/doc/book/src/java-broker/Java-Environment-Variables.xml
diff --git a/qpid/doc/book/src/java-broker/Makefile b/qpid/doc/book/src/java-broker/Makefile
new file mode 100644
index 0000000000..0266a0f54d
--- /dev/null
+++ b/qpid/doc/book/src/java-broker/Makefile
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+include ../Makefile.inc
diff --git a/qpid/doc/book/src/Management-Console-Security.xml b/qpid/doc/book/src/java-broker/Management-Console-Security.xml
index 31f63c70da..31f63c70da 100644
--- a/qpid/doc/book/src/Management-Console-Security.xml
+++ b/qpid/doc/book/src/java-broker/Management-Console-Security.xml
diff --git a/qpid/doc/book/src/MessageStore-Tool.xml b/qpid/doc/book/src/java-broker/MessageStore-Tool.xml
index fdcb3cd560..fdcb3cd560 100644
--- a/qpid/doc/book/src/MessageStore-Tool.xml
+++ b/qpid/doc/book/src/java-broker/MessageStore-Tool.xml
diff --git a/qpid/doc/book/src/java-broker/Producer-Flow-Control.xml b/qpid/doc/book/src/java-broker/Producer-Flow-Control.xml
new file mode 100644
index 0000000000..262279510e
--- /dev/null
+++ b/qpid/doc/book/src/java-broker/Producer-Flow-Control.xml
@@ -0,0 +1,217 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ 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.
+
+-->
+
+<section id="Qpid-Producer-Flow-Control">
+ <title>Producer Flow Control</title>
+
+ <section role="h2" id="QpidProducerFlowControlGeneralInformation">
+ <title>General Information</title>
+ <para>
+ The Qpid 0.6 release introduced a simplistic producer-side flow control mechanism
+ into the Java Messaging Broker, causing producers to be flow-controlled when they
+ attempt to send messages to an overfull queue. Qpid 0.18 introduced a similar
+ mechanism triggered by an overfull persistent message store on a virtual host.
+ </para>
+ </section>
+ <section role="h2" id="QpidProducerFlowControlServerConfiguration">
+ <title>Server Configuration</title>
+ <section role="h3">
+ <title>Configuring a Queue to use flow control</title>
+ <para>
+ Flow control is enabled on a producer when it sends a message to a Queue
+ which is "overfull". The producer flow control will be rescinded when all
+ Queues on which a producer is blocking become "underfull". A Queue is defined
+ as overfull when the size (in bytes) of the messages on the queue exceeds the
+ "capacity" of the Queue. A Queue becomes "underfull" when its size becomes
+ less than the "flowResumeCapacity".
+
+ <programlisting>
+ <![CDATA[
+<queue>
+ <name>test</name>
+ <test>
+ <exchange>amq.direct</exchange>
+ <capacity>10485760</capacity> <!-- set the queue capacity to 10Mb -->
+ <flowResumeCapacity>8388608</flowResumeCapacity> <!-- set the resume capacity to 8Mb -->
+ </test>
+</queue>
+ ]]>
+ </programlisting>
+
+ The default for all queues on a virtual host can also be set
+
+ <programlisting>
+ <![CDATA[
+<virtualhosts>
+ <virtualhost>
+ <name>localhost</name>
+ <localhost>
+ <capacity>10485760</capacity> <!-- set the queue capacity to 10Mb -->
+ <flowResumeCapacity>8388608</flowResumeCapacity> <!-- set the resume capacity to 8Mb -->
+ </localhost>
+ </virtualhost>
+</virtualhosts>
+ ]]>
+ </programlisting>
+
+ Where no flowResumeCapacity is set, the flowResumeCapacity is set to be equal
+ to the capacity. Where no capacity is set, capacity is defaulted to 0 meaning
+ there is no capacity limit.
+ </para>
+ <section role="h4">
+ <title>Broker Log Messages</title>
+ <para>
+ There are four new Broker log messages that may occur if flow control through queue capacity limits is enabled.
+ Firstly, when a capacity limited queue becomes overfull, a log message similar to the following is produced
+ </para>
+ <programlisting>
+MESSAGE [vh(/test)/qu(MyQueue)] [vh(/test)/qu(MyQueue)] QUE-1003 : Overfull : Size : 1,200 bytes, Capacity : 1,000
+ </programlisting>
+ <para>Then for each channel which becomes blocked upon the overful queue a log message similar to the following is produced:</para>
+ <programlisting>
+MESSAGE [con:2(guest@anonymous(713889609)/test)/ch:1] [con:2(guest@anonymous(713889609)/test)/ch:1] CHN-1005 : Flow Control Enforced (Queue MyQueue)
+ </programlisting>
+ <para>When enough messages have been consumed from the queue that it becomes underfull, then the following log is generated: </para>
+ <programlisting>
+MESSAGE [vh(/test)/qu(MyQueue)] [vh(/test)/qu(MyQueue)] QUE-1004 : Underfull : Size : 600 bytes, Resume Capacity : 800
+ </programlisting>
+ <para>And for every channel which becomes unblocked you will see a message similar to: </para>
+ <programlisting>
+MESSAGE [con:2(guest@anonymous(713889609)/test)/ch:1] [con:2(guest@anonymous(713889609)/test)/ch:1] CHN-1006 : Flow Control Removed
+ </programlisting>
+ <para>Obviously the details of connection, virtual host, queue, size, capacity, etc would depend on the configuration in use.</para>
+
+
+ </section><!-- Broker Log Messages -->
+ </section><!-- Configuring a Queue to use flow control -->
+
+ <section role="h3">
+ <title>Disk quota-based flow control</title>
+ <para>
+ Since version 0.18 of Qpid Broker, flow control can be triggered when a
+ configured disk quota is exceeded. This is supported by the BDB and Derby message stores.
+ </para>
+ <para>
+ This functionality blocks all producers on reaching the disk overflow limit. When consumers
+ consume the messages, causing disk space usage to falls below the underflow limit, the
+ producers are unblocked and continue working as normal.
+ </para>
+ <para>
+ Two limits can be configured:
+ </para>
+ <para>
+ overfull limit - the maximum space on disk (in bytes) which can be used by store.
+ </para>
+ <para>
+ underfull limit - when the space on disk drops below this limit, producers are allowed to resume publishing.
+ </para>
+ <para>
+ An example of quota configuration for the BDB message store is provided below.
+ </para>
+ <programlisting>
+ <![CDATA[
+<store>
+ <class>org.apache.qpid.server.store.berkeleydb.BDBMessageStore</class>
+ <environment-path>${work}/bdbstore/test</environment-path>
+ <overfull-size>50000000</overfull-size>
+ <underfull-size>45000000</underfull-size>
+</store>
+ ]]>
+ </programlisting>
+ <para>
+ The disk quota functionality is based on "best effort" principle. This means the broker
+ cannot guarantee that the disk space limit will not be exceeded. If several concurrent
+ transactions are started before the limit is reached, which collectively cause the limit
+ to be exceeded, the broker may allow all of them to be committed.
+ </para>
+
+ <section role="h4">
+ <title>Broker Log Messages for quota flow control</title>
+ <para>
+ There are 2 new broker log messages that may occur if flow control through disk quota limits is enabled.
+ When the virtual host is blocked due to exceeding of the disk quota limit the following message
+ appears in the broker log
+ <programlisting>
+[vh(/test)/ms(BDBMessageStore)] MST-1008 : Store overfull, flow control will be enforced
+ </programlisting>
+ When virtual host is unblocked after cleaning the disk space the following message appears in the broker log
+ <programlisting>
+[vh(/test)/ms(BDBMessageStore)] MST-1009 : Store overfull condition cleared
+ </programlisting>
+ </para>
+ </section>
+ </section><!-- Disk quota-based flow control -->
+ </section><!-- Server configuration -->
+
+
+ <section role="h2" id="QpidProducerFlowControlClientImpact">
+ <title>Client impact and configuration</title>
+ <para>
+ If a producer sends to a queue which is overfull, the broker will respond by
+ instructing the client not to send any more messages. The impact of this is
+ that any future attempts to send will block until the broker rescinds the flow control order.
+ </para>
+ <para>
+ While blocking the client will periodically log the fact that it is blocked waiting on flow control.
+ </para>
+ <programlisting>
+WARN Message send delayed by 5s due to broker enforced flow control
+WARN Message send delayed by 10s due to broker enforced flow control
+ </programlisting>
+ <para>
+ After a set period the send will timeout and throw a JMSException to the calling code.
+ </para>
+ <para>
+ If such a JMSException is thrown, the message will not be sent to the broker,
+ however the underlying Session may still be active - in particular if the
+ Session is transactional then the current transaction will not be automatically
+ rolled back. Users may choose to either attempt to resend the message, or to
+ roll back any transactional work and close the Session.
+ </para>
+ <para>
+ Both the timeout delay and the periodicity of the warning messages can be set
+ using Java system properties.
+ </para>
+ <para>
+ The amount of time (in milliseconds) to wait before timing out
+ is controlled by the property qpid.flow_control_wait_failure.
+ </para>
+ <para>
+ The frequency at which the log message informing that the producer is flow
+ controlled is sent is controlled by the system property qpid.flow_control_wait_notify_period.
+ </para>
+ <para>
+ Adding the following to the command line to start the client would result in a timeout of one minute,
+ with warning messages every ten seconds:
+ </para>
+ <programlisting>
+-Dqpid.flow_control_wait_failure=60000
+-Dqpid.flow_control_wait_notify_period=10000
+ </programlisting>
+ <section role="h3">
+ <title>Older Clients</title>
+ <para>
+ The flow control feature was first added to the Java broker/client in the 0.6 release. If an older client connects to the broker then the flow control commands will be ignored by it and it will not be blocked. So to fully benefit from this feature both Client and Broker need to be at least version 0.6.
+ </para>
+ </section>
+ </section> <!-- Client impact and configuration -->
+</section>
diff --git a/qpid/doc/book/src/Qpid-JMX-Management-Console-FAQ.xml b/qpid/doc/book/src/java-broker/Qpid-JMX-Management-Console-FAQ.xml
index 1806ab01b1..1806ab01b1 100644
--- a/qpid/doc/book/src/Qpid-JMX-Management-Console-FAQ.xml
+++ b/qpid/doc/book/src/java-broker/Qpid-JMX-Management-Console-FAQ.xml
diff --git a/qpid/doc/book/src/Qpid-JMX-Management-Console-User-Guide.xml b/qpid/doc/book/src/java-broker/Qpid-JMX-Management-Console-User-Guide.xml
index 55e1f8e829..35bb5dfbe8 100644
--- a/qpid/doc/book/src/Qpid-JMX-Management-Console-User-Guide.xml
+++ b/qpid/doc/book/src/java-broker/Qpid-JMX-Management-Console-User-Guide.xml
@@ -178,7 +178,7 @@
connection tree, disconnecting the selected server connection,
and removing the server from the connection tree.
</para><para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113098.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113098.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
Beside these buttons is a combo for selecting the refresh
@@ -202,7 +202,7 @@
hostname, management port, and a username and password. An
example is shown below:
</para><para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113099.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113099.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para><para>
Once all the required details are entered, pressing Connect will
@@ -214,7 +214,7 @@
VirtualHosts present on the server, as can be seen in the figure
below.
</para><para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113100.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113100.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para><para>
If the server supports a newer management API than the console in
@@ -284,7 +284,7 @@
the left side of the application. To open a particular MBean from
the tree for viewing, simply select it in the tree and it will be
opened in the main view.
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113101.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113101.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para><para>
As there may be vast numbers of Queues, Connections, and
Exchanges on the server these MBeans are not automatically added
@@ -302,7 +302,7 @@
from the tree by right clicking on them to expose a context menu
allowing deletion.
</para><para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113102.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113102.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para><para>
As an alternative way to open a particular MBean for viewing,
without first adding it to the tree, you can simply double click
@@ -334,7 +334,7 @@
without a restart, and can be performed by clicking the Execute
button and confirming the prompt which follows.
</para><para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113103.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113103.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para>
<!--h1--></section>
@@ -362,7 +362,7 @@
<para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113104.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113104.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para><para>
The Runtime Options tab allows manipulation of the logging
settings without affecting the configuration files (this means
@@ -395,7 +395,7 @@
parent by setting it to an INHERITED level that removes any
previously set Level of its own.
</para><para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113105.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113105.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para><para>
In order to set one of more Loggers to a new Level, they should
be selected in the table (or double click an individual Logger to
@@ -436,7 +436,7 @@
Loggers and use the button to load the dialog to set the new
Level.
</para><para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113106.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113106.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para><para>
One issue to note of when reloading the configuration file
settings, either automatically using LogWatch or manually, is
@@ -469,7 +469,7 @@
<para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113107.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113107.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para><para>
The ServerInformation MBean currently only conveys various pieces
of version information to allow precise identification of the
@@ -490,13 +490,13 @@
allows manipulation of existing user accounts and creation of new
user accounts.
</para><para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113108.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113108.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para><para>
To add a new user, press the <emphasis>Add New User</emphasis> button, which
will load the dialog shown below.
</para><para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113109.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113109.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para><para>
Here you may enter the new users Username, Password, and select
their JMX Management Rights. This controls whether or not they
@@ -561,7 +561,7 @@
The console will prompt for confirmation before undertaking the
operation.
</para><para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113110.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113110.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para><para>
Clicking the <emphasis>Create</emphasis> button in the Exchange section will
open a dialog allowing specification of the Name, Type, and
@@ -602,7 +602,7 @@
MBeans in that VirtualHost. An example of this can be seen in the
figure below.
</para><para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113111.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113111.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para><para>
All received Notifications will be displayed until such time as
the user removes them, either in this aggregated view, or in the
@@ -638,7 +638,7 @@
Attributes</emphasis> button at the bottom right corner of the
table)<emphasis>.</emphasis>
</para><para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113112.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113112.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para><para>
Upon opening a Queue MBean, the Attributes tab is displayed, as
shown below. This allows viewing the value all attributes,
@@ -647,7 +647,7 @@
their purpose, and graphing certain numerical attribute values as
they change over time.
</para><para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113113.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113113.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para><para>
The next tab contains the operations that can be performed on the
queue. The main table serves as a means of viewing the messages
@@ -664,7 +664,7 @@
information to show this (unless only a single message position
is requested).
</para><para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113114.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113114.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para><para>
Upon selecting a message in the table, its header properties and
redelivery status are updated in the area below the table. Double
@@ -700,7 +700,7 @@
fashion as described for Queues, again showing an Attributes tab
initially, with the Operations tab next:
</para><para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113115.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113115.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para><para>
Of the four default Exchange Types <emphasis>(direct, fanout, headers,
and topic)</emphasis> all but <emphasis>headers</emphasis> have their bindings
@@ -711,12 +711,12 @@
button opens a dialog allowing association of an existing queue
with the entered Binding.
</para><para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113116.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113116.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para><para>
The <emphasis>headers</emphasis> Exchange type (default instantiation
<emphasis>amq.match or amq.headers</emphasis>) is presented as below:
</para><para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113117.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113117.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para><para>
In the previous figure, the left table indicates the binding
number, and the Queue associated with the binding. Selecting one
@@ -724,7 +724,7 @@
the header values that control when the binding matches an
incoming message.
</para><para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113118.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113118.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
</para><para>
Pressing the <emphasis>Create</emphasis> button when managing a
<emphasis>headers</emphasis> Exchange opens a dialog allowing creation of a
@@ -760,7 +760,7 @@
Notifications. The Operations tab can be seen in the figure
below.
</para><para>
- <mediaobject><imageobject><imagedata fileref="images/jmx_console/3113119.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
+ <mediaobject><imageobject><imagedata fileref="images/3113119.png" format="PNG" scalefit="1"/></imageobject><textobject><phrase/></textobject><caption><para/></caption></mediaobject>
The main table shows the properties of all the Channels that are
present on the Connection, including whether they are
<emphasis>Transactional</emphasis>, the <emphasis>Number of Unacked Messages</emphasis>
diff --git a/qpid/doc/book/src/Qpid-JMX-Management-Console.xml b/qpid/doc/book/src/java-broker/Qpid-JMX-Management-Console.xml
index fb46f4a01a..fb46f4a01a 100644
--- a/qpid/doc/book/src/Qpid-JMX-Management-Console.xml
+++ b/qpid/doc/book/src/java-broker/Qpid-JMX-Management-Console.xml
diff --git a/qpid/doc/book/src/Qpid-Java-Broker-Management-CLI.xml b/qpid/doc/book/src/java-broker/Qpid-Java-Broker-Management-CLI.xml
index 84c4b7b7a4..84c4b7b7a4 100644
--- a/qpid/doc/book/src/Qpid-Java-Broker-Management-CLI.xml
+++ b/qpid/doc/book/src/java-broker/Qpid-Java-Broker-Management-CLI.xml
diff --git a/qpid/doc/book/src/Qpid-Java-Build-How-To.xml b/qpid/doc/book/src/java-broker/Qpid-Java-Build-How-To.xml
index 9f3625760a..9f3625760a 100644
--- a/qpid/doc/book/src/Qpid-Java-Build-How-To.xml
+++ b/qpid/doc/book/src/java-broker/Qpid-Java-Build-How-To.xml
diff --git a/qpid/doc/book/src/Qpid-Java-FAQ.xml b/qpid/doc/book/src/java-broker/Qpid-Java-FAQ.xml
index 845c343350..2940e58138 100644
--- a/qpid/doc/book/src/Qpid-Java-FAQ.xml
+++ b/qpid/doc/book/src/java-broker/Qpid-Java-FAQ.xml
@@ -742,15 +742,9 @@ amqj.logging.level
</title>
<para>
- There are two possibilities here:
- </para><para>
- 1) The management console can be used to interogate an active
+ The management console can be used to interogate an active
broker and browse the contents of a queue.See the <xref linkend="qpid_Qpid-JMX-Management-Console"/>
page for further details.
- </para><para>
- 2) The <xref linkend="qpid_MessageStore-Tool"/> can be used to inspect
- the contents of a persistent message store. Note: this can
- currently only be used when the broker is offline.
</para>
<!--h3--></section>
@@ -773,13 +767,14 @@ amqj.logging.level
</title>
<para>
- The Java broker does not currently implement producer flow
- control. Publishes are currently asynchronous, so there is no
- ability to rate limit this automatically. While this is something
- which will be addressed in the future, it is currently up to
- applications to ensure that they do not publish faster than the
- messages are being consumed for signifcant periods of time.
- </para>
+ Switch on producer flow control to prevent temporary spikes in
+ message production over-filling the broker.
+
+ Of course, if the long-term rate of message production exceeds
+ the rate of message
+ consumption then that is an architectural problem that can only
+ be temporarily mitigated by producer flow control.
+ </para>
<!--h3--></section>
<section role="h3" id="QpidJavaFAQ-ThebrokerkeepsthrowinganOutOfMemoryexception-3F"><title>
diff --git a/qpid/doc/book/src/Qpid-Management-Features.xml b/qpid/doc/book/src/java-broker/Qpid-Management-Features.xml
index c90d7e97c6..c90d7e97c6 100644
--- a/qpid/doc/book/src/Qpid-Management-Features.xml
+++ b/qpid/doc/book/src/java-broker/Qpid-Management-Features.xml
diff --git a/qpid/doc/book/src/Qpid-Troubleshooting-Guide.xml b/qpid/doc/book/src/java-broker/Qpid-Troubleshooting-Guide.xml
index 0920f18798..0920f18798 100644
--- a/qpid/doc/book/src/Qpid-Troubleshooting-Guide.xml
+++ b/qpid/doc/book/src/java-broker/Qpid-Troubleshooting-Guide.xml
diff --git a/qpid/doc/book/src/java/broker/configuration/Topic-Configuration.xml b/qpid/doc/book/src/java-broker/Topic-Configuration.xml
index 1f73bbd7a4..1f73bbd7a4 100644
--- a/qpid/doc/book/src/java/broker/configuration/Topic-Configuration.xml
+++ b/qpid/doc/book/src/java-broker/Topic-Configuration.xml
diff --git a/qpid/doc/book/src/Use-Priority-Queues.xml b/qpid/doc/book/src/java-broker/Use-Priority-Queues.xml
index 466d958d43..466d958d43 100644
--- a/qpid/doc/book/src/Use-Priority-Queues.xml
+++ b/qpid/doc/book/src/java-broker/Use-Priority-Queues.xml
diff --git a/qpid/doc/book/src/images/jmx_console/3113098.png b/qpid/doc/book/src/java-broker/images/3113098.png
index 7de85030c6..7de85030c6 100644
--- a/qpid/doc/book/src/images/jmx_console/3113098.png
+++ b/qpid/doc/book/src/java-broker/images/3113098.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113099.png b/qpid/doc/book/src/java-broker/images/3113099.png
index fb6fc65d73..fb6fc65d73 100644
--- a/qpid/doc/book/src/images/jmx_console/3113099.png
+++ b/qpid/doc/book/src/java-broker/images/3113099.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113100.png b/qpid/doc/book/src/java-broker/images/3113100.png
index a7d727b854..a7d727b854 100644
--- a/qpid/doc/book/src/images/jmx_console/3113100.png
+++ b/qpid/doc/book/src/java-broker/images/3113100.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113101.png b/qpid/doc/book/src/java-broker/images/3113101.png
index 30731277c2..30731277c2 100644
--- a/qpid/doc/book/src/images/jmx_console/3113101.png
+++ b/qpid/doc/book/src/java-broker/images/3113101.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113102.png b/qpid/doc/book/src/java-broker/images/3113102.png
index f150a21b10..f150a21b10 100644
--- a/qpid/doc/book/src/images/jmx_console/3113102.png
+++ b/qpid/doc/book/src/java-broker/images/3113102.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113103.png b/qpid/doc/book/src/java-broker/images/3113103.png
index a91efb4306..a91efb4306 100644
--- a/qpid/doc/book/src/images/jmx_console/3113103.png
+++ b/qpid/doc/book/src/java-broker/images/3113103.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113104.png b/qpid/doc/book/src/java-broker/images/3113104.png
index c5ef12d8b1..c5ef12d8b1 100644
--- a/qpid/doc/book/src/images/jmx_console/3113104.png
+++ b/qpid/doc/book/src/java-broker/images/3113104.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113105.png b/qpid/doc/book/src/java-broker/images/3113105.png
index b155f9d9a1..b155f9d9a1 100644
--- a/qpid/doc/book/src/images/jmx_console/3113105.png
+++ b/qpid/doc/book/src/java-broker/images/3113105.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113106.png b/qpid/doc/book/src/java-broker/images/3113106.png
index 22bcdd084e..22bcdd084e 100644
--- a/qpid/doc/book/src/images/jmx_console/3113106.png
+++ b/qpid/doc/book/src/java-broker/images/3113106.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113107.png b/qpid/doc/book/src/java-broker/images/3113107.png
index cf5dd97e89..cf5dd97e89 100644
--- a/qpid/doc/book/src/images/jmx_console/3113107.png
+++ b/qpid/doc/book/src/java-broker/images/3113107.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113108.png b/qpid/doc/book/src/java-broker/images/3113108.png
index c0e5eafde2..c0e5eafde2 100644
--- a/qpid/doc/book/src/images/jmx_console/3113108.png
+++ b/qpid/doc/book/src/java-broker/images/3113108.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113109.png b/qpid/doc/book/src/java-broker/images/3113109.png
index 139d81d849..139d81d849 100644
--- a/qpid/doc/book/src/images/jmx_console/3113109.png
+++ b/qpid/doc/book/src/java-broker/images/3113109.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113110.png b/qpid/doc/book/src/java-broker/images/3113110.png
index 2207f15cd7..2207f15cd7 100644
--- a/qpid/doc/book/src/images/jmx_console/3113110.png
+++ b/qpid/doc/book/src/java-broker/images/3113110.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113111.png b/qpid/doc/book/src/java-broker/images/3113111.png
index 5737f41caf..5737f41caf 100644
--- a/qpid/doc/book/src/images/jmx_console/3113111.png
+++ b/qpid/doc/book/src/java-broker/images/3113111.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113112.png b/qpid/doc/book/src/java-broker/images/3113112.png
index d9ee094ab4..d9ee094ab4 100644
--- a/qpid/doc/book/src/images/jmx_console/3113112.png
+++ b/qpid/doc/book/src/java-broker/images/3113112.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113113.png b/qpid/doc/book/src/java-broker/images/3113113.png
index e80812f83c..e80812f83c 100644
--- a/qpid/doc/book/src/images/jmx_console/3113113.png
+++ b/qpid/doc/book/src/java-broker/images/3113113.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113114.png b/qpid/doc/book/src/java-broker/images/3113114.png
index b237181150..b237181150 100644
--- a/qpid/doc/book/src/images/jmx_console/3113114.png
+++ b/qpid/doc/book/src/java-broker/images/3113114.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113115.png b/qpid/doc/book/src/java-broker/images/3113115.png
index 84ad42b567..84ad42b567 100644
--- a/qpid/doc/book/src/images/jmx_console/3113115.png
+++ b/qpid/doc/book/src/java-broker/images/3113115.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113116.png b/qpid/doc/book/src/java-broker/images/3113116.png
index 18b979792f..18b979792f 100644
--- a/qpid/doc/book/src/images/jmx_console/3113116.png
+++ b/qpid/doc/book/src/java-broker/images/3113116.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113117.png b/qpid/doc/book/src/java-broker/images/3113117.png
index 3b33ef67ac..3b33ef67ac 100644
--- a/qpid/doc/book/src/images/jmx_console/3113117.png
+++ b/qpid/doc/book/src/java-broker/images/3113117.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113118.png b/qpid/doc/book/src/java-broker/images/3113118.png
index 60451f88cf..60451f88cf 100644
--- a/qpid/doc/book/src/images/jmx_console/3113118.png
+++ b/qpid/doc/book/src/java-broker/images/3113118.png
Binary files differ
diff --git a/qpid/doc/book/src/images/jmx_console/3113119.png b/qpid/doc/book/src/java-broker/images/3113119.png
index 16ded074bd..16ded074bd 100644
--- a/qpid/doc/book/src/images/jmx_console/3113119.png
+++ b/qpid/doc/book/src/java-broker/images/3113119.png
Binary files differ
diff --git a/qpid/doc/book/src/java-broker/images/HA-2N-Key.png b/qpid/doc/book/src/java-broker/images/HA-2N-Key.png
new file mode 100644
index 0000000000..3c1856d496
--- /dev/null
+++ b/qpid/doc/book/src/java-broker/images/HA-2N-Key.png
Binary files differ
diff --git a/qpid/doc/book/src/java-broker/images/HA-2N-Key.svg b/qpid/doc/book/src/java-broker/images/HA-2N-Key.svg
new file mode 100644
index 0000000000..9567f385d5
--- /dev/null
+++ b/qpid/doc/book/src/java-broker/images/HA-2N-Key.svg
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="-7 -3 435 195" width="435pt" height="195pt"><metadata xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:date>2012-05-31 06:19Z</dc:date><!-- Produced by OmniGraffle Professional 5.3.6 --></metadata><defs><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="3.488"/><feOffset in="blur" result="offset" dx="0" dy="4"/><feFlood flood-color="black" flood-opacity=".75" result="flood"/><feComposite in="flood" in2="offset" operator="in"/></filter><linearGradient x1="0" x2="1" id="Gradient" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#2824aa"/></linearGradient><linearGradient id="Obj_Gradient" xl:href="#Gradient" gradientTransform="translate(52.803375 70.5) rotate(90) scale(24)"/><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><linearGradient x1="0" x2="1" id="Gradient_2" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#0caa25"/></linearGradient><linearGradient id="Obj_Gradient_2" xl:href="#Gradient_2" gradientTransform="translate(127.99037 28.999998) rotate(90) scale(25.5938)"/><linearGradient x1="0" x2="1" id="Gradient_3" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#aa172d"/></linearGradient><linearGradient id="Obj_Gradient_3" xl:href="#Gradient_3" gradientTransform="translate(160.86441 28.999998) rotate(90) scale(25.5938)"/><linearGradient x1="0" x2="1" id="Gradient_4" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#6923aa"/></linearGradient><linearGradient id="Obj_Gradient_4" xl:href="#Gradient_4" gradientTransform="translate(52.803375 107.5) rotate(90) scale(24)"/><radialGradient cx="0" cy="0" r="1" id="Gradient_5" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset=".5" stop-color="#b5d0ea"/><stop offset="1" stop-color="#aaa"/></radialGradient><radialGradient id="Obj_Gradient_5" xl:href="#Gradient_5" gradientTransform="translate(56.678375 41.796898) scale(37.736065)"/><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="Arrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -3 6 6" markerWidth="6" markerHeight="6" color="black"><g><path d="M 3.7333333 0 L 0 -1.4 L 0 1.4 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><linearGradient x1="0" x2="1" id="Gradient_6" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#b5011d"/></linearGradient><linearGradient id="Obj_Gradient_6" xl:href="#Gradient_6" gradientTransform="translate(287.5 113.94637) rotate(90) scale(26.035202)"/><radialGradient cx="0" cy="0" r="1" id="Gradient_7" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset=".5" stop-color="#ea061f"/><stop offset="1" stop-color="#aaa"/></radialGradient><radialGradient id="Obj_Gradient_7" xl:href="#Gradient_7" gradientTransform="translate(280 41) scale(26.907248)"/><font-face font-family="Arial Unicode MS" font-size="36" panose-1="2 11 6 4 2 2 2 2 2 4" units-per-em="1000" underline-position="-100.097656" underline-thickness="49.804688" slope="0" x-height="529.78516" cap-height="728.02734" ascent="1068.84766" descent="-270.9961" font-weight="500"><font-face-src><font-face-name name="ArialUnicodeMS"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 7</title><g><title>Layer 1</title><g><use xl:href="#id240_Graphic" filter="url(#Shadow)"/><use xl:href="#id239_Graphic" filter="url(#Shadow)"/><use xl:href="#id238_Graphic" filter="url(#Shadow)"/><use xl:href="#id237_Graphic" filter="url(#Shadow)"/><use xl:href="#id236_Graphic" filter="url(#Shadow)"/><use xl:href="#id235_Graphic" filter="url(#Shadow)"/><use xl:href="#id229_Graphic" filter="url(#Shadow)"/><use xl:href="#id227_Graphic" filter="url(#Shadow)"/></g><g id="id240_Graphic"><rect x="13.999878" y="13" width="394.00012" height="155" fill="white"/><rect x="13.999878" y="13" width="394.00012" height="155" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g id="id239_Graphic"><rect x="28.177876" y="70.5" width="49.251003" height="24" fill="url(#Obj_Gradient)"/><rect x="28.177876" y="70.5" width="49.251003" height="24" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(33.177876 75.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="1.9536247" y="11" textLength="35.34375">Broker</tspan></text></g><g id="id238_Graphic"><path d="M 115.652176 41.796898 L 121.821274 28.999998 L 134.15947 28.999998 L 140.32857 41.796898 L 134.15947 54.593796 L 121.821274 54.593796 Z" fill="url(#Obj_Gradient_2)"/><path d="M 115.652176 41.796898 L 121.821274 28.999998 L 134.15947 28.999998 L 140.32857 41.796898 L 134.15947 54.593796 L 121.821274 54.593796 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g id="id237_Graphic"><path d="M 148.526215 41.796898 L 154.69531 28.999998 L 167.03351 28.999998 L 173.20261 41.796898 L 167.03351 54.593796 L 154.69531 54.593796 Z" fill="url(#Obj_Gradient_3)"/><path d="M 148.526215 41.796898 L 154.69531 28.999998 L 167.03351 28.999998 L 173.20261 41.796898 L 167.03351 54.593796 L 154.69531 54.593796 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g id="id236_Graphic"><path d="M 32.853378 107.5 L 72.753372 107.5 C 77.472977 107.5 81.303375 112.876 81.303375 119.5 C 81.303375 126.124 77.472977 131.5 72.753372 131.5 L 32.853378 131.5 C 28.133776 131.5 24.303375 126.124 24.303375 119.5 C 24.303375 112.876 28.133776 107.5 32.853378 107.5" fill="url(#Obj_Gradient_4)"/><path d="M 32.853378 107.5 L 72.753372 107.5 C 77.472977 107.5 81.303375 112.876 81.303375 119.5 C 81.303375 126.124 77.472977 131.5 72.753372 131.5 L 32.853378 131.5 C 28.133776 131.5 24.303375 126.124 24.303375 119.5 C 24.303375 112.876 28.133776 107.5 32.853378 107.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(35.003376 112.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="2.4601574" y="11" textLength="30.679688">Client</tspan></text></g><g id="id235_Graphic"><path d="M 30.434645 43.17154 C 18.959625 41.796898 23.535576 30.224663 41.840797 32.199223 C 43.539116 28.35017 64.825623 28.974915 64.686462 32.199223 C 78.033752 28.075294 95.09079 36.298325 83.649857 40.422256 C 97.37842 42.421642 83.476616 53.194073 72.209625 51.394573 C 71.307922 54.39391 51.165936 55.443512 49.398033 51.394573 C 37.992596 55.718647 14.210434 49.070145 30.434645 43.17154 Z" fill="url(#Obj_Gradient_5)"/><path d="M 30.434645 43.17154 C 18.959625 41.796898 23.535576 30.224663 41.840797 32.199223 C 43.539116 28.35017 64.825623 28.974915 64.686462 32.199223 C 78.033752 28.075294 95.09079 36.298325 83.649857 40.422256 C 97.37842 42.421642 83.476616 53.194073 72.209625 51.394573 C 71.307922 54.39391 51.165936 55.443512 49.398033 51.394573 C 37.992596 55.718647 14.210434 49.070145 30.434645 43.17154 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(36.828377 34.796898)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".84511757" y="11" textLength="38.009766">Cluster</tspan></text></g><text transform="translate(194.40121 29)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="8.0039062">V</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="7.7929688" y="11" textLength="26.009766">irtual</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="22.68164">host</tspan></text><line x1="115.65217" y1="84" x2="162.7522" y2="84" marker-end="url(#Arrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="114.77717" y1="114.406006" x2="155.878815" y2="114.99225" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><text transform="translate(181.40121 70.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="59.367188">Replication</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="36.673828">stream</tspan></text><text transform="translate(181.90121 93.906006)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="30.679688">Client</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="39" textLength="58.04297">connection</tspan></text><g id="id229_Graphic"><path d="M 257.6145 123.341385 L 303.64609 123.13183 C 303.64609 123.13183 305.97821 110.84691 310.80124 114.69145 C 315.62408 118.53591 319 121.44636 319 121.44636 L 315.92612 122.44486 L 307.22827 122.069695 L 307.22821 132.69258 L 315.47678 133.06787 C 315.47678 133.06787 315.92618 136.46542 315.92618 136.78604 C 315.92618 137.10628 311.77792 139.65259 309.63474 139.972885 C 307.49097 140.29318 304.06018 131.63016 304.06018 131.63039 C 304.06018 131.63039 257.61444 130.06921 257.48056 130.06921 C 257.34662 130.06921 256.13666 132.15001 256.00693 126.86558 C 255.87709 121.581276 257.6145 123.341385 257.6145 123.341385 Z" fill="url(#Obj_Gradient_6)"/><path d="M 257.6145 123.341385 L 303.64609 123.13183 C 303.64609 123.13183 305.97821 110.84691 310.80124 114.69145 C 315.62408 118.53591 319 121.44636 319 121.44636 L 315.92612 122.44486 L 307.22827 122.069695 L 307.22821 132.69258 L 315.47678 133.06787 C 315.47678 133.06787 315.92618 136.46542 315.92618 136.78604 C 315.92618 137.10628 311.77792 139.65259 309.63474 139.972885 C 307.49097 140.29318 304.06018 131.63016 304.06018 131.63039 C 304.06018 131.63039 257.61444 130.06921 257.48056 130.06921 C 257.34662 130.06921 256.13666 132.15001 256.00693 126.86558 C 255.87709 121.581276 257.6145 123.341385 257.6145 123.341385 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><text transform="translate(334.09879 119.963974)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="35.34961">Repair</tspan></text><g id="id227_Graphic"><path d="M 260 32.310394 L 275.19995 42.241386 L 263.13727 54.738464 L 267.99988 59 L 281.17648 45.481422 L 295.20001 54.655197 L 300 49.68975 L 284.79993 39.137974 L 297.59991 27.965553 L 290.39993 23 L 279.19989 35.413807 L 266.4 25.482777 Z" fill="url(#Obj_Gradient_7)"/><path d="M 260 32.310394 L 275.19995 42.241386 L 263.13727 54.738464 L 267.99988 59 L 281.17648 45.481422 L 295.20001 54.655197 L 300 49.68975 L 284.79993 39.137974 L 297.59991 27.965553 L 290.39993 23 L 279.19989 35.413807 L 266.4 25.482777 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><text transform="translate(329.02448 34)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="26.677734">Fault</tspan></text><text transform="translate(264 59.5)" fill="#262626"><tspan font-family="Arial Unicode MS" font-size="36" font-weight="500" fill="#262626" x="0" y="38" textLength="36">â™›</tspan></text><text transform="translate(320.52448 72)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="54.035156">Designate</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="41.332031">Primary</tspan></text></g></g></svg>
diff --git a/qpid/doc/book/src/java-broker/images/HA-2N-MasterFail.png b/qpid/doc/book/src/java-broker/images/HA-2N-MasterFail.png
new file mode 100644
index 0000000000..b839d291b9
--- /dev/null
+++ b/qpid/doc/book/src/java-broker/images/HA-2N-MasterFail.png
Binary files differ
diff --git a/qpid/doc/book/src/java-broker/images/HA-2N-MasterFail.svg b/qpid/doc/book/src/java-broker/images/HA-2N-MasterFail.svg
new file mode 100644
index 0000000000..35b2c643ff
--- /dev/null
+++ b/qpid/doc/book/src/java-broker/images/HA-2N-MasterFail.svg
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="-20 -16 600 871" width="50pc" height="871pt"><metadata xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:date>2012-05-31 06:19Z</dc:date><!-- Produced by OmniGraffle Professional 5.3.6 --></metadata><defs><radialGradient cx="0" cy="0" r="1" id="Gradient" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset=".5" stop-color="#b5d0ea"/><stop offset="1" stop-color="#aaa"/></radialGradient><radialGradient id="Obj_Gradient" xl:href="#Gradient" gradientTransform="translate(200.35501 136.5) scale(145.18695)"/><linearGradient x1="0" x2="1" id="Gradient_2" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#2824aa"/></linearGradient><linearGradient id="Obj_Gradient_2" xl:href="#Gradient_2" gradientTransform="translate(212.855 146) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_3" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#0caa25"/></linearGradient><linearGradient id="Obj_Gradient_3" xl:href="#Gradient_3" gradientTransform="translate(212.85451 162.5) rotate(90) scale(36)"/><linearGradient x1="0" x2="1" id="Gradient_4" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#6923aa"/></linearGradient><linearGradient id="Obj_Gradient_4" xl:href="#Gradient_4" gradientTransform="translate(73.18171 111) rotate(90) scale(24)"/><linearGradient id="Obj_Gradient_5" xl:href="#Gradient_2" gradientTransform="translate(195.355 59.999996) rotate(90) scale(69.000007)"/><linearGradient x1="0" x2="1" id="Gradient_5" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#aa172d"/></linearGradient><linearGradient id="Obj_Gradient_6" xl:href="#Gradient_5" gradientTransform="translate(195.35451 78) rotate(90) scale(36)"/><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -3 6 6" markerWidth="6" markerHeight="6" color="black"><g><path d="M 3.7333333 0 L 0 -1.4 L 0 1.4 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="Arrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><radialGradient id="Obj_Gradient_7" xl:href="#Gradient" gradientTransform="translate(470.5 133.5) scale(145.18695)"/><linearGradient id="Obj_Gradient_8" xl:href="#Gradient_2" gradientTransform="translate(488 141.5) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_6" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#aaa3a7"/></linearGradient><linearGradient id="Obj_Gradient_9" xl:href="#Gradient_6" gradientTransform="translate(487.9995 158) rotate(90) scale(36)"/><linearGradient id="Obj_Gradient_10" xl:href="#Gradient_4" gradientTransform="translate(350.21 85) rotate(90) scale(24)"/><linearGradient id="Obj_Gradient_11" xl:href="#Gradient_2" gradientTransform="translate(470.5 55.5) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_7" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#aaaaa3"/></linearGradient><linearGradient id="Obj_Gradient_12" xl:href="#Gradient_7" gradientTransform="translate(470.4995 73.5) rotate(90) scale(36)"/><radialGradient cx="0" cy="0" r="1" id="Gradient_8" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset=".5" stop-color="#ea061f"/><stop offset="1" stop-color="#aaa"/></radialGradient><radialGradient id="Obj_Gradient_13" xl:href="#Gradient_8" gradientTransform="translate(488 176) scale(38.183766)"/><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><radialGradient id="Obj_Gradient_14" xl:href="#Gradient" gradientTransform="translate(474.08 415.255) scale(145.18695)"/><linearGradient id="Obj_Gradient_15" xl:href="#Gradient_2" gradientTransform="translate(491.58 423.255) rotate(90) scale(69)"/><linearGradient id="Obj_Gradient_16" xl:href="#Gradient_6" gradientTransform="translate(491.5795 439.755) rotate(90) scale(36)"/><linearGradient id="Obj_Gradient_17" xl:href="#Gradient_4" gradientTransform="translate(353.79 366.755) rotate(90) scale(24.00003)"/><linearGradient id="Obj_Gradient_18" xl:href="#Gradient_2" gradientTransform="translate(474.08 337.255) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_9" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#21aa1a"/></linearGradient><linearGradient id="Obj_Gradient_19" xl:href="#Gradient_9" gradientTransform="translate(474.0795 355.255) rotate(90) scale(36)"/><radialGradient id="Obj_Gradient_20" xl:href="#Gradient" gradientTransform="translate(192.79001 696.5) scale(145.18695)"/><linearGradient id="Obj_Gradient_21" xl:href="#Gradient_2" gradientTransform="translate(210.28999 704.5) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_10" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#aa1f43"/></linearGradient><linearGradient id="Obj_Gradient_22" xl:href="#Gradient_10" gradientTransform="translate(210.2895 721) rotate(90) scale(36)"/><linearGradient id="Obj_Gradient_23" xl:href="#Gradient_4" gradientTransform="translate(72.5 648) rotate(90) scale(24)"/><linearGradient id="Obj_Gradient_24" xl:href="#Gradient_2" gradientTransform="translate(192.78999 618.5) rotate(90) scale(69)"/><linearGradient id="Obj_Gradient_25" xl:href="#Gradient_9" gradientTransform="translate(192.7895 636.5) rotate(90) scale(36)"/><radialGradient id="Obj_Gradient_26" xl:href="#Gradient" gradientTransform="translate(166.45009 415.255) scale(145.18695)"/><linearGradient id="Obj_Gradient_27" xl:href="#Gradient_2" gradientTransform="translate(183.95009 423.255) rotate(90) scale(69)"/><linearGradient id="Obj_Gradient_28" xl:href="#Gradient_6" gradientTransform="translate(183.9496 439.755) rotate(90) scale(36)"/><linearGradient id="Obj_Gradient_29" xl:href="#Gradient_4" gradientTransform="translate(72.5 372.755) rotate(90) scale(24)"/><linearGradient id="Obj_Gradient_30" xl:href="#Gradient_2" gradientTransform="translate(180.08009 343.255) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_11" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#a9a5a6"/></linearGradient><linearGradient id="Obj_Gradient_31" xl:href="#Gradient_11" gradientTransform="translate(180.0796 361.255) rotate(90) scale(36)"/><radialGradient id="Obj_Gradient_32" xl:href="#Gradient_8" gradientTransform="translate(183.95009 457.755) scale(38.183766)"/><font-face font-family="Arial Unicode MS" font-size="36" panose-1="2 11 6 4 2 2 2 2 2 4" units-per-em="1000" underline-position="-100.097656" underline-thickness="49.804688" slope="0" x-height="529.78516" cap-height="728.02734" ascent="1068.84766" descent="-270.9961" font-weight="500"><font-face-src><font-face-name name="ArialUnicodeMS"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 2</title><g><title>Layer 1</title><rect x="0" y="0" width="278" height="273" fill="white"/><rect x="0" y="0" width="278" height="273" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 146.38904 149.98122 C 122.79251 136.5 132.20221 23.010353 169.84393 42.375 C 173.33626 4.6271057 217.1085 10.754013 216.82236 42.375 C 244.26889 1.931366 279.34393 82.575165 255.81749 123.01879 C 284.04803 142.62691 255.46124 248.27281 232.29251 230.625 C 230.43831 260.03967 189.01958 270.33319 185.38417 230.625 C 161.930725 273.03143 113.02657 207.82918 146.38904 149.98122 Z" fill="url(#Obj_Gradient)"/><path d="M 146.38904 149.98122 C 122.79251 136.5 132.20221 23.010353 169.84393 42.375 C 173.33626 4.6271057 217.1085 10.754013 216.82236 42.375 C 244.26889 1.931366 279.34393 82.575165 255.81749 123.01879 C 284.04803 142.62691 255.46124 248.27281 232.29251 230.625 C 230.43831 260.03967 189.01958 270.33319 185.38417 230.625 C 161.930725 273.03143 113.02657 207.82918 146.38904 149.98122 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="179.355" y="146" width="67" height="69" fill="url(#Obj_Gradient_2)"/><rect x="179.355" y="146" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 192.524 180.5 L 202.68925 162.5 L 223.01976 162.5 L 233.18501 180.5 L 223.01976 198.5 L 202.68925 198.5 Z" fill="url(#Obj_Gradient_3)"/><path d="M 192.524 180.5 L 202.68925 162.5 L 223.01976 162.5 L 233.18501 180.5 L 223.01976 198.5 L 202.68925 198.5 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 62.68171 111 L 83.68171 111 C 86.16571 111 88.18171 116.376 88.18171 123 C 88.18171 129.62399 86.16571 135 83.68171 135 L 62.68171 135 C 60.197708 135 58.18171 129.62399 58.18171 123 C 58.18171 116.376 60.197708 111 62.68171 111" fill="url(#Obj_Gradient_4)"/><path d="M 62.68171 111 L 83.68171 111 C 86.16571 111 88.18171 116.376 88.18171 123 C 88.18171 129.62399 86.16571 135 83.68171 135 L 62.68171 135 C 60.197708 135 58.18171 129.62399 58.18171 123 C 58.18171 116.376 60.197708 111 62.68171 111" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="161.855" y="60" width="67" height="69" fill="url(#Obj_Gradient_5)"/><rect x="161.855" y="60" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 175.024 96 L 185.18925 78 L 205.51976 78 L 215.68501 96 L 205.51976 114 L 185.18925 114 Z" fill="url(#Obj_Gradient_6)"/><path d="M 175.024 96 L 185.18925 78 L 205.51976 78 L 215.68501 96 L 205.51976 114 L 185.18925 114 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 74.355003 137 C 80.38701 150.765915 72.80871 171.11536 92.452835 178.3019 C 109.12348 184.40063 145.40762 181.02281 176.33522 180.23158" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><path d="M 222.38084 162.05576 C 227.03842 153.03807 238.59448 143.85422 236.355 135 C 234.66139 128.303986 225.07715 121.79347 216.5538 115.25858" marker-end="url(#Arrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="281.29001" y="0" width="278.00003" height="273" fill="white"/><rect x="281.29001" y="0" width="278.00003" height="273" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 416.53403 146.98122 C 392.9375 133.5 402.3472 20.010353 439.98892 39.375 C 443.48123 1.6271057 487.2535 7.754013 486.96735 39.375 C 514.41388 -1.06863403 549.4889 79.575165 525.96246 120.01879 C 554.19305 139.62691 525.60626 245.27281 502.4375 227.625 C 500.5833 257.03967 459.16455 267.33319 455.52917 227.625 C 432.07571 270.03143 383.17157 204.82918 416.53403 146.98122 Z" fill="url(#Obj_Gradient_7)"/><path d="M 416.53403 146.98122 C 392.9375 133.5 402.3472 20.010353 439.98892 39.375 C 443.48123 1.6271057 487.2535 7.754013 486.96735 39.375 C 514.41388 -1.06863403 549.4889 79.575165 525.96246 120.01879 C 554.19305 139.62691 525.60626 245.27281 502.4375 227.625 C 500.5833 257.03967 459.16455 267.33319 455.52917 227.625 C 432.07571 270.03143 383.17157 204.82918 416.53403 146.98122 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="454.5" y="141.5" width="67" height="69" fill="url(#Obj_Gradient_8)"/><rect x="454.5" y="141.5" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 467.669 176 L 477.83426 158 L 498.16476 158 L 508.33002 176 L 498.16476 194 L 477.83426 194 Z" fill="url(#Obj_Gradient_9)"/><path d="M 467.669 176 L 477.83426 158 L 498.16476 158 L 508.33002 176 L 498.16476 194 L 477.83426 194 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 339.71 85 L 360.71 85 C 363.194 85 365.21 90.376 365.21 97 C 365.21 103.624 363.194 109 360.71 109 L 339.71 109 C 337.22598 109 335.21 103.624 335.21 97 C 335.21 90.376 337.22598 85 339.71 85" fill="url(#Obj_Gradient_10)"/><path d="M 339.71 85 L 360.71 85 C 363.194 85 365.21 90.376 365.21 97 C 365.21 103.624 363.194 109 360.71 109 L 339.71 109 C 337.22598 109 335.21 103.624 335.21 97 C 335.21 90.376 337.22598 85 339.71 85" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="437" y="55.5" width="67" height="69" fill="url(#Obj_Gradient_11)"/><rect x="437" y="55.5" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 450.169 91.5 L 460.33426 73.5 L 480.66476 73.5 L 490.83002 91.5 L 480.66476 109.5 L 460.33426 109.5 Z" fill="url(#Obj_Gradient_12)"/><path d="M 450.169 91.5 L 460.33426 73.5 L 480.66476 73.5 L 490.83002 91.5 L 480.66476 109.5 L 460.33426 109.5 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 461 162.96559 L 481.51996 177.86208 L 465.23532 196.6077 L 471.79984 203 L 489.58826 182.72214 L 508.52 196.48279 L 515 189.03462 L 494.4799 173.20695 L 511.7599 156.44833 L 502.0399 149 L 486.91986 167.62071 L 469.64001 152.72417 Z" fill="url(#Obj_Gradient_13)"/><path d="M 461 162.96559 L 481.51996 177.86208 L 465.23532 196.6077 L 471.79984 203 L 489.58826 182.72214 L 508.52 196.48279 L 515 189.03462 L 494.4799 173.20695 L 511.7599 156.44833 L 502.0399 149 L 486.91986 167.62071 L 469.64001 152.72417 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(5.8400302 4)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".16308594" y="11" textLength="6.673828">1</tspan></text><rect x="281.29001" y="278.755" width="278.00003" height="273" fill="white"/><rect x="281.29001" y="278.755" width="278.00003" height="273" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 420.11401 428.7362 C 396.51749 415.255 405.92719 301.76535 443.5689 321.13 C 447.06122 283.38211 490.8335 289.50903 490.54733 321.13 C 517.9939 280.68637 553.0689 361.33017 529.54248 401.7738 C 557.773 421.38193 529.18622 527.02783 506.0175 509.38 C 504.1633 538.79468 462.74454 549.0882 459.10913 509.38 C 435.6557 551.78644 386.75156 486.58417 420.11401 428.7362 Z" fill="url(#Obj_Gradient_14)"/><path d="M 420.11401 428.7362 C 396.51749 415.255 405.92719 301.76535 443.5689 321.13 C 447.06122 283.38211 490.8335 289.50903 490.54733 321.13 C 517.9939 280.68637 553.0689 361.33017 529.54248 401.7738 C 557.773 421.38193 529.18622 527.02783 506.0175 509.38 C 504.1633 538.79468 462.74454 549.0882 459.10913 509.38 C 435.6557 551.78644 386.75156 486.58417 420.11401 428.7362 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="458.08" y="423.255" width="66.99997" height="69" fill="url(#Obj_Gradient_15)"/><rect x="458.08" y="423.255" width="66.99997" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 471.249 457.755 L 481.41425 439.755 L 501.74475 439.755 L 511.91 457.755 L 501.74475 475.755 L 481.41425 475.755 Z" fill="url(#Obj_Gradient_16)"/><path d="M 471.249 457.755 L 481.41425 439.755 L 501.74475 439.755 L 511.91 457.755 L 501.74475 475.755 L 481.41425 475.755 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 343.29 366.755 L 364.29 366.755 C 366.77402 366.755 368.79 372.13101 368.79 378.755 C 368.79 385.379 366.77402 390.755 364.29 390.755 L 343.29 390.755 C 340.806 390.755 338.79 385.379 338.79 378.755 C 338.79 372.13101 340.806 366.755 343.29 366.755" fill="url(#Obj_Gradient_17)"/><path d="M 343.29 366.755 L 364.29 366.755 C 366.77402 366.755 368.79 372.13101 368.79 378.755 C 368.79 385.379 366.77402 390.755 364.29 390.755 L 343.29 390.755 C 340.806 390.755 338.79 385.379 338.79 378.755 C 338.79 372.13101 340.806 366.755 343.29 366.755" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="440.57999" y="337.255" width="67" height="69" fill="url(#Obj_Gradient_18)"/><rect x="440.57999" y="337.255" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 453.749 373.255 L 463.91425 355.255 L 484.24475 355.255 L 494.41 373.255 L 484.24475 391.255 L 463.91425 391.255 Z" fill="url(#Obj_Gradient_19)"/><path d="M 453.749 373.255 L 463.91425 355.255 L 484.24475 355.255 L 494.41 373.255 L 484.24475 391.255 L 463.91425 391.255 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 365.46664 366.5985 C 373.74026 357.98486 375.04666 340.75385 390.29 340.755 C 402.3567 340.75592 423.16241 351.55505 442.3288 360.32996" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><rect x="0" y="558" width="278" height="273" fill="white"/><rect x="0" y="558" width="278" height="273" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 138.824036 709.9812 C 115.22751 696.5 124.63721 583.01038 162.27893 602.375 C 165.77126 564.62708 209.5435 570.75403 209.25735 602.375 C 236.70389 561.9314 271.77893 642.57513 248.25249 683.0188 C 276.48303 702.6269 247.89624 808.27283 224.72751 790.625 C 222.8733 820.03967 181.45457 830.3332 177.81917 790.625 C 154.36572 833.03143 105.46157 767.82916 138.824036 709.9812 Z" fill="url(#Obj_Gradient_20)"/><path d="M 138.824036 709.9812 C 115.22751 696.5 124.63721 583.01038 162.27893 602.375 C 165.77126 564.62708 209.5435 570.75403 209.25735 602.375 C 236.70389 561.9314 271.77893 642.57513 248.25249 683.0188 C 276.48303 702.6269 247.89624 808.27283 224.72751 790.625 C 222.8733 820.03967 181.45457 830.3332 177.81917 790.625 C 154.36572 833.03143 105.46157 767.82916 138.824036 709.9812 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="176.78999" y="704.5" width="67" height="69" fill="url(#Obj_Gradient_21)"/><rect x="176.78999" y="704.5" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 189.959 739 L 200.12425 721 L 220.45476 721 L 230.62001 739 L 220.45476 757 L 200.12425 757 Z" fill="url(#Obj_Gradient_22)"/><path d="M 189.959 739 L 200.12425 721 L 220.45476 721 L 230.62001 739 L 220.45476 757 L 200.12425 757 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 62 648 L 83 648 C 85.484 648 87.5 653.37598 87.5 660 C 87.5 666.62402 85.484 672 83 672 L 62 672 C 59.516 672 57.5 666.62402 57.5 660 C 57.5 653.37598 59.516 648 62 648" fill="url(#Obj_Gradient_23)"/><path d="M 62 648 L 83 648 C 85.484 648 87.5 653.37598 87.5 660 C 87.5 666.62402 85.484 672 83 672 L 62 672 C 59.516 672 57.5 666.62402 57.5 660 C 57.5 653.37598 59.516 648 62 648" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="159.28999" y="618.5" width="67" height="69" fill="url(#Obj_Gradient_24)"/><rect x="159.28999" y="618.5" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 172.459 654.5 L 182.62425 636.5 L 202.95476 636.5 L 213.12001 654.5 L 202.95476 672.5 L 182.62425 672.5 Z" fill="url(#Obj_Gradient_25)"/><path d="M 172.459 654.5 L 182.62425 636.5 L 202.95476 636.5 L 213.12001 654.5 L 202.95476 672.5 L 182.62425 672.5 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 84.176636 647.8435 C 92.450264 639.22986 93.75663 621.99884 109 622 C 121.06667 622.0009 141.87247 632.80005 161.03886 641.575" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><path d="M 181.93205 672.16388 C 174.38545 684.44135 157.17926 699.40002 159.28999 709 C 160.93364 716.4756 174.29166 720.7052 185.68005 725.4799" marker-end="url(#Arrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(286 8)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".16308594" y="11" textLength="6.673828">2</tspan></text><text transform="translate(286 289.62299)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".16308594" y="11" textLength="6.673828">4</tspan></text><text transform="translate(10.3773594 571.472)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".16308594" y="11" textLength="6.673828">5</tspan></text><rect x="0" y="278.755" width="278" height="273" fill="white"/><rect x="0" y="278.755" width="278" height="273" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 112.48411 428.7362 C 88.88759 415.255 98.297287 301.76535 135.93901 321.13 C 139.431335 283.38211 183.20358 289.50903 182.91743 321.13 C 210.36397 280.68637 245.439 361.33017 221.91257 401.7738 C 250.14313 421.38193 221.55634 527.02783 198.38759 509.38 C 196.53339 538.79468 155.11465 549.0882 151.47925 509.38 C 128.02582 551.78644 79.12165 486.58417 112.48411 428.7362 Z" fill="url(#Obj_Gradient_26)"/><path d="M 112.48411 428.7362 C 88.88759 415.255 98.297287 301.76535 135.93901 321.13 C 139.431335 283.38211 183.20358 289.50903 182.91743 321.13 C 210.36397 280.68637 245.439 361.33017 221.91257 401.7738 C 250.14313 421.38193 221.55634 527.02783 198.38759 509.38 C 196.53339 538.79468 155.11465 549.0882 151.47925 509.38 C 128.02582 551.78644 79.12165 486.58417 112.48411 428.7362 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="150.45009" y="423.255" width="67" height="69" fill="url(#Obj_Gradient_27)"/><rect x="150.45009" y="423.255" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 163.61909 457.755 L 173.78435 439.755 L 194.11485 439.755 L 204.2801 457.755 L 194.11485 475.755 L 173.78435 475.755 Z" fill="url(#Obj_Gradient_28)"/><path d="M 163.61909 457.755 L 173.78435 439.755 L 194.11485 439.755 L 204.2801 457.755 L 194.11485 475.755 L 173.78435 475.755 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 62 372.755 L 83 372.755 C 85.484 372.755 87.5 378.13101 87.5 384.755 C 87.5 391.379 85.484 396.755 83 396.755 L 62 396.755 C 59.516 396.755 57.5 391.379 57.5 384.755 C 57.5 378.13101 59.516 372.755 62 372.755" fill="url(#Obj_Gradient_29)"/><path d="M 62 372.755 L 83 372.755 C 85.484 372.755 87.5 378.13101 87.5 384.755 C 87.5 391.379 85.484 396.755 83 396.755 L 62 396.755 C 59.516 396.755 57.5 391.379 57.5 384.755 C 57.5 378.13101 59.516 372.755 62 372.755" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="146.58009" y="343.255" width="67" height="69" fill="url(#Obj_Gradient_30)"/><rect x="146.58009" y="343.255" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 159.7491 379.255 L 169.91435 361.255 L 190.24486 361.255 L 200.41011 379.255 L 190.24486 397.255 L 169.91435 397.255 Z" fill="url(#Obj_Gradient_31)"/><path d="M 159.7491 379.255 L 169.91435 361.255 L 190.24486 361.255 L 200.41011 379.255 L 190.24486 397.255 L 169.91435 397.255 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 156.95009 444.72058 L 177.47003 459.6171 L 161.18541 478.3627 L 167.74992 484.755 L 185.53835 464.47714 L 204.47009 478.2378 L 210.95009 470.78964 L 190.42998 454.96198 L 207.70998 438.20334 L 197.98997 430.755 L 182.86996 449.3757 L 165.59009 434.47916 Z" fill="url(#Obj_Gradient_32)"/><path d="M 156.95009 444.72058 L 177.47003 459.6171 L 161.18541 478.3627 L 167.74992 484.755 L 185.53835 464.47714 L 204.47009 478.2378 L 210.95009 470.78964 L 190.42998 454.96198 L 207.70998 438.20334 L 197.98997 430.755 L 182.86996 449.3757 L 165.59009 434.47916 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(210.37738 346.23645)" fill="#262626"><tspan font-family="Arial Unicode MS" font-size="36" font-weight="500" fill="#262626" x="0" y="38" textLength="36">â™›</tspan></text><text transform="translate(5.8400302 287.736)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".16308594" y="11" textLength="6.673828">3</tspan></text></g></g></svg>
diff --git a/qpid/doc/book/src/java-broker/images/HA-2N-NetworkPartition.png b/qpid/doc/book/src/java-broker/images/HA-2N-NetworkPartition.png
new file mode 100644
index 0000000000..cd4a196924
--- /dev/null
+++ b/qpid/doc/book/src/java-broker/images/HA-2N-NetworkPartition.png
Binary files differ
diff --git a/qpid/doc/book/src/java-broker/images/HA-2N-NetworkPartition.svg b/qpid/doc/book/src/java-broker/images/HA-2N-NetworkPartition.svg
new file mode 100644
index 0000000000..375d88a7db
--- /dev/null
+++ b/qpid/doc/book/src/java-broker/images/HA-2N-NetworkPartition.svg
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="-20 -12 600 596" width="50pc" height="596pt"><metadata xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:date>2012-05-31 06:19Z</dc:date><!-- Produced by OmniGraffle Professional 5.3.6 --></metadata><defs><radialGradient cx="0" cy="0" r="1" id="Gradient" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset=".5" stop-color="#b5d0ea"/><stop offset="1" stop-color="#aaa"/></radialGradient><radialGradient id="Obj_Gradient" xl:href="#Gradient" gradientTransform="translate(474.08 425.5) scale(145.18695)"/><linearGradient x1="0" x2="1" id="Gradient_2" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#2824aa"/></linearGradient><linearGradient id="Obj_Gradient_2" xl:href="#Gradient_2" gradientTransform="translate(466.66 343.723) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_3" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#aa1f43"/></linearGradient><linearGradient id="Obj_Gradient_3" xl:href="#Gradient_3" gradientTransform="translate(466.65952 360.223) rotate(90) scale(36)"/><linearGradient x1="0" x2="1" id="Gradient_4" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#6923aa"/></linearGradient><linearGradient id="Obj_Gradient_4" xl:href="#Gradient_4" gradientTransform="translate(353.79 377) rotate(90) scale(24.00003)"/><linearGradient id="Obj_Gradient_5" xl:href="#Gradient_2" gradientTransform="translate(474.08 438) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_5" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#21aa1a"/></linearGradient><linearGradient id="Obj_Gradient_6" xl:href="#Gradient_5" gradientTransform="translate(474.0795 456) rotate(90) scale(36)"/><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -3 6 6" markerWidth="6" markerHeight="6" color="black"><g><path d="M 3.7333333 0 L 0 -1.4 L 0 1.4 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="Arrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><radialGradient id="Obj_Gradient_7" xl:href="#Gradient" gradientTransform="translate(470.5 137.5) scale(145.18695)"/><linearGradient id="Obj_Gradient_8" xl:href="#Gradient_2" gradientTransform="translate(488 145.5) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_6" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#6ead6a"/></linearGradient><linearGradient id="Obj_Gradient_9" xl:href="#Gradient_6" gradientTransform="translate(487.9995 162) rotate(90) scale(36)"/><linearGradient id="Obj_Gradient_10" xl:href="#Gradient_4" gradientTransform="translate(350.21 89) rotate(90) scale(24)"/><radialGradient cx="0" cy="0" r="1" id="Gradient_7" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset=".5" stop-color="#ea061f"/><stop offset="1" stop-color="#aaa"/></radialGradient><radialGradient id="Obj_Gradient_11" xl:href="#Gradient_7" gradientTransform="translate(487.17532 136.54291) scale(20.071926)"/><linearGradient id="Obj_Gradient_12" xl:href="#Gradient_2" gradientTransform="translate(466.66 55.445) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_8" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#b6b4b5"/></linearGradient><linearGradient id="Obj_Gradient_13" xl:href="#Gradient_8" gradientTransform="translate(466.65952 71.945) rotate(90) scale(36)"/><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><radialGradient id="Obj_Gradient_14" xl:href="#Gradient" gradientTransform="translate(200.35501 140.5) scale(145.18695)"/><linearGradient id="Obj_Gradient_15" xl:href="#Gradient_2" gradientTransform="translate(212.855 150) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_9" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#0caa25"/></linearGradient><linearGradient id="Obj_Gradient_16" xl:href="#Gradient_9" gradientTransform="translate(212.85451 166.5) rotate(90) scale(36)"/><linearGradient id="Obj_Gradient_17" xl:href="#Gradient_4" gradientTransform="translate(80.355003 115) rotate(90) scale(24)"/><linearGradient id="Obj_Gradient_18" xl:href="#Gradient_2" gradientTransform="translate(195.355 63.999996) rotate(90) scale(69.000007)"/><linearGradient x1="0" x2="1" id="Gradient_10" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#aa172d"/></linearGradient><linearGradient id="Obj_Gradient_19" xl:href="#Gradient_10" gradientTransform="translate(195.35451 82) rotate(90) scale(36)"/><radialGradient id="Obj_Gradient_20" xl:href="#Gradient" gradientTransform="translate(180.29001 423.5) scale(145.18695)"/><linearGradient id="Obj_Gradient_21" xl:href="#Gradient_2" gradientTransform="translate(172.37 346) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_11" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#aaa3a7"/></linearGradient><linearGradient id="Obj_Gradient_22" xl:href="#Gradient_11" gradientTransform="translate(172.36951 360.5) rotate(90) scale(36)"/><linearGradient id="Obj_Gradient_23" xl:href="#Gradient_4" gradientTransform="translate(72.5 379) rotate(90) scale(24)"/><linearGradient id="Obj_Gradient_24" xl:href="#Gradient_2" gradientTransform="translate(180.953 435.5) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_12" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#66ab61"/></linearGradient><linearGradient id="Obj_Gradient_25" xl:href="#Gradient_12" gradientTransform="translate(180.95251 452) rotate(90) scale(36)"/><linearGradient x1="0" x2="1" id="Gradient_13" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#b5011d"/></linearGradient><linearGradient id="Obj_Gradient_26" xl:href="#Gradient_13" gradientTransform="translate(169.38802 421.22614) rotate(45) scale(21.988586)"/></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 5</title><g><title>Layer 1</title><rect x="281.29001" y="287" width="278.00003" height="273" fill="white"/><rect x="281.29001" y="287" width="278.00003" height="273" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 420.11401 438.9812 C 396.51749 425.5 405.92719 312.01035 443.5689 331.375 C 447.06122 293.6271 490.8335 299.75403 490.54733 331.375 C 517.9939 290.93137 553.0689 371.57516 529.54248 412.0188 C 557.773 431.62692 529.18622 537.27283 506.0175 519.625 C 504.1633 549.03967 462.74454 559.3332 459.10913 519.625 C 435.6557 562.03143 386.75156 496.82916 420.11401 438.9812 Z" fill="url(#Obj_Gradient)"/><path d="M 420.11401 438.9812 C 396.51749 425.5 405.92719 312.01035 443.5689 331.375 C 447.06122 293.6271 490.8335 299.75403 490.54733 331.375 C 517.9939 290.93137 553.0689 371.57516 529.54248 412.0188 C 557.773 431.62692 529.18622 537.27283 506.0175 519.625 C 504.1633 549.03967 462.74454 559.3332 459.10913 519.625 C 435.6557 562.03143 386.75156 496.82916 420.11401 438.9812 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="433.16" y="343.723" width="67" height="69" fill="url(#Obj_Gradient_2)"/><rect x="433.16" y="343.723" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 446.329 378.223 L 456.49426 360.223 L 476.82477 360.223 L 486.99002 378.223 L 476.82477 396.223 L 456.49426 396.223 Z" fill="url(#Obj_Gradient_3)"/><path d="M 446.329 378.223 L 456.49426 360.223 L 476.82477 360.223 L 486.99002 378.223 L 476.82477 396.223 L 456.49426 396.223 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 343.29 377 L 364.29 377 C 366.77402 377 368.79 382.376 368.79 389 C 368.79 395.624 366.77402 401 364.29 401 L 343.29 401 C 340.806 401 338.79 395.624 338.79 389 C 338.79 382.376 340.806 377 343.29 377" fill="url(#Obj_Gradient_4)"/><path d="M 343.29 377 L 364.29 377 C 366.77402 377 368.79 382.376 368.79 389 C 368.79 395.624 366.77402 401 364.29 401 L 343.29 401 C 340.806 401 338.79 395.624 338.79 389 C 338.79 382.376 340.806 377 343.29 377" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="440.57999" y="438" width="67" height="69" fill="url(#Obj_Gradient_5)"/><rect x="440.57999" y="438" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 453.749 474 L 463.91425 456 L 484.24475 456 L 494.41 474 L 484.24475 492 L 463.91425 492 Z" fill="url(#Obj_Gradient_6)"/><path d="M 453.749 474 L 463.91425 456 L 484.24475 456 L 494.41 474 L 484.24475 492 L 463.91425 492 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 358.11069 401.47244 C 364.4065 419.64648 360.82266 444.49713 377 456 C 390.09668 465.31238 416.1513 465.88107 439.42624 468.3237" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><path d="M 480.27563 455.52597 C 482.5702 448.68466 488.31772 444.80447 487.16 435 C 486.26978 427.46088 481.2962 416.41544 476.94943 405.90878" marker-end="url(#Arrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="281.29001" y="4" width="278.00003" height="273" fill="white"/><rect x="281.29001" y="4" width="278.00003" height="273" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 416.53403 150.98122 C 392.9375 137.5 402.3472 24.010353 439.98892 43.375 C 443.48123 5.6271057 487.2535 11.754013 486.96735 43.375 C 514.41388 2.931366 549.4889 83.575165 525.96246 124.01879 C 554.19305 143.62691 525.60626 249.27281 502.4375 231.625 C 500.5833 261.03967 459.16455 271.33319 455.52917 231.625 C 432.07571 274.03143 383.17157 208.82918 416.53403 150.98122 Z" fill="url(#Obj_Gradient_7)"/><path d="M 416.53403 150.98122 C 392.9375 137.5 402.3472 24.010353 439.98892 43.375 C 443.48123 5.6271057 487.2535 11.754013 486.96735 43.375 C 514.41388 2.931366 549.4889 83.575165 525.96246 124.01879 C 554.19305 143.62691 525.60626 249.27281 502.4375 231.625 C 500.5833 261.03967 459.16455 271.33319 455.52917 231.625 C 432.07571 274.03143 383.17157 208.82918 416.53403 150.98122 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="454.5" y="145.5" width="67" height="69" fill="url(#Obj_Gradient_8)"/><rect x="454.5" y="145.5" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 467.669 180 L 477.83426 162 L 498.16476 162 L 508.33002 180 L 498.16476 198 L 477.83426 198 Z" fill="url(#Obj_Gradient_9)"/><path d="M 467.669 180 L 477.83426 162 L 498.16476 162 L 508.33002 180 L 498.16476 198 L 477.83426 198 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 339.71 89 L 360.71 89 C 363.194 89 365.21 94.376 365.21 101 C 365.21 107.624 363.194 113 360.71 113 L 339.71 113 C 337.22598 113 335.21 107.624 335.21 101 C 335.21 94.376 337.22598 89 339.71 89" fill="url(#Obj_Gradient_10)"/><path d="M 339.71 89 L 360.71 89 C 363.194 89 365.21 94.376 365.21 101 C 365.21 107.624 363.194 113 360.71 113 L 339.71 113 C 337.22598 113 335.21 107.624 335.21 101 C 335.21 94.376 337.22598 89 339.71 89" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 474.53464 129.01602 L 484.14154 137.61832 L 476.51752 148.44287 L 479.59082 152.134415 L 487.91891 140.42473 L 496.7823 148.37096 L 499.816 144.06992 L 490.20901 134.93007 L 498.2991 125.25254 L 493.74844 120.95141 L 486.66962 131.70415 L 478.57965 123.101974 Z" fill="url(#Obj_Gradient_11)"/><path d="M 474.53464 129.01602 L 484.14154 137.61832 L 476.51752 148.44287 L 479.59082 152.134415 L 487.91891 140.42473 L 496.7823 148.37096 L 499.816 144.06992 L 490.20901 134.93007 L 498.2991 125.25254 L 493.74844 120.95141 L 486.66962 131.70415 L 478.57965 123.101974 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="433.16" y="55.445" width="67" height="69" fill="url(#Obj_Gradient_12)"/><rect x="433.16" y="55.445" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 446.329 89.945 L 456.49426 71.945 L 476.82477 71.945 L 486.99002 89.945 L 476.82477 107.945 L 456.49426 107.945 Z" fill="url(#Obj_Gradient_13)"/><path d="M 446.329 89.945 L 456.49426 71.945 L 476.82477 71.945 L 486.99002 89.945 L 476.82477 107.945 L 456.49426 107.945 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 487.69427 161.50006 C 487.55954 153.3342 489.4468 145.84863 487.29 137 C 485.7971 130.87509 482.3659 124.09454 479.00916 117.33957" marker-end="url(#Arrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(5.8400302 4)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".16308594" y="11" textLength="6.673828">1</tspan></text><text transform="translate(286 6)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".16308594" y="11" textLength="6.673828">2</tspan></text><rect x="0" y="4" width="278" height="273" fill="white"/><rect x="0" y="4" width="278" height="273" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 146.38904 153.98122 C 122.79251 140.5 132.20221 27.010353 169.84393 46.375 C 173.33626 8.6271057 217.1085 14.754013 216.82236 46.375 C 244.26889 5.931366 279.34393 86.575165 255.81749 127.01879 C 284.04803 146.62691 255.46124 252.27281 232.29251 234.625 C 230.43831 264.03967 189.01958 274.33319 185.38417 234.625 C 161.930725 277.03143 113.02657 211.82918 146.38904 153.98122 Z" fill="url(#Obj_Gradient_14)"/><path d="M 146.38904 153.98122 C 122.79251 140.5 132.20221 27.010353 169.84393 46.375 C 173.33626 8.6271057 217.1085 14.754013 216.82236 46.375 C 244.26889 5.931366 279.34393 86.575165 255.81749 127.01879 C 284.04803 146.62691 255.46124 252.27281 232.29251 234.625 C 230.43831 264.03967 189.01958 274.33319 185.38417 234.625 C 161.930725 277.03143 113.02657 211.82918 146.38904 153.98122 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="179.355" y="150" width="67" height="69" fill="url(#Obj_Gradient_15)"/><rect x="179.355" y="150" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 192.524 184.5 L 202.68925 166.5 L 223.01976 166.5 L 233.18501 184.5 L 223.01976 202.5 L 202.68925 202.5 Z" fill="url(#Obj_Gradient_16)"/><path d="M 192.524 184.5 L 202.68925 166.5 L 223.01976 166.5 L 233.18501 184.5 L 223.01976 202.5 L 202.68925 202.5 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 69.855003 115 L 90.855003 115 C 93.339005 115 95.355003 120.376 95.355003 127 C 95.355003 133.62399 93.339005 139 90.855003 139 L 69.855003 139 C 67.371002 139 65.355003 133.62399 65.355003 127 C 65.355003 120.376 67.371002 115 69.855003 115" fill="url(#Obj_Gradient_17)"/><path d="M 69.855003 115 L 90.855003 115 C 93.339005 115 95.355003 120.376 95.355003 127 C 95.355003 133.62399 93.339005 139 90.855003 139 L 69.855003 139 C 67.371002 139 65.355003 133.62399 65.355003 127 C 65.355003 120.376 67.371002 115 69.855003 115" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="161.855" y="64" width="67" height="69" fill="url(#Obj_Gradient_18)"/><rect x="161.855" y="64" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 175.024 100 L 185.18925 82 L 205.51976 82 L 215.68501 100 L 205.51976 118 L 185.18925 118 Z" fill="url(#Obj_Gradient_19)"/><path d="M 175.024 100 L 185.18925 82 L 205.51976 82 L 215.68501 100 L 205.51976 118 L 185.18925 118 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 88 139 C 101.11702 162.9976 109.51097 202.49907 127.355 211 C 141.45248 217.71606 161.45502 205.08597 180.36313 196.03654" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><path d="M 222.38084 166.05576 C 227.03842 157.03807 238.59448 147.85422 236.355 139 C 234.66139 132.304 225.07715 125.79347 216.5538 119.25858" marker-end="url(#Arrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(286 293)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".16308594" y="11" textLength="6.673828">4</tspan></text><text transform="translate(5.8400302 6)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".16308594" y="11" textLength="6.673828">1</tspan></text><text transform="translate(5.840027 287)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".16308594" y="11" textLength="6.673828">3</tspan></text><rect x="0" y="287" width="278" height="273" fill="white"/><rect x="0" y="287" width="278" height="273" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 126.32403 436.9812 C 102.72751 423.5 112.13721 310.01035 149.77893 329.375 C 153.27126 291.6271 197.0435 297.75403 196.75735 329.375 C 224.20389 288.93137 259.27893 369.57516 235.75249 410.0188 C 263.98303 429.62692 235.39624 535.27283 212.22751 517.625 C 210.37331 547.03967 168.95457 557.3332 165.31917 517.625 C 141.86572 560.03143 92.96157 494.82916 126.32403 436.9812 Z" fill="url(#Obj_Gradient_20)"/><path d="M 126.32403 436.9812 C 102.72751 423.5 112.13721 310.01035 149.77893 329.375 C 153.27126 291.6271 197.0435 297.75403 196.75735 329.375 C 224.20389 288.93137 259.27893 369.57516 235.75249 410.0188 C 263.98303 429.62692 235.39624 535.27283 212.22751 517.625 C 210.37331 547.03967 168.95457 557.3332 165.31917 517.625 C 141.86572 560.03143 92.96157 494.82916 126.32403 436.9812 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="138.869995" y="346" width="67" height="69" fill="url(#Obj_Gradient_21)"/><rect x="138.869995" y="346" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 152.039 378.5 L 162.20425 360.5 L 182.53476 360.5 L 192.70001 378.5 L 182.53476 396.5 L 162.20425 396.5 Z" fill="url(#Obj_Gradient_22)"/><path d="M 152.039 378.5 L 162.20425 360.5 L 182.53476 360.5 L 192.70001 378.5 L 182.53476 396.5 L 162.20425 396.5 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 62 379 L 83 379 C 85.484 379 87.5 384.376 87.5 391 C 87.5 397.624 85.484 403 83 403 L 62 403 C 59.516 403 57.5 397.624 57.5 391 C 57.5 384.376 59.516 379 62 379" fill="url(#Obj_Gradient_23)"/><path d="M 62 379 L 83 379 C 85.484 379 87.5 384.376 87.5 391 C 87.5 397.624 85.484 403 83 403 L 62 403 C 59.516 403 57.5 397.624 57.5 391 C 57.5 384.376 59.516 379 62 379" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="147.453" y="435.5" width="67" height="69" fill="url(#Obj_Gradient_24)"/><rect x="147.453" y="435.5" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 160.62201 470 L 170.78726 452 L 191.11777 452 L 201.28302 470 L 191.11777 488 L 170.78726 488 Z" fill="url(#Obj_Gradient_25)"/><path d="M 160.62201 470 L 170.78726 452 L 191.11777 452 L 201.28302 470 L 191.11777 488 L 170.78726 488 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 181.17079 451.50006 C 181.27284 442.8504 182.34718 434.63248 181.47696 425.54852 C 180.89874 419.51263 179.46176 413.09204 178.022 406.67056" marker-end="url(#Arrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 162.48625 439.34933 L 182.67911 418.90622 C 182.67911 418.90622 176.37192 410.54022 180.79669 410.70734 C 185.22139 410.87448 187.68919 413.07565 187.68919 413.07565 L 183.62592 416.69077 L 189.96997 423.03482 L 193.83488 419.61807 C 193.83488 419.61807 196.0623 421.44876 196.25372 421.64017 C 196.44499 421.83145 196.1347 425.18307 195.37999 426.3204 C 194.62503 427.45789 187.93712 423.79865 187.93724 423.79877 C 187.93724 423.79877 166.50412 443.3672 166.44502 443.4263 C 166.38591 443.4854 165.92685 444.32736 163.88141 442.16357 C 161.83583 439.99976 162.48625 439.34933 162.48625 439.34933 Z" fill="url(#Obj_Gradient_26)"/><path d="M 162.48625 439.34933 L 182.67911 418.90622 C 182.67911 418.90622 176.37192 410.54022 180.79669 410.70734 C 185.22139 410.87448 187.68919 413.07565 187.68919 413.07565 L 183.62592 416.69077 L 189.96997 423.03482 L 193.83488 419.61807 C 193.83488 419.61807 196.0623 421.44876 196.25372 421.64017 C 196.44499 421.83145 196.1347 425.18307 195.37999 426.3204 C 194.62503 427.45789 187.93712 423.79865 187.93724 423.79877 C 187.93724 423.79877 166.50412 443.3672 166.44502 443.4263 C 166.38591 443.4854 165.92685 444.32736 163.88141 442.16357 C 161.83583 439.99976 162.48625 439.34933 162.48625 439.34933 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 75.526443 403.48596 C 80.05397 422.16476 74.80702 448.81583 89.11038 459.52802 C 100.46647 468.0329 124.151474 466.49478 145.480865 466.87387" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><text transform="translate(10 297)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".16308594" y="11" textLength="6.673828">3</tspan></text></g></g></svg>
diff --git a/qpid/doc/book/src/java-broker/images/HA-2N-Normal.png b/qpid/doc/book/src/java-broker/images/HA-2N-Normal.png
new file mode 100644
index 0000000000..eeaad4f230
--- /dev/null
+++ b/qpid/doc/book/src/java-broker/images/HA-2N-Normal.png
Binary files differ
diff --git a/qpid/doc/book/src/java-broker/images/HA-2N-Normal.svg b/qpid/doc/book/src/java-broker/images/HA-2N-Normal.svg
new file mode 100644
index 0000000000..c4fac9d37a
--- /dev/null
+++ b/qpid/doc/book/src/java-broker/images/HA-2N-Normal.svg
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="-11 -10 318 313" width="318pt" height="313pt"><metadata xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:date>2012-05-31 06:19Z</dc:date><!-- Produced by OmniGraffle Professional 5.3.6 --></metadata><defs><radialGradient cx="0" cy="0" r="1" id="Gradient" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset=".5" stop-color="#b5d0ea"/><stop offset="1" stop-color="#aaa"/></radialGradient><radialGradient id="Obj_Gradient" xl:href="#Gradient" gradientTransform="translate(209.35501 142.5) scale(145.18695)"/><linearGradient x1="0" x2="1" id="Gradient_2" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#2824aa"/></linearGradient><linearGradient id="Obj_Gradient_2" xl:href="#Gradient_2" gradientTransform="translate(221.85501 152) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_3" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#0caa25"/></linearGradient><linearGradient id="Obj_Gradient_3" xl:href="#Gradient_3" gradientTransform="translate(221.85452 168.5) rotate(90) scale(36)"/><linearGradient x1="0" x2="1" id="Gradient_4" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#6923aa"/></linearGradient><linearGradient id="Obj_Gradient_4" xl:href="#Gradient_4" gradientTransform="translate(71.35501 115) rotate(90) scale(24)"/><linearGradient id="Obj_Gradient_5" xl:href="#Gradient_2" gradientTransform="translate(204.35501 66) rotate(90) scale(69.000007)"/><linearGradient x1="0" x2="1" id="Gradient_5" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#aa172d"/></linearGradient><linearGradient id="Obj_Gradient_6" xl:href="#Gradient_5" gradientTransform="translate(204.35452 84) rotate(90) scale(36)"/><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -3 6 6" markerWidth="6" markerHeight="6" color="black"><g><path d="M 3.7333333 0 L 0 -1.4 L 0 1.4 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="Arrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><g><title>Layer 1</title><rect x="9" y="6" width="278" height="273" fill="white"/><rect x="9" y="6" width="278" height="273" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 155.38904 155.98122 C 131.79251 142.5 141.20221 29.010353 178.84393 48.375 C 182.33626 10.627106 226.1085 16.754013 225.82236 48.375 C 253.26889 7.931366 288.34393 88.575165 264.8175 129.01878 C 293.04803 148.62691 264.46124 254.27281 241.29251 236.625 C 239.43831 266.03967 198.01958 276.33319 194.38417 236.625 C 170.93073 279.03143 122.02657 213.82918 155.38904 155.98122 Z" fill="url(#Obj_Gradient)"/><path d="M 155.38904 155.98122 C 131.79251 142.5 141.20221 29.010353 178.84393 48.375 C 182.33626 10.627106 226.1085 16.754013 225.82236 48.375 C 253.26889 7.931366 288.34393 88.575165 264.8175 129.01878 C 293.04803 148.62691 264.46124 254.27281 241.29251 236.625 C 239.43831 266.03967 198.01958 276.33319 194.38417 236.625 C 170.93073 279.03143 122.02657 213.82918 155.38904 155.98122 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="188.35501" y="152" width="67" height="69" fill="url(#Obj_Gradient_2)"/><rect x="188.35501" y="152" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 201.52402 186.5 L 211.68927 168.5 L 232.01978 168.5 L 242.18503 186.5 L 232.01978 204.5 L 211.68927 204.5 Z" fill="url(#Obj_Gradient_3)"/><path d="M 201.52402 186.5 L 211.68927 168.5 L 232.01978 168.5 L 242.18503 186.5 L 232.01978 204.5 L 211.68927 204.5 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 60.85501 115 L 81.85501 115 C 84.33901 115 86.35501 120.376 86.35501 127 C 86.35501 133.62399 84.33901 139 81.85501 139 L 60.85501 139 C 58.37101 139 56.35501 133.62399 56.35501 127 C 56.35501 120.376 58.37101 115 60.85501 115" fill="url(#Obj_Gradient_4)"/><path d="M 60.85501 115 L 81.85501 115 C 84.33901 115 86.35501 120.376 86.35501 127 C 86.35501 133.62399 84.33901 139 81.85501 139 L 60.85501 139 C 58.37101 139 56.35501 133.62399 56.35501 127 C 56.35501 120.376 58.37101 115 60.85501 115" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="170.85501" y="66" width="67" height="69" fill="url(#Obj_Gradient_5)"/><rect x="170.85501" y="66" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 184.02402 102 L 194.18927 84 L 214.51978 84 L 224.68503 102 L 214.51978 120 L 194.18927 120 Z" fill="url(#Obj_Gradient_6)"/><path d="M 184.02402 102 L 194.18927 84 L 214.51978 84 L 224.68503 102 L 214.51978 120 L 194.18927 120 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 83.35501 143 C 101.01991 166.33099 116.23704 204.83237 136.35501 213 C 152.19005 219.42882 171.06885 207.06767 189.39609 198.1017" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><path d="M 231.38086 168.05576 C 236.03844 159.03807 247.59447 149.85422 245.35498 141 C 243.66136 134.304 234.07715 127.79349 225.55383 121.2586" marker-end="url(#Arrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g></g></svg>
diff --git a/qpid/doc/book/src/java-broker/images/HA-2N-ReplicaFail.png b/qpid/doc/book/src/java-broker/images/HA-2N-ReplicaFail.png
new file mode 100644
index 0000000000..769fc959fc
--- /dev/null
+++ b/qpid/doc/book/src/java-broker/images/HA-2N-ReplicaFail.png
Binary files differ
diff --git a/qpid/doc/book/src/java-broker/images/HA-2N-ReplicaFail.svg b/qpid/doc/book/src/java-broker/images/HA-2N-ReplicaFail.svg
new file mode 100644
index 0000000000..aa872a30e3
--- /dev/null
+++ b/qpid/doc/book/src/java-broker/images/HA-2N-ReplicaFail.svg
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="-20 -16 600 590" width="50pc" height="590pt"><metadata xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:date>2012-05-31 06:19Z</dc:date><!-- Produced by OmniGraffle Professional 5.3.6 --></metadata><defs><radialGradient cx="0" cy="0" r="1" id="Gradient" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset=".5" stop-color="#b5d0ea"/><stop offset="1" stop-color="#aaa"/></radialGradient><radialGradient id="Obj_Gradient" xl:href="#Gradient" gradientTransform="translate(470.5 133.5) scale(145.18695)"/><linearGradient x1="0" x2="1" id="Gradient_2" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#2824aa"/></linearGradient><linearGradient id="Obj_Gradient_2" xl:href="#Gradient_2" gradientTransform="translate(488 141.5) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_3" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#6cad67"/></linearGradient><linearGradient id="Obj_Gradient_3" xl:href="#Gradient_3" gradientTransform="translate(487.9995 158) rotate(90) scale(36)"/><linearGradient x1="0" x2="1" id="Gradient_4" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#6923aa"/></linearGradient><linearGradient id="Obj_Gradient_4" xl:href="#Gradient_4" gradientTransform="translate(350.21 85) rotate(90) scale(24)"/><linearGradient id="Obj_Gradient_5" xl:href="#Gradient_2" gradientTransform="translate(470.5 55.5) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_5" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#aaaaa3"/></linearGradient><linearGradient id="Obj_Gradient_6" xl:href="#Gradient_5" gradientTransform="translate(470.4995 73.5) rotate(90) scale(36)"/><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -3 6 6" markerWidth="6" markerHeight="6" color="black"><g><path d="M 3.7333333 0 L 0 -1.4 L 0 1.4 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><radialGradient cx="0" cy="0" r="1" id="Gradient_6" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset=".5" stop-color="#ea061f"/><stop offset="1" stop-color="#aaa"/></radialGradient><radialGradient id="Obj_Gradient_7" xl:href="#Gradient_6" gradientTransform="translate(472.145 91.5) scale(38.183766)"/><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><radialGradient id="Obj_Gradient_8" xl:href="#Gradient" gradientTransform="translate(474.08 415.5) scale(145.18695)"/><linearGradient id="Obj_Gradient_9" xl:href="#Gradient_2" gradientTransform="translate(466.66 333.723) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_7" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#aa1f43"/></linearGradient><linearGradient id="Obj_Gradient_10" xl:href="#Gradient_7" gradientTransform="translate(466.65952 350.223) rotate(90) scale(36)"/><linearGradient id="Obj_Gradient_11" xl:href="#Gradient_4" gradientTransform="translate(353.79 367) rotate(90) scale(24.00003)"/><linearGradient id="Obj_Gradient_12" xl:href="#Gradient_2" gradientTransform="translate(474.08 428) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_8" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#21aa1a"/></linearGradient><linearGradient id="Obj_Gradient_13" xl:href="#Gradient_8" gradientTransform="translate(474.0795 446) rotate(90) scale(36)"/><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="Arrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><radialGradient id="Obj_Gradient_14" xl:href="#Gradient" gradientTransform="translate(180.29001 409.5) scale(145.18695)"/><linearGradient id="Obj_Gradient_15" xl:href="#Gradient_2" gradientTransform="translate(172.37 332) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_9" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#aaa3a7"/></linearGradient><linearGradient id="Obj_Gradient_16" xl:href="#Gradient_9" gradientTransform="translate(172.36951 346.5) rotate(90) scale(36)"/><linearGradient id="Obj_Gradient_17" xl:href="#Gradient_4" gradientTransform="translate(72.5 365) rotate(90) scale(24)"/><linearGradient id="Obj_Gradient_18" xl:href="#Gradient_2" gradientTransform="translate(180.953 421.5) rotate(90) scale(69)"/><linearGradient id="Obj_Gradient_19" xl:href="#Gradient_8" gradientTransform="translate(180.9525 438) rotate(90) scale(36)"/><linearGradient x1="0" x2="1" id="Gradient_10" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#b5011d"/></linearGradient><linearGradient id="Obj_Gradient_20" xl:href="#Gradient_10" gradientTransform="translate(134.69392 361.18848) rotate(45) scale(23.213103)"/><radialGradient id="Obj_Gradient_21" xl:href="#Gradient" gradientTransform="translate(200.35501 136.5) scale(145.18695)"/><linearGradient id="Obj_Gradient_22" xl:href="#Gradient_2" gradientTransform="translate(212.855 146) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_11" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#0caa25"/></linearGradient><linearGradient id="Obj_Gradient_23" xl:href="#Gradient_11" gradientTransform="translate(212.85451 162.5) rotate(90) scale(36)"/><linearGradient id="Obj_Gradient_24" xl:href="#Gradient_4" gradientTransform="translate(80.355003 111) rotate(90) scale(24)"/><linearGradient id="Obj_Gradient_25" xl:href="#Gradient_2" gradientTransform="translate(195.355 59.999996) rotate(90) scale(69.000007)"/><linearGradient x1="0" x2="1" id="Gradient_12" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#aa172d"/></linearGradient><linearGradient id="Obj_Gradient_26" xl:href="#Gradient_12" gradientTransform="translate(195.35451 78) rotate(90) scale(36)"/></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 4</title><g><title>Layer 1</title><rect x="281.29001" y="0" width="278.00003" height="273" fill="white"/><rect x="281.29001" y="0" width="278.00003" height="273" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 416.53403 146.98122 C 392.9375 133.5 402.3472 20.010353 439.98892 39.375 C 443.48123 1.6271057 487.2535 7.754013 486.96735 39.375 C 514.41388 -1.06863403 549.4889 79.575165 525.96246 120.01879 C 554.19305 139.62691 525.60626 245.27281 502.4375 227.625 C 500.5833 257.03967 459.16455 267.33319 455.52917 227.625 C 432.07571 270.03143 383.17157 204.82918 416.53403 146.98122 Z" fill="url(#Obj_Gradient)"/><path d="M 416.53403 146.98122 C 392.9375 133.5 402.3472 20.010353 439.98892 39.375 C 443.48123 1.6271057 487.2535 7.754013 486.96735 39.375 C 514.41388 -1.06863403 549.4889 79.575165 525.96246 120.01879 C 554.19305 139.62691 525.60626 245.27281 502.4375 227.625 C 500.5833 257.03967 459.16455 267.33319 455.52917 227.625 C 432.07571 270.03143 383.17157 204.82918 416.53403 146.98122 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="454.5" y="141.5" width="67" height="69" fill="url(#Obj_Gradient_2)"/><rect x="454.5" y="141.5" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 467.669 176 L 477.83426 158 L 498.16476 158 L 508.33002 176 L 498.16476 194 L 477.83426 194 Z" fill="url(#Obj_Gradient_3)"/><path d="M 467.669 176 L 477.83426 158 L 498.16476 158 L 508.33002 176 L 498.16476 194 L 477.83426 194 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 339.71 85 L 360.71 85 C 363.194 85 365.21 90.376 365.21 97 C 365.21 103.624 363.194 109 360.71 109 L 339.71 109 C 337.22598 109 335.21 103.624 335.21 97 C 335.21 90.376 337.22598 85 339.71 85" fill="url(#Obj_Gradient_4)"/><path d="M 339.71 85 L 360.71 85 C 363.194 85 365.21 90.376 365.21 97 C 365.21 103.624 363.194 109 360.71 109 L 339.71 109 C 337.22598 109 335.21 103.624 335.21 97 C 335.21 90.376 337.22598 85 339.71 85" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="437" y="55.5" width="67" height="69" fill="url(#Obj_Gradient_5)"/><rect x="437" y="55.5" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 450.169 91.5 L 460.33426 73.5 L 480.66476 73.5 L 490.83002 91.5 L 480.66476 109.5 L 460.33426 109.5 Z" fill="url(#Obj_Gradient_6)"/><path d="M 450.169 91.5 L 460.33426 73.5 L 480.66476 73.5 L 490.83002 91.5 L 480.66476 109.5 L 460.33426 109.5 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 355.0216 109.46646 C 360.75049 124.30949 353.19086 143.50909 372.21 154 C 388.3057 162.87834 423.4454 165.52399 453.4511 169.84836" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><path d="M 445.145 78.46559 L 465.66495 93.362083 L 449.3803 112.107697 L 455.94482 118.5 L 473.73325 98.22214 L 492.66498 111.982796 L 499.145 104.53463 L 478.62488 88.706955 L 495.90488 71.948326 L 486.18488 64.5 L 471.06485 83.12071 L 453.78497 68.224167 Z" fill="url(#Obj_Gradient_7)"/><path d="M 445.145 78.46559 L 465.66495 93.362083 L 449.3803 112.107697 L 455.94482 118.5 L 473.73325 98.22214 L 492.66498 111.982796 L 499.145 104.53463 L 478.62488 88.706955 L 495.90488 71.948326 L 486.18488 64.5 L 471.06485 83.12071 L 453.78497 68.224167 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(286 6)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".16308594" y="11" textLength="6.673828">2</tspan></text><rect x="281.29001" y="277" width="278.00003" height="273" fill="white"/><rect x="281.29001" y="277" width="278.00003" height="273" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 420.11401 428.9812 C 396.51749 415.5 405.92719 302.01035 443.5689 321.375 C 447.06122 283.6271 490.8335 289.75403 490.54733 321.375 C 517.9939 280.93137 553.0689 361.57516 529.54248 402.0188 C 557.773 421.62692 529.18622 527.27283 506.0175 509.625 C 504.1633 539.03967 462.74454 549.3332 459.10913 509.625 C 435.6557 552.03143 386.75156 486.82916 420.11401 428.9812 Z" fill="url(#Obj_Gradient_8)"/><path d="M 420.11401 428.9812 C 396.51749 415.5 405.92719 302.01035 443.5689 321.375 C 447.06122 283.6271 490.8335 289.75403 490.54733 321.375 C 517.9939 280.93137 553.0689 361.57516 529.54248 402.0188 C 557.773 421.62692 529.18622 527.27283 506.0175 509.625 C 504.1633 539.03967 462.74454 549.3332 459.10913 509.625 C 435.6557 552.03143 386.75156 486.82916 420.11401 428.9812 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="433.16" y="333.723" width="67" height="69" fill="url(#Obj_Gradient_9)"/><rect x="433.16" y="333.723" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 446.329 368.223 L 456.49426 350.223 L 476.82477 350.223 L 486.99002 368.223 L 476.82477 386.223 L 456.49426 386.223 Z" fill="url(#Obj_Gradient_10)"/><path d="M 446.329 368.223 L 456.49426 350.223 L 476.82477 350.223 L 486.99002 368.223 L 476.82477 386.223 L 456.49426 386.223 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 343.29 367 L 364.29 367 C 366.77402 367 368.79 372.376 368.79 379 C 368.79 385.624 366.77402 391 364.29 391 L 343.29 391 C 340.806 391 338.79 385.624 338.79 379 C 338.79 372.376 340.806 367 343.29 367" fill="url(#Obj_Gradient_11)"/><path d="M 343.29 367 L 364.29 367 C 366.77402 367 368.79 372.376 368.79 379 C 368.79 385.624 366.77402 391 364.29 391 L 343.29 391 C 340.806 391 338.79 385.624 338.79 379 C 338.79 372.376 340.806 367 343.29 367" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="440.57999" y="428" width="67" height="69" fill="url(#Obj_Gradient_12)"/><rect x="440.57999" y="428" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 453.749 464 L 463.91425 446 L 484.24475 446 L 494.41 464 L 484.24475 482 L 463.91425 482 Z" fill="url(#Obj_Gradient_13)"/><path d="M 453.749 464 L 463.91425 446 L 484.24475 446 L 494.41 464 L 484.24475 482 L 463.91425 482 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 358.11069 391.47244 C 364.4065 409.64648 360.82266 434.49713 377 446 C 390.09668 455.31238 416.1513 455.88107 439.42624 458.3237" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><path d="M 480.27567 445.52594 C 482.57022 438.68463 488.31775 434.80447 487.16 425 C 486.26974 417.46088 481.2962 406.41544 476.94943 395.90878" marker-end="url(#Arrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="0" y="277" width="278" height="273" fill="white"/><rect x="0" y="277" width="278" height="273" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 126.32403 422.9812 C 102.72751 409.5 112.13721 296.01035 149.77893 315.375 C 153.27126 277.6271 197.0435 283.75403 196.75735 315.375 C 224.20389 274.93137 259.27893 355.57516 235.75249 396.0188 C 263.98303 415.62692 235.39624 521.27283 212.22751 503.625 C 210.37331 533.03967 168.95457 543.3332 165.31917 503.625 C 141.86572 546.03143 92.96157 480.82916 126.32403 422.9812 Z" fill="url(#Obj_Gradient_14)"/><path d="M 126.32403 422.9812 C 102.72751 409.5 112.13721 296.01035 149.77893 315.375 C 153.27126 277.6271 197.0435 283.75403 196.75735 315.375 C 224.20389 274.93137 259.27893 355.57516 235.75249 396.0188 C 263.98303 415.62692 235.39624 521.27283 212.22751 503.625 C 210.37331 533.03967 168.95457 543.3332 165.31917 503.625 C 141.86572 546.03143 92.96157 480.82916 126.32403 422.9812 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="138.869995" y="332" width="67" height="69" fill="url(#Obj_Gradient_15)"/><rect x="138.869995" y="332" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 152.039 364.5 L 162.20425 346.5 L 182.53476 346.5 L 192.70001 364.5 L 182.53476 382.5 L 162.20425 382.5 Z" fill="url(#Obj_Gradient_16)"/><path d="M 152.039 364.5 L 162.20425 346.5 L 182.53476 346.5 L 192.70001 364.5 L 182.53476 382.5 L 162.20425 382.5 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 62 365 L 83 365 C 85.484 365 87.5 370.376 87.5 377 C 87.5 383.624 85.484 389 83 389 L 62 389 C 59.516 389 57.5 383.624 57.5 377 C 57.5 370.376 59.516 365 62 365" fill="url(#Obj_Gradient_17)"/><path d="M 62 365 L 83 365 C 85.484 365 87.5 370.376 87.5 377 C 87.5 383.624 85.484 389 83 389 L 62 389 C 59.516 389 57.5 383.624 57.5 377 C 57.5 370.376 59.516 365 62 365" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="147.453" y="421.5" width="67" height="69" fill="url(#Obj_Gradient_18)"/><rect x="147.453" y="421.5" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 160.62199 456 L 170.78725 438 L 191.11775 438 L 201.283 456 L 191.11775 474 L 170.78725 474 Z" fill="url(#Obj_Gradient_19)"/><path d="M 160.62199 456 L 170.78725 438 L 191.11775 438 L 201.283 456 L 191.11775 474 L 170.78725 474 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 75.38166 389.48718 C 79.4207 406.98972 73.112816 431.39597 87.5 442 C 99.01256 450.4853 123.781944 450.13696 145.894165 451.55768" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><path d="M 118.20667 389.5221 L 154.46504 352.99951 C 154.46504 352.99951 148.56358 343.41068 154.80022 342.02164 C 161.03673 340.63266 163.73999 342.85837 163.73999 342.85837 L 156.62726 349.49799 L 163.3246 356.19534 L 170.08206 349.91098 C 170.08206 349.91098 172.57938 351.69775 172.78146 351.89984 C 172.98338 352.10178 171.3094 356.98645 169.817 358.88278 C 168.32417 360.77942 160.15027 358.03 160.15039 358.03012 C 160.15039 358.03012 122.44828 393.7637 122.342445 393.86954 C 122.23656 393.9754 121.27359 395.3426 119.157745 393.0148 C 117.04174 390.68701 118.20667 389.5221 118.20667 389.5221 Z" fill="url(#Obj_Gradient_20)"/><path d="M 118.20667 389.5221 L 154.46504 352.99951 C 154.46504 352.99951 148.56358 343.41068 154.80022 342.02164 C 161.03673 340.63266 163.73999 342.85837 163.73999 342.85837 L 156.62726 349.49799 L 163.3246 356.19534 L 170.08206 349.91098 C 170.08206 349.91098 172.57938 351.69775 172.78146 351.89984 C 172.98338 352.10178 171.3094 356.98645 169.817 358.88278 C 168.32417 360.77942 160.15027 358.03 160.15039 358.03012 C 160.15039 358.03012 122.44828 393.7637 122.342445 393.86954 C 122.23656 393.9754 121.27359 395.3426 119.157745 393.0148 C 117.04174 390.68701 118.20667 389.5221 118.20667 389.5221 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="0" y="0" width="278" height="273" fill="white"/><rect x="0" y="0" width="278" height="273" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 146.38904 149.98122 C 122.79251 136.5 132.20221 23.010353 169.84393 42.375 C 173.33626 4.6271057 217.1085 10.754013 216.82236 42.375 C 244.26889 1.931366 279.34393 82.575165 255.81749 123.01879 C 284.04803 142.62691 255.46124 248.27281 232.29251 230.625 C 230.43831 260.03967 189.01958 270.33319 185.38417 230.625 C 161.930725 273.03143 113.02657 207.82918 146.38904 149.98122 Z" fill="url(#Obj_Gradient_21)"/><path d="M 146.38904 149.98122 C 122.79251 136.5 132.20221 23.010353 169.84393 42.375 C 173.33626 4.6271057 217.1085 10.754013 216.82236 42.375 C 244.26889 1.931366 279.34393 82.575165 255.81749 123.01879 C 284.04803 142.62691 255.46124 248.27281 232.29251 230.625 C 230.43831 260.03967 189.01958 270.33319 185.38417 230.625 C 161.930725 273.03143 113.02657 207.82918 146.38904 149.98122 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="179.355" y="146" width="67" height="69" fill="url(#Obj_Gradient_22)"/><rect x="179.355" y="146" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 192.524 180.5 L 202.68925 162.5 L 223.01976 162.5 L 233.18501 180.5 L 223.01976 198.5 L 202.68925 198.5 Z" fill="url(#Obj_Gradient_23)"/><path d="M 192.524 180.5 L 202.68925 162.5 L 223.01976 162.5 L 233.18501 180.5 L 223.01976 198.5 L 202.68925 198.5 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 69.855003 111 L 90.855003 111 C 93.339005 111 95.355003 116.376 95.355003 123 C 95.355003 129.62399 93.339005 135 90.855003 135 L 69.855003 135 C 67.371002 135 65.355003 129.62399 65.355003 123 C 65.355003 116.376 67.371002 111 69.855003 111" fill="url(#Obj_Gradient_24)"/><path d="M 69.855003 111 L 90.855003 111 C 93.339005 111 95.355003 116.376 95.355003 123 C 95.355003 129.62399 93.339005 135 90.855003 135 L 69.855003 135 C 67.371002 135 65.355003 129.62399 65.355003 123 C 65.355003 116.376 67.371002 111 69.855003 111" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="161.855" y="60" width="67" height="69" fill="url(#Obj_Gradient_25)"/><rect x="161.855" y="60" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 175.024 96 L 185.18925 78 L 205.51976 78 L 215.68501 96 L 205.51976 114 L 185.18925 114 Z" fill="url(#Obj_Gradient_26)"/><path d="M 175.024 96 L 185.18925 78 L 205.51976 78 L 215.68501 96 L 205.51976 114 L 185.18925 114 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 74.355003 137 C 92.019905 160.33099 107.23704 198.83237 127.355 207 C 143.19005 213.42882 162.06882 201.06767 180.39609 192.10172" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><path d="M 222.38084 162.05576 C 227.03842 153.03807 238.59448 143.85422 236.355 135 C 234.66139 128.303986 225.07715 121.79347 216.5538 115.25858" marker-end="url(#Arrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(5.8400302 4)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".16308594" y="11" textLength="6.673828">1</tspan></text><text transform="translate(8 286)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".16308594" y="11" textLength="6.673828">3</tspan></text><text transform="translate(286 286)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".16308594" y="11" textLength="6.673828">4</tspan></text></g></g></svg>
diff --git a/qpid/doc/book/src/java-broker/images/HA-2N-SplitBrain.png b/qpid/doc/book/src/java-broker/images/HA-2N-SplitBrain.png
new file mode 100644
index 0000000000..74393648a5
--- /dev/null
+++ b/qpid/doc/book/src/java-broker/images/HA-2N-SplitBrain.png
Binary files differ
diff --git a/qpid/doc/book/src/java-broker/images/HA-2N-SplitBrain.svg b/qpid/doc/book/src/java-broker/images/HA-2N-SplitBrain.svg
new file mode 100644
index 0000000000..c82cc5065a
--- /dev/null
+++ b/qpid/doc/book/src/java-broker/images/HA-2N-SplitBrain.svg
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="-20 -16 598 591" width="598pt" height="591pt"><metadata xmlns:dc="http://purl.org/dc/elements/1.1/"><dc:date>2012-05-31 06:19Z</dc:date><!-- Produced by OmniGraffle Professional 5.3.6 --></metadata><defs><radialGradient cx="0" cy="0" r="1" id="Gradient" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset=".5" stop-color="#b5d0ea"/><stop offset="1" stop-color="#aaa"/></radialGradient><radialGradient id="Obj_Gradient" xl:href="#Gradient" gradientTransform="translate(200.35501 136.5) scale(145.18695)"/><linearGradient x1="0" x2="1" id="Gradient_2" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#2824aa"/></linearGradient><linearGradient id="Obj_Gradient_2" xl:href="#Gradient_2" gradientTransform="translate(212.855 146) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_3" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#0caa25"/></linearGradient><linearGradient id="Obj_Gradient_3" xl:href="#Gradient_3" gradientTransform="translate(212.85451 162.5) rotate(90) scale(36)"/><linearGradient x1="0" x2="1" id="Gradient_4" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#6923aa"/></linearGradient><linearGradient id="Obj_Gradient_4" xl:href="#Gradient_4" gradientTransform="translate(80.355003 111) rotate(90) scale(24)"/><linearGradient id="Obj_Gradient_5" xl:href="#Gradient_2" gradientTransform="translate(195.355 59.999996) rotate(90) scale(69.000007)"/><linearGradient x1="0" x2="1" id="Gradient_5" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#aa172d"/></linearGradient><linearGradient id="Obj_Gradient_6" xl:href="#Gradient_5" gradientTransform="translate(195.35451 78) rotate(90) scale(36)"/><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -3 6 6" markerWidth="6" markerHeight="6" color="black"><g><path d="M 3.7333333 0 L 0 -1.4 L 0 1.4 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="Arrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><radialGradient id="Obj_Gradient_7" xl:href="#Gradient" gradientTransform="translate(468.855 133.5) scale(145.18695)"/><linearGradient id="Obj_Gradient_8" xl:href="#Gradient_2" gradientTransform="translate(486.355 141.5) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_6" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#6ead6a"/></linearGradient><linearGradient id="Obj_Gradient_9" xl:href="#Gradient_6" gradientTransform="translate(486.3545 158) rotate(90) scale(36)"/><linearGradient id="Obj_Gradient_10" xl:href="#Gradient_4" gradientTransform="translate(348.565 85) rotate(90) scale(24)"/><radialGradient cx="0" cy="0" r="1" id="Gradient_7" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset=".5" stop-color="#ea061f"/><stop offset="1" stop-color="#aaa"/></radialGradient><radialGradient id="Obj_Gradient_11" xl:href="#Gradient_7" gradientTransform="translate(483.56808 132.68761) scale(20.071926)"/><linearGradient id="Obj_Gradient_12" xl:href="#Gradient_2" gradientTransform="translate(465.01501 51.445) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_8" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#b6b4b5"/></linearGradient><linearGradient id="Obj_Gradient_13" xl:href="#Gradient_8" gradientTransform="translate(465.0145 67.945) rotate(90) scale(36)"/><font-face font-family="Arial Unicode MS" font-size="36" panose-1="2 11 6 4 2 2 2 2 2 4" units-per-em="1000" underline-position="-100.097656" underline-thickness="49.804688" slope="0" x-height="529.78516" cap-height="728.02734" ascent="1068.84766" descent="-270.9961" font-weight="500"><font-face-src><font-face-name name="ArialUnicodeMS"/></font-face-src></font-face><radialGradient id="Obj_Gradient_14" xl:href="#Gradient" gradientTransform="translate(180.29001 414.5) scale(145.18695)"/><linearGradient id="Obj_Gradient_15" xl:href="#Gradient_2" gradientTransform="translate(172.37 337) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_9" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#66ab61"/></linearGradient><linearGradient id="Obj_Gradient_16" xl:href="#Gradient_9" gradientTransform="translate(172.36951 351.5) rotate(90) scale(36)"/><linearGradient id="Obj_Gradient_17" xl:href="#Gradient_4" gradientTransform="translate(53.18649 396) rotate(90) scale(24)"/><linearGradient id="Obj_Gradient_18" xl:href="#Gradient_2" gradientTransform="translate(180.953 426.5) rotate(90) scale(69)"/><linearGradient x1="0" x2="1" id="Gradient_10" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="white"/><stop offset="1" stop-color="#21aa1a"/></linearGradient><linearGradient id="Obj_Gradient_19" xl:href="#Gradient_10" gradientTransform="translate(180.9525 443) rotate(90) scale(36)"/><radialGradient id="Obj_Gradient_20" xl:href="#Gradient_7" gradientTransform="translate(185.10121 416.23209) scale(20.071926)"/><font-face font-family="Arial Unicode MS" font-size="72" panose-1="2 11 6 4 2 2 2 2 2 4" units-per-em="1000" underline-position="-100.097656" underline-thickness="49.804688" slope="0" x-height="529.78516" cap-height="728.02734" ascent="1068.84766" descent="-270.9961" font-weight="500"><font-face-src><font-face-name name="ArialUnicodeMS"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 6</title><g><title>Layer 1</title><rect x="0" y="0" width="278" height="273" fill="white"/><rect x="0" y="0" width="278" height="273" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 146.38904 149.98122 C 122.79251 136.5 132.20221 23.010353 169.84393 42.375 C 173.33626 4.6271057 217.1085 10.754013 216.82236 42.375 C 244.26889 1.931366 279.34393 82.575165 255.81749 123.01879 C 284.04803 142.62691 255.46124 248.27281 232.29251 230.625 C 230.43831 260.03967 189.01958 270.33319 185.38417 230.625 C 161.930725 273.03143 113.02657 207.82918 146.38904 149.98122 Z" fill="url(#Obj_Gradient)"/><path d="M 146.38904 149.98122 C 122.79251 136.5 132.20221 23.010353 169.84393 42.375 C 173.33626 4.6271057 217.1085 10.754013 216.82236 42.375 C 244.26889 1.931366 279.34393 82.575165 255.81749 123.01879 C 284.04803 142.62691 255.46124 248.27281 232.29251 230.625 C 230.43831 260.03967 189.01958 270.33319 185.38417 230.625 C 161.930725 273.03143 113.02657 207.82918 146.38904 149.98122 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="179.355" y="146" width="67" height="69" fill="url(#Obj_Gradient_2)"/><rect x="179.355" y="146" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 192.524 180.5 L 202.68925 162.5 L 223.01976 162.5 L 233.18501 180.5 L 223.01976 198.5 L 202.68925 198.5 Z" fill="url(#Obj_Gradient_3)"/><path d="M 192.524 180.5 L 202.68925 162.5 L 223.01976 162.5 L 233.18501 180.5 L 223.01976 198.5 L 202.68925 198.5 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 69.855003 111 L 90.855003 111 C 93.339005 111 95.355003 116.376 95.355003 123 C 95.355003 129.62399 93.339005 135 90.855003 135 L 69.855003 135 C 67.371002 135 65.355003 129.62399 65.355003 123 C 65.355003 116.376 67.371002 111 69.855003 111" fill="url(#Obj_Gradient_4)"/><path d="M 69.855003 111 L 90.855003 111 C 93.339005 111 95.355003 116.376 95.355003 123 C 95.355003 129.62399 93.339005 135 90.855003 135 L 69.855003 135 C 67.371002 135 65.355003 129.62399 65.355003 123 C 65.355003 116.376 67.371002 111 69.855003 111" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="161.855" y="60" width="67" height="69" fill="url(#Obj_Gradient_5)"/><rect x="161.855" y="60" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 175.024 96 L 185.18925 78 L 205.51976 78 L 215.68501 96 L 205.51976 114 L 185.18925 114 Z" fill="url(#Obj_Gradient_6)"/><path d="M 175.024 96 L 185.18925 78 L 205.51976 78 L 215.68501 96 L 205.51976 114 L 185.18925 114 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 83 135 C 87.728355 147.33211 78.882416 164.66258 97.186493 172 C 112.61268 178.18378 147.33044 177.27293 176.94928 178.23598" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><path d="M 222.38084 162.05576 C 227.03842 153.03807 238.59448 143.85422 236.355 135 C 234.66139 128.303986 225.07715 121.79347 216.5538 115.25858" marker-end="url(#Arrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(285 6)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".16308594" y="11" textLength="6.673828">2</tspan></text><rect x="279.64499" y="0" width="278.00003" height="273" fill="white"/><rect x="279.64499" y="0" width="278.00003" height="273" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 414.88904 146.98122 C 391.2925 133.5 400.7022 20.010353 438.34393 39.375 C 441.83624 1.6271057 485.60852 7.754013 485.32236 39.375 C 512.76892 -1.06863403 547.84393 79.575165 524.3175 120.01879 C 552.54803 139.62691 523.96124 245.27281 500.7925 227.625 C 498.93832 257.03967 457.51956 267.33319 453.88416 227.625 C 430.43073 270.03143 381.52658 204.82918 414.88904 146.98122 Z" fill="url(#Obj_Gradient_7)"/><path d="M 414.88904 146.98122 C 391.2925 133.5 400.7022 20.010353 438.34393 39.375 C 441.83624 1.6271057 485.60852 7.754013 485.32236 39.375 C 512.76892 -1.06863403 547.84393 79.575165 524.3175 120.01879 C 552.54803 139.62691 523.96124 245.27281 500.7925 227.625 C 498.93832 257.03967 457.51956 267.33319 453.88416 227.625 C 430.43073 270.03143 381.52658 204.82918 414.88904 146.98122 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="452.855" y="141.5" width="66.99997" height="69" fill="url(#Obj_Gradient_8)"/><rect x="452.855" y="141.5" width="66.99997" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 466.024 176 L 476.18924 158 L 496.51974 158 L 506.685 176 L 496.51974 194 L 476.18924 194 Z" fill="url(#Obj_Gradient_9)"/><path d="M 466.024 176 L 476.18924 158 L 496.51974 158 L 506.685 176 L 496.51974 194 L 476.18924 194 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 338.065 85 L 359.065 85 C 361.54901 85 363.565 90.376 363.565 97 C 363.565 103.624 361.54901 109 359.065 109 L 338.065 109 C 335.581 109 333.565 103.624 333.565 97 C 333.565 90.376 335.581 85 338.065 85" fill="url(#Obj_Gradient_10)"/><path d="M 338.065 85 L 359.065 85 C 361.54901 85 363.565 90.376 363.565 97 C 363.565 103.624 361.54901 109 359.065 109 L 338.065 109 C 335.581 109 333.565 103.624 333.565 97 C 333.565 90.376 335.581 85 338.065 85" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 353.37662 109.46646 C 359.1055 124.30949 351.54587 143.50909 370.565 154 C 386.66068 162.87834 421.80042 165.52399 451.8061 169.84834" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><path d="M 470.9274 125.16071 L 480.5343 133.763016 L 472.91028 144.58757 L 475.98358 148.27911 L 484.31168 136.56943 L 493.17505 144.515656 L 496.20877 140.214615 L 486.60178 131.07477 L 494.69186 121.39724 L 490.1412 117.09611 L 483.06238 127.84885 L 474.9724 119.246674 Z" fill="url(#Obj_Gradient_11)"/><path d="M 470.9274 125.16071 L 480.5343 133.763016 L 472.91028 144.58757 L 475.98358 148.27911 L 484.31168 136.56943 L 493.17505 144.515656 L 496.20877 140.214615 L 486.60178 131.07477 L 494.69186 121.39724 L 490.1412 117.09611 L 483.06238 127.84885 L 474.9724 119.246674 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="431.51501" y="51.445" width="67" height="69" fill="url(#Obj_Gradient_12)"/><rect x="431.51501" y="51.445" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 444.684 85.945 L 454.84924 67.945 L 475.17975 67.945 L 485.345 85.945 L 475.17975 103.945 L 454.84924 103.945 Z" fill="url(#Obj_Gradient_13)"/><path d="M 444.684 85.945 L 454.84924 67.945 L 475.17975 67.945 L 485.345 85.945 L 475.17975 103.945 L 454.84924 103.945 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 485.18884 157.50099 C 484.6743 149.33481 485.7887 141.847626 483.645 133 C 482.1737 126.92762 479.1671 120.211914 476.20593 113.515625" marker-end="url(#Arrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(496 56.5)" fill="#262626"><tspan font-family="Arial Unicode MS" font-size="36" font-weight="500" fill="#262626" x="0" y="38" textLength="36">♛</tspan></text><text transform="translate(6 6)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".16308594" y="11" textLength="6.673828">1</tspan></text><rect x="0" y="278" width="278" height="273" fill="white"/><rect x="0" y="278" width="278" height="273" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 126.32403 427.9812 C 102.72751 414.5 112.13721 301.01035 149.77893 320.375 C 153.27126 282.6271 197.0435 288.75403 196.75735 320.375 C 224.20389 279.93137 259.27893 360.57516 235.75249 401.0188 C 263.98303 420.62692 235.39624 526.27283 212.22751 508.625 C 210.37331 538.03967 168.95457 548.3332 165.31917 508.625 C 141.86572 551.03143 92.96157 485.82916 126.32403 427.9812 Z" fill="url(#Obj_Gradient_14)"/><path d="M 126.32403 427.9812 C 102.72751 414.5 112.13721 301.01035 149.77893 320.375 C 153.27126 282.6271 197.0435 288.75403 196.75735 320.375 C 224.20389 279.93137 259.27893 360.57516 235.75249 401.0188 C 263.98303 420.62692 235.39624 526.27283 212.22751 508.625 C 210.37331 538.03967 168.95457 548.3332 165.31917 508.625 C 141.86572 551.03143 92.96157 485.82916 126.32403 427.9812 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="138.869995" y="337" width="67" height="69" fill="url(#Obj_Gradient_15)"/><rect x="138.869995" y="337" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 152.039 369.5 L 162.20425 351.5 L 182.53476 351.5 L 192.70001 369.5 L 182.53476 387.5 L 162.20425 387.5 Z" fill="url(#Obj_Gradient_16)"/><path d="M 152.039 369.5 L 162.20425 351.5 L 182.53476 351.5 L 192.70001 369.5 L 182.53476 387.5 L 162.20425 387.5 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 42.68649 396 L 63.68649 396 C 66.170486 396 68.186493 401.376 68.186493 408 C 68.186493 414.624 66.170486 420 63.68649 420 L 42.68649 420 C 40.202488 420 38.18649 414.624 38.18649 408 C 38.18649 401.376 40.202488 396 42.68649 396" fill="url(#Obj_Gradient_17)"/><path d="M 42.68649 396 L 63.68649 396 C 66.170486 396 68.186493 401.376 68.186493 408 C 68.186493 414.624 66.170486 420 63.68649 420 L 42.68649 420 C 40.202488 420 38.18649 414.624 38.18649 408 C 38.18649 401.376 40.202488 396 42.68649 396" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="147.453" y="426.5" width="67" height="69" fill="url(#Obj_Gradient_18)"/><rect x="147.453" y="426.5" width="67" height="69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 160.62199 461 L 170.78725 443 L 191.11775 443 L 201.283 461 L 191.11775 479 L 170.78725 479 Z" fill="url(#Obj_Gradient_19)"/><path d="M 160.62199 461 L 170.78725 443 L 191.11775 443 L 201.283 461 L 191.11775 479 L 170.78725 479 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 64.073936 420.37445 C 71.881844 429.24875 71.228394 440.71005 87.5 447 C 100.49186 452.02213 124.27972 453.7499 145.93179 456.12848" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><path d="M 61.40507 395.58307 C 67.269463 386.72293 63.968967 373.3653 79 369 C 90.933815 365.53418 114.428574 367.73508 135.71434 368.85406" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="3"/><text transform="translate(6 286)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".16308594" y="11" textLength="6.673828">3</tspan></text><path d="M 172.46051 408.7052 L 182.06741 417.3075 L 174.4434 428.13205 L 177.51671 431.82358 L 185.8448 420.1139 L 194.70818 428.06015 L 197.74191 423.7591 L 188.1349 414.61926 L 196.22501 404.9417 L 191.67433 400.6406 L 184.59552 411.39334 L 176.50554 402.79114 Z" fill="url(#Obj_Gradient_20)"/><path d="M 172.46051 408.7052 L 182.06741 417.3075 L 174.4434 428.13205 L 177.51671 431.82358 L 185.8448 420.1139 L 194.70818 428.06015 L 197.74191 423.7591 L 188.1349 414.61926 L 196.22501 404.9417 L 191.67433 400.6406 L 184.59552 411.39334 L 176.50554 402.79114 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 186.722 441.046 C 186.20738 432.87949 187.32178 425.39178 185.17799 416.544 C 183.70665 410.47153 180.69995 403.75586 177.73871 397.05966" marker-end="url(#Arrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(19.372978 273)" fill="black"><tspan font-family="Arial Unicode MS" font-size="72" font-weight="500" x="0" y="77" textLength="54">☠</tspan></text></g></g></svg>
diff --git a/qpid/doc/book/src/java-broker/images/HA-BDBHAMessageStore-MBean-jconsole.png b/qpid/doc/book/src/java-broker/images/HA-BDBHAMessageStore-MBean-jconsole.png
new file mode 100644
index 0000000000..6caaacb1e1
--- /dev/null
+++ b/qpid/doc/book/src/java-broker/images/HA-BDBHAMessageStore-MBean-jconsole.png
Binary files differ
diff --git a/qpid/doc/book/src/java-broker/images/HA-perftests-results.png b/qpid/doc/book/src/java-broker/images/HA-perftests-results.png
new file mode 100644
index 0000000000..e8dbb3c123
--- /dev/null
+++ b/qpid/doc/book/src/java-broker/images/HA-perftests-results.png
Binary files differ
diff --git a/qpid/doc/book/src/ACL.xml b/qpid/doc/book/src/old/ACL.xml
index ceb7cecb23..ceb7cecb23 100644
--- a/qpid/doc/book/src/ACL.xml
+++ b/qpid/doc/book/src/old/ACL.xml
diff --git a/qpid/doc/book/src/AMQP-.NET-Messaging-Client.xml b/qpid/doc/book/src/old/AMQP-.NET-Messaging-Client.xml
index 1d4001942b..1d4001942b 100644
--- a/qpid/doc/book/src/AMQP-.NET-Messaging-Client.xml
+++ b/qpid/doc/book/src/old/AMQP-.NET-Messaging-Client.xml
diff --git a/qpid/doc/book/src/AMQP-C++-Messaging-Client.xml b/qpid/doc/book/src/old/AMQP-C++-Messaging-Client.xml
index 73a2cd6c0b..73a2cd6c0b 100644
--- a/qpid/doc/book/src/AMQP-C++-Messaging-Client.xml
+++ b/qpid/doc/book/src/old/AMQP-C++-Messaging-Client.xml
diff --git a/qpid/doc/book/src/AMQP-Java-JMS-Messaging-Client.xml b/qpid/doc/book/src/old/AMQP-Java-JMS-Messaging-Client.xml
index 8c14d67e14..8c14d67e14 100644
--- a/qpid/doc/book/src/AMQP-Java-JMS-Messaging-Client.xml
+++ b/qpid/doc/book/src/old/AMQP-Java-JMS-Messaging-Client.xml
diff --git a/qpid/doc/book/src/AMQP-Messaging-Broker-CPP.xml b/qpid/doc/book/src/old/AMQP-Messaging-Broker-CPP.xml
index b4e0deb13d..b4e0deb13d 100644
--- a/qpid/doc/book/src/AMQP-Messaging-Broker-CPP.xml
+++ b/qpid/doc/book/src/old/AMQP-Messaging-Broker-CPP.xml
diff --git a/qpid/doc/book/src/AMQP-Python-Messaging-Client.xml b/qpid/doc/book/src/old/AMQP-Python-Messaging-Client.xml
index 15baf214ec..15baf214ec 100644
--- a/qpid/doc/book/src/AMQP-Python-Messaging-Client.xml
+++ b/qpid/doc/book/src/old/AMQP-Python-Messaging-Client.xml
diff --git a/qpid/doc/book/src/AMQP-Ruby-Messaging-Client.xml b/qpid/doc/book/src/old/AMQP-Ruby-Messaging-Client.xml
index 45318c0beb..45318c0beb 100644
--- a/qpid/doc/book/src/AMQP-Ruby-Messaging-Client.xml
+++ b/qpid/doc/book/src/old/AMQP-Ruby-Messaging-Client.xml
diff --git a/qpid/doc/book/src/AMQP.xml b/qpid/doc/book/src/old/AMQP.xml
index 1a609649bb..1a609649bb 100644
--- a/qpid/doc/book/src/AMQP.xml
+++ b/qpid/doc/book/src/old/AMQP.xml
diff --git a/qpid/doc/book/src/Binding-URL-Format.xml b/qpid/doc/book/src/old/Binding-URL-Format.xml
index 3d938b740a..3d938b740a 100644
--- a/qpid/doc/book/src/Binding-URL-Format.xml
+++ b/qpid/doc/book/src/old/Binding-URL-Format.xml
diff --git a/qpid/doc/book/src/Book-Info.xml b/qpid/doc/book/src/old/Book-Info.xml
index 2e02fbe8ea..2e02fbe8ea 100644
--- a/qpid/doc/book/src/Book-Info.xml
+++ b/qpid/doc/book/src/old/Book-Info.xml
diff --git a/qpid/doc/book/src/Book.xml b/qpid/doc/book/src/old/Book.xml
index ee69532152..ee69532152 100644
--- a/qpid/doc/book/src/Book.xml
+++ b/qpid/doc/book/src/old/Book.xml
diff --git a/qpid/doc/book/src/Broker-CPP.xml b/qpid/doc/book/src/old/Broker-CPP.xml
index 99584be23d..99584be23d 100644
--- a/qpid/doc/book/src/Broker-CPP.xml
+++ b/qpid/doc/book/src/old/Broker-CPP.xml
diff --git a/qpid/doc/book/src/Broker-Java.xml b/qpid/doc/book/src/old/Broker-Java.xml
index f8ce89b185..f8ce89b185 100644
--- a/qpid/doc/book/src/Broker-Java.xml
+++ b/qpid/doc/book/src/old/Broker-Java.xml
diff --git a/qpid/doc/book/src/Clients.xml b/qpid/doc/book/src/old/Clients.xml
index 3dc2d38e86..3dc2d38e86 100644
--- a/qpid/doc/book/src/Clients.xml
+++ b/qpid/doc/book/src/old/Clients.xml
diff --git a/qpid/doc/book/src/Connection-URL-Format.xml b/qpid/doc/book/src/old/Connection-URL-Format.xml
index cb772487cd..cb772487cd 100644
--- a/qpid/doc/book/src/Connection-URL-Format.xml
+++ b/qpid/doc/book/src/old/Connection-URL-Format.xml
diff --git a/qpid/doc/book/src/Download.xml b/qpid/doc/book/src/old/Download.xml
index 7bc08143ac..7bc08143ac 100644
--- a/qpid/doc/book/src/Download.xml
+++ b/qpid/doc/book/src/old/Download.xml
diff --git a/qpid/doc/book/src/Excel-AddIn.xml b/qpid/doc/book/src/old/Excel-AddIn.xml
index e38f620bd8..e38f620bd8 100644
--- a/qpid/doc/book/src/Excel-AddIn.xml
+++ b/qpid/doc/book/src/old/Excel-AddIn.xml
diff --git a/qpid/doc/book/src/FAQ.xml b/qpid/doc/book/src/old/FAQ.xml
index 5647f18f69..5647f18f69 100644
--- a/qpid/doc/book/src/FAQ.xml
+++ b/qpid/doc/book/src/old/FAQ.xml
diff --git a/qpid/doc/book/src/Getting-Started.xml b/qpid/doc/book/src/old/Getting-Started.xml
index 216a52170e..216a52170e 100644
--- a/qpid/doc/book/src/Getting-Started.xml
+++ b/qpid/doc/book/src/old/Getting-Started.xml
diff --git a/qpid/doc/book/src/How-to-Use-JNDI.xml b/qpid/doc/book/src/old/How-to-Use-JNDI.xml
index 74506dde0f..0d6315c2a3 100644
--- a/qpid/doc/book/src/How-to-Use-JNDI.xml
+++ b/qpid/doc/book/src/old/How-to-Use-JNDI.xml
@@ -167,7 +167,7 @@ ctx.close();
<section>
<title>Using Qpid with Other JNDI Providers</title>
- <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Using Qpid with other JNDI Providers.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Using-Qpid-with-other-JNDI-Providers.xml"/>
</section>
<!--h2-->
</section>
diff --git a/qpid/doc/book/src/InfoPlugin.xml b/qpid/doc/book/src/old/InfoPlugin.xml
index aebcd08c02..aebcd08c02 100644
--- a/qpid/doc/book/src/InfoPlugin.xml
+++ b/qpid/doc/book/src/old/InfoPlugin.xml
diff --git a/qpid/doc/book/src/Introduction.xml b/qpid/doc/book/src/old/Introduction.xml
index 8f92c207cf..8f92c207cf 100644
--- a/qpid/doc/book/src/Introduction.xml
+++ b/qpid/doc/book/src/old/Introduction.xml
diff --git a/qpid/doc/book/src/Java-Broker-StatusLogMessages.xml b/qpid/doc/book/src/old/Java-Broker-StatusLogMessages.xml
index 98f876e532..98f876e532 100644
--- a/qpid/doc/book/src/Java-Broker-StatusLogMessages.xml
+++ b/qpid/doc/book/src/old/Java-Broker-StatusLogMessages.xml
diff --git a/qpid/doc/book/src/Java-JMS-Selector-Syntax.xml b/qpid/doc/book/src/old/Java-JMS-Selector-Syntax.xml
index 870e277b66..870e277b66 100644
--- a/qpid/doc/book/src/Java-JMS-Selector-Syntax.xml
+++ b/qpid/doc/book/src/old/Java-JMS-Selector-Syntax.xml
diff --git a/qpid/doc/book/src/Management-Design-notes.xml b/qpid/doc/book/src/old/Management-Design-notes.xml
index 76f0dac926..76f0dac926 100644
--- a/qpid/doc/book/src/Management-Design-notes.xml
+++ b/qpid/doc/book/src/old/Management-Design-notes.xml
diff --git a/qpid/doc/book/src/NET-User-Guide.xml b/qpid/doc/book/src/old/NET-User-Guide.xml
index 7bfa20b8c8..7bfa20b8c8 100644
--- a/qpid/doc/book/src/NET-User-Guide.xml
+++ b/qpid/doc/book/src/old/NET-User-Guide.xml
diff --git a/qpid/doc/book/src/PythonBrokerTest.xml b/qpid/doc/book/src/old/PythonBrokerTest.xml
index ae7edade40..ae7edade40 100644
--- a/qpid/doc/book/src/PythonBrokerTest.xml
+++ b/qpid/doc/book/src/old/PythonBrokerTest.xml
diff --git a/qpid/doc/book/src/QMan-Qpid-Management-bridge.xml b/qpid/doc/book/src/old/QMan-Qpid-Management-bridge.xml
index f2c366dcbb..f2c366dcbb 100644
--- a/qpid/doc/book/src/QMan-Qpid-Management-bridge.xml
+++ b/qpid/doc/book/src/old/QMan-Qpid-Management-bridge.xml
diff --git a/qpid/doc/book/src/Qpid-ACLs.xml b/qpid/doc/book/src/old/Qpid-ACLs.xml
index a2b64061c3..a2b64061c3 100644
--- a/qpid/doc/book/src/Qpid-ACLs.xml
+++ b/qpid/doc/book/src/old/Qpid-ACLs.xml
diff --git a/qpid/doc/book/src/Qpid-Book.xml b/qpid/doc/book/src/old/Qpid-Book.xml
index ee69532152..ee69532152 100644
--- a/qpid/doc/book/src/Qpid-Book.xml
+++ b/qpid/doc/book/src/old/Qpid-Book.xml
diff --git a/qpid/doc/book/src/Qpid-Compatibility-And-Interoperability-Book.xml b/qpid/doc/book/src/old/Qpid-Compatibility-And-Interoperability-Book.xml
index f382f390c7..f382f390c7 100644
--- a/qpid/doc/book/src/Qpid-Compatibility-And-Interoperability-Book.xml
+++ b/qpid/doc/book/src/old/Qpid-Compatibility-And-Interoperability-Book.xml
diff --git a/qpid/doc/book/src/SASL-Compatibility.xml b/qpid/doc/book/src/old/SASL-Compatibility.xml
index ad223792b5..ad223792b5 100644
--- a/qpid/doc/book/src/SASL-Compatibility.xml
+++ b/qpid/doc/book/src/old/SASL-Compatibility.xml
diff --git a/qpid/doc/book/src/SSL.xml b/qpid/doc/book/src/old/SSL.xml
index a9a5cb953a..a9a5cb953a 100644
--- a/qpid/doc/book/src/SSL.xml
+++ b/qpid/doc/book/src/old/SSL.xml
diff --git a/qpid/doc/book/src/Security-Plugins.xml b/qpid/doc/book/src/old/Security-Plugins.xml
index bf5cb726b3..bf5cb726b3 100644
--- a/qpid/doc/book/src/Security-Plugins.xml
+++ b/qpid/doc/book/src/old/Security-Plugins.xml
diff --git a/qpid/doc/book/src/System-Properties.xml b/qpid/doc/book/src/old/System-Properties.xml
index 40b823185f..40b823185f 100644
--- a/qpid/doc/book/src/System-Properties.xml
+++ b/qpid/doc/book/src/old/System-Properties.xml
diff --git a/qpid/doc/book/src/Using-Qpid-with-other-JNDI-Providers.xml b/qpid/doc/book/src/old/Using-Qpid-with-other-JNDI-Providers.xml
index 2bd7d761ef..2bd7d761ef 100644
--- a/qpid/doc/book/src/Using-Qpid-with-other-JNDI-Providers.xml
+++ b/qpid/doc/book/src/old/Using-Qpid-with-other-JNDI-Providers.xml
diff --git a/qpid/doc/book/src/WCF.xml b/qpid/doc/book/src/old/WCF.xml
index aaf54463db..aaf54463db 100644
--- a/qpid/doc/book/src/WCF.xml
+++ b/qpid/doc/book/src/old/WCF.xml
diff --git a/qpid/doc/book/src/schemas.xml b/qpid/doc/book/src/old/schemas.xml
index 6102e65f07..6102e65f07 100644
--- a/qpid/doc/book/src/schemas.xml
+++ b/qpid/doc/book/src/old/schemas.xml
diff --git a/qpid/doc/book/src/programming/Makefile b/qpid/doc/book/src/programming/Makefile
new file mode 100644
index 0000000000..0266a0f54d
--- /dev/null
+++ b/qpid/doc/book/src/programming/Makefile
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+include ../Makefile.inc
diff --git a/qpid/doc/book/src/Message-Groups-Guide.xml b/qpid/doc/book/src/programming/Message-Groups-Guide.xml
index 3e5c549ff9..3e5c549ff9 100644
--- a/qpid/doc/book/src/Message-Groups-Guide.xml
+++ b/qpid/doc/book/src/programming/Message-Groups-Guide.xml
diff --git a/qpid/doc/book/src/Programming-In-Apache-Qpid.xml b/qpid/doc/book/src/programming/Programming-In-Apache-Qpid-Book.xml
index 3052e2acc1..c1162c96a8 100644
--- a/qpid/doc/book/src/Programming-In-Apache-Qpid.xml
+++ b/qpid/doc/book/src/programming/Programming-In-Apache-Qpid-Book.xml
@@ -3783,6 +3783,16 @@ spout - -content "$(cat rdu.xml | sed -e 's/70/45/')" xml/weather
<para>For compatibility with older clients, the synonym <varname>amqj.receiveBufferSize</varname> is supported.</para>
</entry>
</row>
+ <row>
+ <entry>qpid.failover_method_timeout</entry>
+ <entry>long</entry>
+ <entry>60000</entry>
+ <entry>
+ <para>During failover, this is the timeout for each attempt to try to re-establish the connection.
+ If a reconnection attempt exceeds the timeout, the entire failover process is aborted.</para>
+ <para>It is only applicable for AMQP 0-8/0-9/0-9-1 clients.</para>
+ </entry>
+ </row>
</tbody>
</tgroup>
</table>
diff --git a/qpid/doc/book/src/QmfBook.xml b/qpid/doc/book/src/qmf/QmfBook.xml
index 64a6545fb5..64a6545fb5 100644
--- a/qpid/doc/book/src/QmfBook.xml
+++ b/qpid/doc/book/src/qmf/QmfBook.xml
diff --git a/qpid/doc/book/src/QmfIntroduction.xml b/qpid/doc/book/src/qmf/QmfIntroduction.xml
index db7b8949a5..db7b8949a5 100644
--- a/qpid/doc/book/src/QmfIntroduction.xml
+++ b/qpid/doc/book/src/qmf/QmfIntroduction.xml
diff --git a/qpid/doc/book/xsl/html-custom.xsl b/qpid/doc/book/xsl/html-custom.xsl
new file mode 100644
index 0000000000..94ba3b67f9
--- /dev/null
+++ b/qpid/doc/book/xsl/html-custom.xsl
@@ -0,0 +1,188 @@
+<?xml version='1.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.
+ -
+ -->
+<xsl:stylesheet
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:import href="http://docbook.sourceforge.net/release/xsl/current/html/chunk.xsl"/>
+
+<xsl:template name="chunk-element-content">
+ <xsl:param name="prev"/>
+ <xsl:param name="next"/>
+ <xsl:param name="nav.context"/>
+ <xsl:param name="content">
+ <xsl:apply-imports/>
+ </xsl:param>
+
+ <xsl:call-template name="user.preroot"/>
+
+ <html>
+ <xsl:call-template name="html.head">
+ <xsl:with-param name="prev" select="$prev"/>
+ <xsl:with-param name="next" select="$next"/>
+ </xsl:call-template>
+
+ <body>
+<div class="container">
+ <xsl:call-template name="body.attributes"/>
+ <xsl:call-template name="user.header.navigation"/>
+
+ <xsl:call-template name="header.navigation">
+ <xsl:with-param name="prev" select="$prev"/>
+ <xsl:with-param name="next" select="$next"/>
+ <xsl:with-param name="nav.context" select="$nav.context"/>
+ </xsl:call-template>
+
+<div class="main_text_area">
+ <div class="main_text_area_top">
+ </div>
+ <div class="main_text_area_body">
+ <xsl:call-template name="user.header.content"/>
+ <xsl:copy-of select="$content"/>
+
+ <xsl:call-template name="user.footer.content"/>
+ </div>
+ <xsl:call-template name="footer.navigation">
+ <xsl:with-param name="prev" select="$prev"/>
+ <xsl:with-param name="next" select="$next"/>
+ <xsl:with-param name="nav.context" select="$nav.context"/>
+ </xsl:call-template>
+
+ <xsl:call-template name="user.footer.navigation"/>
+ <div class="main_text_area_bottom">
+ </div>
+</div>
+</div>
+ </body>
+ </html>
+ <xsl:value-of select="$chunk.append"/>
+</xsl:template>
+
+<xsl:template name="breadcrumbs">
+ <xsl:param name="this.node" select="."/>
+ <DIV class="breadcrumbs">
+ <xsl:for-each select="$this.node/ancestor::*">
+ <span class="breadcrumb-link">
+ <a>
+ <xsl:attribute name="href">
+ <xsl:call-template name="href.target">
+ <xsl:with-param name="object" select="."/>
+ <xsl:with-param name="context" select="$this.node"/>
+ </xsl:call-template>
+ </xsl:attribute>
+ <xsl:apply-templates select="." mode="title.markup"/>
+ </a>
+ </span>
+ <xsl:text> &gt; </xsl:text>
+ </xsl:for-each>
+ <!-- And display the current node, but not as a link -->
+ <span class="breadcrumb-node">
+ <xsl:apply-templates select="$this.node" mode="title.markup"/>
+ </span>
+ </DIV>
+</xsl:template>
+
+<xsl:template name="header.navigation">
+ <DIV class="header">
+ <DIV class="logo">
+ <H1>Apache Qpid&#8482;</H1>
+ <H2>Open Source AMQP Messaging</H2>
+ </DIV>
+ </DIV>
+
+ <DIV class="menu_box">
+ <DIV class="menu_box_top"></DIV>
+ <DIV class="menu_box_body">
+ <H3>Apache Qpid</H3>
+ <UL>
+ <LI><A href="http://qpid.apache.org/index.html">Home</A></LI>
+ <LI><A href="http://qpid.apache.org/download.html">Download</A></LI>
+ <LI><A href="http://qpid.apache.org/getting_started.html">Getting Started</A></LI>
+ <LI><A href="http://www.apache.org/licenses/">License</A></LI>
+ <LI><A href="https://cwiki.apache.org/qpid/faq.html">FAQ</A></LI>
+ </UL>
+ </DIV>
+ <DIV class="menu_box_bottom"></DIV>
+
+ <DIV class="menu_box_top"></DIV>
+ <DIV class="menu_box_body">
+ <H3>Documentation</H3>
+ <UL>
+ <LI><A href="http://qpid.apache.org/documentation.html#doc-release">0.14 Release</A></LI>
+ <LI><A href="http://qpid.apache.org/documentation.html#doc-trunk">Trunk</A></LI>
+ <LI><A href="http://qpid.apache.org/documentation.html#doc-archives">Archive</A></LI>
+ </UL>
+ </DIV>
+ <DIV class="menu_box_bottom"></DIV>
+
+ <DIV class="menu_box_top"></DIV>
+ <DIV class="menu_box_body">
+ <H3>Community</H3>
+ <UL>
+ <LI><A href="http://qpid.apache.org/getting_involved.html">Getting Involved</A></LI>
+ <LI><A href="http://qpid.apache.org/source_repository.html">Source Repository</A></LI>
+ <LI><A href="http://qpid.apache.org/mailing_lists.html">Mailing Lists</A></LI>
+ <LI><A href="https://cwiki.apache.org/qpid/">Wiki</A></LI>
+ <LI><A href="https://issues.apache.org/jira/browse/qpid">Issue Reporting</A></LI>
+ <LI><A href="http://qpid.apache.org/people.html">People</A></LI>
+ <LI><A href="http://qpid.apache.org/acknowledgements.html">Acknowledgements</A></LI>
+ </UL>
+ </DIV>
+ <DIV class="menu_box_bottom"></DIV>
+
+ <DIV class="menu_box_top"></DIV>
+ <DIV class="menu_box_body">
+ <H3>Developers</H3>
+ <UL>
+ <LI><A href="https://cwiki.apache.org/qpid/building.html">Building Qpid</A></LI>
+ <LI><A href="https://cwiki.apache.org/qpid/developer-pages.html">Developer Pages</A></LI>
+ </UL>
+ </DIV>
+ <DIV class="menu_box_bottom"></DIV>
+
+ <DIV class="menu_box_top"></DIV>
+ <DIV class="menu_box_body">
+ <H3>About AMQP</H3>
+ <UL>
+ <LI><A href="http://qpid.apache.org/amqp.html">What is AMQP?</A></LI>
+ </UL>
+ </DIV>
+ <DIV class="menu_box_bottom"></DIV>
+
+ <DIV class="menu_box_top"></DIV>
+ <DIV class="menu_box_body">
+ <H3>About Apache</H3>
+ <UL>
+ <LI><A href="http://www.apache.org">Home</A></LI>
+ <LI><A href="http://www.apache.org/foundation/sponsorship.html">Sponsorship</A></LI>
+ <LI><A href="http://www.apache.org/foundation/thanks.html">Thanks</A></LI>
+ <LI><A href="http://www.apache.org/security/">Security</A></LI>
+ </UL>
+ </DIV>
+ <DIV class="menu_box_bottom"></DIV>
+ </DIV>
+
+</xsl:template>
+
+<xsl:template name="user.header.content">
+ <xsl:call-template name="breadcrumbs"/>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/qpid/extras/qmf/src/py/qmf/console.py b/qpid/extras/qmf/src/py/qmf/console.py
index ef7deb1d68..bd1ddb2833 100644
--- a/qpid/extras/qmf/src/py/qmf/console.py
+++ b/qpid/extras/qmf/src/py/qmf/console.py
@@ -3948,7 +3948,7 @@ class Event:
return "<uninterpretable>"
out = strftime("%c", gmtime(self.timestamp / 1000000000))
out += " " + self._sevName() + " " + self.classKey.getPackageName() + ":" + self.classKey.getClassName()
- out += " broker=" + self.broker.getUrl()
+ out += " broker=" + str(self.broker.getUrl())
for arg in self.schema.arguments:
disp = self.session._displayValue(self.arguments[arg.name], arg.type).encode("utf8")
if " " in disp:
diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py
index f2c83d113c..66e1cb49be 100644
--- a/qpid/python/qpid/connection.py
+++ b/qpid/python/qpid/connection.py
@@ -170,6 +170,10 @@ class Connection(Framer):
if not status:
self.detach_all()
break
+ # When we do not use SSL transport, we get periodic
+ # spurious timeout events on the socket. When using SSL,
+ # these events show up as timeout *errors*. Both should be
+ # ignored unless we have aborted.
except socket.timeout:
if self.aborted():
self.close_code = (None, "connection timed out")
@@ -178,9 +182,12 @@ class Connection(Framer):
else:
continue
except socket.error, e:
- self.close_code = (None, str(e))
- self.detach_all()
- break
+ if self.aborted() or str(e) != "The read operation timed out":
+ self.close_code = (None, str(e))
+ self.detach_all()
+ break
+ else:
+ continue
frame_dec.write(data)
seg_dec.write(*frame_dec.read())
op_dec.write(*seg_dec.read())
diff --git a/qpid/python/qpid/messaging/driver.py b/qpid/python/qpid/messaging/driver.py
index 0358659111..3cb62d75c9 100644
--- a/qpid/python/qpid/messaging/driver.py
+++ b/qpid/python/qpid/messaging/driver.py
@@ -226,7 +226,11 @@ class LinkIn:
def do_link(self, sst, rcv, _rcv, type, subtype, action):
link_opts = _rcv.options.get("link", {})
- reliability = link_opts.get("reliability", "at-least-once")
+ if type == "topic":
+ default_reliability = "unreliable"
+ else:
+ default_reliability = "at-least-once"
+ reliability = link_opts.get("reliability", default_reliability)
declare = link_opts.get("x-declare", {})
subscribe = link_opts.get("x-subscribe", {})
acq_mode = acquire_mode.pre_acquired
diff --git a/qpid/python/qpid/messaging/util.py b/qpid/python/qpid/messaging/util.py
index 265cf7d51f..726cfd5172 100644
--- a/qpid/python/qpid/messaging/util.py
+++ b/qpid/python/qpid/messaging/util.py
@@ -50,10 +50,13 @@ def set_reconnect_urls(conn, msg):
reconnect_urls = []
urls = msg.properties["amq.failover"]
for u in urls:
+ # FIXME aconway 2012-06-12: Nasty hack parsing of the C++ broker's URL format.
if u.startswith("amqp:"):
- for p in u[5:].split(","):
- parts = p.split(":")
- host, port = parts[1:3]
+ for a in u[5:].split(","):
+ parts = a.split(":")
+ # Handle IPv6 addresses which have : in the host part.
+ port = parts[-1] # Last : separated field is port
+ host = ":".join(parts[1:-1]) # First : separated field is protocol, host is the rest.
reconnect_urls.append("%s:%s" % (host, port))
conn.reconnect_urls = reconnect_urls
log.warn("set reconnect_urls for conn %s: %s", conn, reconnect_urls)
diff --git a/qpid/specs/amqp.xsl b/qpid/specs/amqp.xsl
new file mode 100644
index 0000000000..afe3bd5e65
--- /dev/null
+++ b/qpid/specs/amqp.xsl
@@ -0,0 +1,534 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ -
+ - 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.
+ -
+ -->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+<xsl:output indent="no"/>
+ <xsl:template match="node()[name()='amqp']">
+
+<html>
+ <head>
+ <title>AMQP <xsl:call-template name="initCap"><xsl:with-param name="input" select="@name"/></xsl:call-template></title>
+ <style type="text/css">
+ body {
+ margin: 2em;
+ padding: 1em;
+ font-family: "DejaVu LGC Sans", "Bitstream Vera Sans", arial, helvetica, sans-serif;
+ }
+
+ h2 {
+ font-size: 1.25em;
+ color: #339;
+ }
+
+ h2 a {
+ color: #339;
+ }
+
+ h3 {
+ font-size: 1em;
+ position: relative;
+ left: -0.5em;
+ }
+
+ h4 {
+ font-size: 1em;
+ position: relative;
+ left: -0.5em;
+ }
+
+ h1 a, h3 a, h4 a {
+ color: #000;
+ }
+
+ a {
+ text-decoration: none;
+ color: #66f;
+ }
+
+ a.anchor {
+ color: #000;
+ }
+
+ a.toc {
+ float: right;
+ }
+
+ table {
+ border-collapse: collapse;
+ margin: 1em 1em;
+ }
+
+ dt {
+ font-weight: bold;
+ }
+
+ dt:after {
+ content: ":";
+ }
+
+ div.toc {
+ border: 1px solid #ccc;
+ padding: 1em;
+ }
+
+ table.toc {
+ margin: 0;
+ }
+
+ table.pre {
+ background: #eee;
+ border: 1px solid #ccc;
+ margin-left: auto;
+ margin-right: auto;
+ }
+
+ table.pre td {
+ font-family: Courier, monospace;
+ white-space: pre;
+ padding: 0em 2em;
+ }
+
+ table.definition {
+ width: 100%;
+ }
+
+ table.signature {
+ background: #eee;
+ border: 1px solid #ccc;
+ width: 100%;
+ }
+
+ table.signature td {
+ font-family: monospace;
+ white-space: pre;
+ padding: 1em;
+ }
+
+ table.composite {
+ width: 100%;
+ }
+
+ td.field {
+ padding: 0.5em 2em;
+ }
+
+ div.section {
+ padding: 0 0 0 1em;
+ }
+
+ div.doc {
+ padding: 0 0 1em 0;
+ }
+
+ table.composite div.doc {
+ padding: 0;
+ }
+
+ table.error {
+ width: 100%;
+ margin: 0;
+ }
+
+ table.error tr:last-child td {
+ padding: 0 0 0 1em;
+ }
+
+ .todo:before {
+ content: "TODO: ";
+ font-weight: bold;
+ color: red;
+ }
+
+ .todo {
+ font-variant: small-caps;
+ font-weight: bold;
+ color: red;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>AMQP <xsl:call-template name="initCap"><xsl:with-param name="input" select="@name"/></xsl:call-template></h1>
+
+ <xsl:if test="descendant-or-self::node()[name()='section']"><xsl:call-template name="toc"/></xsl:if>
+ <xsl:apply-templates mode="spec" select="."/>
+ </body>
+</html>
+ </xsl:template>
+
+ <!-- Table of Contents -->
+
+ <xsl:template name="toc">
+ <h2><a name="toc">Table of Contents</a></h2>
+ <div class="toc">
+ <table class="toc" summary="Table of Contents">
+ <xsl:apply-templates mode="toc" select="."/>
+ </table>
+ </div>
+ </xsl:template>
+
+ <xsl:template mode="toc" match="node()[name()='section']">
+ <xsl:variable name="title"><xsl:choose>
+ <xsl:when test="@title"><xsl:value-of select="@title"/></xsl:when>
+ <xsl:otherwise><xsl:call-template name="initCapWords"><xsl:with-param name="input" select="translate(@name,'-',' ')"/></xsl:call-template></xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="ref">#section-<xsl:value-of select="@name"/></xsl:variable>
+ <xsl:variable name="section-number"><xsl:value-of select="count(preceding-sibling::node()[name()='section'])+1"/></xsl:variable>
+ <tr><td><xsl:value-of select="$section-number"/><xsl:text> </xsl:text><xsl:element name="a"><xsl:attribute name="href"><xsl:value-of select="$ref"/></xsl:attribute><xsl:value-of select="$title"/></xsl:element></td><td><xsl:value-of select="@label"/></td></tr>
+ <xsl:apply-templates mode="toc" select="node()[name()='type' or name()='definition' or ( name()='doc' and attribute::node()[name()='title']!='')]"/>
+ </xsl:template>
+
+ <xsl:template mode="toc" match="node()[name()='doc' and attribute::node()[name()='title']]">
+ <xsl:variable name="ref">#doc-<xsl:choose><xsl:when test="@name"><xsl:value-of select="@name"/></xsl:when><xsl:otherwise><xsl:value-of select="generate-id(.)"/></xsl:otherwise></xsl:choose></xsl:variable>
+ <xsl:variable name="section-number"><xsl:apply-templates mode="doc-number" select="."/></xsl:variable>
+ <tr><td><xsl:text>&#160;&#160;&#160;&#160;&#160;&#160;</xsl:text><xsl:value-of select="$section-number"/><xsl:text> </xsl:text><xsl:element name="a"><xsl:attribute name="href"><xsl:value-of select="$ref"/></xsl:attribute><xsl:value-of select="@title"/></xsl:element></td><td><xsl:value-of select="@label"/></td></tr>
+ </xsl:template>
+
+ <xsl:template mode="toc" match="node()[name()='type' or name()='definition']">
+ <xsl:variable name="ref">#<xsl:value-of select="name()"/>-<xsl:value-of select="@name"/></xsl:variable>
+ <xsl:variable name="section-number"><xsl:apply-templates mode="type-number" select="."/></xsl:variable>
+ <tr><td><xsl:text>&#160;&#160;&#160;&#160;&#160;&#160;</xsl:text><xsl:if test="contains(substring-after($section-number,'.'),'.')"><xsl:text>&#160;&#160;&#160;&#160;&#160;&#160;</xsl:text></xsl:if><xsl:value-of select="$section-number"/><xsl:text> </xsl:text><xsl:element name="a"><xsl:attribute name="href"><xsl:value-of select="$ref"/></xsl:attribute><xsl:call-template name="initCapWords"><xsl:with-param name="input" select="translate(@name,'-',' ')"/></xsl:call-template></xsl:element></td><td><xsl:value-of select="@label"/></td></tr>
+ </xsl:template>
+
+ <!-- Sections -->
+
+ <xsl:template match="node()[name()='section']" mode="spec">
+ <xsl:variable name="title"><xsl:choose>
+ <xsl:when test="@title"><xsl:value-of select="@title"/></xsl:when>
+ <xsl:otherwise><xsl:call-template name="initCapWords"><xsl:with-param name="input" select="translate(@name,'-',' ')"/></xsl:call-template></xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <h2><a class="toc" href="#toc"><xsl:text>&#8592;</xsl:text></a> <xsl:apply-templates mode="section-number" select="."/><xsl:text> </xsl:text><xsl:element name="a"><xsl:attribute name="name">section-<xsl:value-of select="@name"/></xsl:attribute><xsl:value-of select="$title"/></xsl:element></h2>
+ <div class="section">
+ <xsl:apply-templates mode="spec" select="*"/>
+ </div>
+ </xsl:template>
+
+ <!-- docs -->
+ <xsl:template match="node()[name()='doc']" mode="spec">
+ <xsl:choose>
+ <xsl:when test="ancestor::node()[name() = 'doc']">
+ <xsl:if test="@title">
+ <h4><a class="toc" href="#toc"><xsl:text>&#8592;</xsl:text></a><xsl:element name="a"><xsl:attribute name="name">doc-<xsl:choose><xsl:when test="@name"><xsl:value-of select="@name"/></xsl:when><xsl:otherwise><xsl:value-of select="generate-id(.)"/></xsl:otherwise></xsl:choose></xsl:attribute><xsl:call-template name="initCapWords"><xsl:with-param name="input" select="translate(@title,'-',' ')"></xsl:with-param></xsl:call-template></xsl:element></h4>
+ </xsl:if>
+ <xsl:apply-templates mode="spec" select="*|comment()"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:if test="@title">
+ <h3><a class="toc" href="#toc"><xsl:text>&#8592;</xsl:text></a> <xsl:apply-templates mode="doc-number" select="."/><xsl:text> </xsl:text><xsl:element name="a"><xsl:attribute name="name">doc-<xsl:choose><xsl:when test="@name"><xsl:value-of select="@name"/></xsl:when><xsl:otherwise><xsl:value-of select="generate-id(.)"/></xsl:otherwise></xsl:choose></xsl:attribute><xsl:call-template name="initCapWords"><xsl:with-param name="input" select="translate(@title,'-',' ')"></xsl:with-param></xsl:call-template></xsl:element></h3>
+ </xsl:if>
+ <div class="doc">
+ <xsl:apply-templates mode="spec" select="*|comment()"/>
+ </div>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- picture -->
+
+ <xsl:template match="node()[name()='picture']" mode="spec">
+ <xsl:element name="table"><xsl:attribute name="class">pre</xsl:attribute><xsl:if test="@title"><xsl:attribute name="summary"><xsl:value-of select="@title"/></xsl:attribute></xsl:if>
+ <xsl:if test="@title"><caption><xsl:value-of select="@title"/></caption></xsl:if>
+<tr><td>
+<xsl:apply-templates select="*|text()" mode="spec"/>
+</td></tr></xsl:element>
+ </xsl:template>
+
+ <!-- xref -->
+<xsl:template match="node()[name()='xref']" mode="spec">
+ <xsl:variable name="ref" select="@name"/>
+ <xsl:choose>
+ <!-- section xref -->
+ <xsl:when test="@type='section' or ancestor-or-self::node()/descendant-or-self::node()[name()='section' and @name=$ref]">
+ <xsl:element name="a"><xsl:attribute name="href">#section-<xsl:value-of select="@name"/></xsl:attribute><xsl:value-of select="@name"/></xsl:element>
+ </xsl:when>
+ <!-- type xref -->
+ <xsl:when test="@type='type' or ancestor-or-self::node()/descendant-or-self::node()[name()='type' and @name=$ref]">
+ <xsl:element name="a"><xsl:attribute name="href">#type-<xsl:value-of select="@name"/></xsl:attribute><xsl:value-of select="@name"/></xsl:element>
+ </xsl:when>
+ <!-- doc xref -->
+ <xsl:when test="@type='doc' or ancestor-or-self::node()/descendant-or-self::node()[name()='doc' and @name=$ref]">
+ <xsl:element name="a"><xsl:attribute name="href">#doc-<xsl:value-of select="@name"/></xsl:attribute><xsl:value-of select="@name"/></xsl:element>
+ </xsl:when>
+ <xsl:when test="@type='amqp'">
+ <xsl:element name="a"><xsl:attribute name="href"><xsl:value-of select="@name"/>.xml</xsl:attribute><xsl:call-template name="initCap"><xsl:with-param name="input"><xsl:value-of select="@name"/></xsl:with-param></xsl:call-template></xsl:element>
+ </xsl:when>
+ <xsl:otherwise><xsl:value-of select="@name"/></xsl:otherwise>
+ </xsl:choose>
+
+
+
+</xsl:template>
+
+ <!-- todo -->
+ <xsl:template match="node()[name()='todo']" mode="spec"><span class="todo"><xsl:apply-templates select="*|text()" mode="spec"/></span></xsl:template>
+
+ <!-- primitive type -->
+ <xsl:template match="node()[name()='type' and @class='primitive']" mode="spec">
+ <h3><a class="toc" href="#toc"><xsl:text>&#8592;</xsl:text></a> <xsl:apply-templates mode="type-number" select="."/><xsl:text> </xsl:text> <xsl:element name="a"><xsl:attribute name="name">type-<xsl:value-of select="@name"/></xsl:attribute><xsl:call-template name="initCap"><xsl:with-param name="input" select="@name"/></xsl:call-template></xsl:element></h3>
+ <xsl:apply-templates select="." mode="type-signature"/>
+ <xsl:element name="table"><xsl:attribute name="class">signature</xsl:attribute><xsl:attribute name="summary"><xsl:value-of select="@label"/></xsl:attribute>
+ <tr><td><b><xsl:value-of select="@name"/></b>: <xsl:value-of select="@label"/></td></tr>
+ </xsl:element>
+
+ <b>Encodings:</b>
+ <xsl:element name="table"><xsl:attribute name="class">composite</xsl:attribute><xsl:attribute name="summary"><xsl:value-of select="@label"/></xsl:attribute>
+ <xsl:apply-templates select="node()[name()='encoding']" mode="spec"/>
+ </xsl:element>
+ </xsl:template>
+
+
+
+ <!-- composite type -->
+ <xsl:template match="node()[name()='type' and @class='composite']" mode="spec">
+ <h3><a class="toc" href="#toc"><xsl:text>&#8592;</xsl:text></a> <xsl:apply-templates mode="type-number" select="."/><xsl:text> </xsl:text> <xsl:element name="a"><xsl:attribute name="name">type-<xsl:value-of select="@name"/></xsl:attribute><xsl:call-template name="initCapWords"><xsl:with-param name="input" select="translate(@name,'-',' ')"></xsl:with-param></xsl:call-template></xsl:element></h3>
+ <xsl:apply-templates select="." mode="type-signature"/>
+ <xsl:apply-templates select="node()[name()='doc']" mode="spec"/>
+ <xsl:element name="table"><xsl:attribute name="class">composite</xsl:attribute><xsl:attribute name="summary"><xsl:value-of select="@name"/> fields</xsl:attribute>
+ <xsl:for-each select="node()[name()='field']">
+ <tr><td><b><xsl:value-of select="@name"/></b></td><td><i><xsl:value-of select="@label"/></i></td><td><xsl:choose>
+ <xsl:when test="@mandatory">mandatory</xsl:when>
+ <xsl:otherwise>optional</xsl:otherwise>
+ </xsl:choose><xsl:text> </xsl:text><xsl:call-template name="genTypeXref"><xsl:with-param name="type" select="@type"/></xsl:call-template><xsl:if test="@multiple">[]</xsl:if></td></tr>
+ <tr><td class="field" colspan="2"><xsl:apply-templates select="node()[name()='doc' or name()='error']" mode="spec"/></td></tr>
+ </xsl:for-each>
+ </xsl:element>
+ </xsl:template>
+
+ <!-- restricted type -->
+ <xsl:template match="node()[name()='type' and @class='restricted']" mode="spec">
+ <xsl:variable name="name"><xsl:value-of select="@name"/></xsl:variable>
+ <h3><a class="toc" href="#toc"><xsl:text>&#8592;</xsl:text></a> <xsl:apply-templates mode="type-number" select="."/><xsl:text> </xsl:text> <xsl:element name="a"><xsl:attribute name="name">type-<xsl:value-of select="@name"/></xsl:attribute><xsl:call-template name="initCapWords"><xsl:with-param name="input" select="translate(@name,'-',' ')"></xsl:with-param></xsl:call-template></xsl:element></h3>
+ <xsl:apply-templates select="." mode="type-signature"/>
+ <xsl:apply-templates select="node()[name()='doc']" mode="spec"/>
+ <xsl:element name="table"><xsl:attribute name="class">composite</xsl:attribute><xsl:attribute name="summary">possible values</xsl:attribute>
+ <xsl:for-each select="node()[name()='choice']">
+ <tr><td><b><xsl:element name="a"><xsl:attribute name="class">anchor</xsl:attribute><xsl:attribute name="name">choice-<xsl:value-of select="$name"/>-<xsl:value-of select="@name"/></xsl:attribute><xsl:value-of select="@name"/></xsl:element></b></td><td></td><td><xsl:value-of select="@value"/></td></tr>
+ <tr><td class="field" colspan="2"><xsl:apply-templates select="node()[name()='doc' or name()='error']" mode="spec"/></td></tr>
+ </xsl:for-each>
+ </xsl:element>
+ </xsl:template>
+
+ <!-- type signature -->
+ <xsl:template match="node()[name()='type']" mode="type-signature">
+ <table class="signature" summary="@(label)"><tr>
+<td>&#60;type<xsl:if test="@name"><xsl:text> </xsl:text>name="<xsl:value-of select="@name"/>"</xsl:if><xsl:if test="@class"><xsl:text> </xsl:text>class="<xsl:value-of select="@class"/>"</xsl:if><xsl:if test="@source"><xsl:text> </xsl:text>source="<xsl:value-of select="@source"/>"</xsl:if><xsl:if test="@provides"><xsl:text> </xsl:text>provides="<xsl:value-of select="@provides"/>"</xsl:if><xsl:if test="not( node()[name() = 'field' or name() = 'choice' or name() = 'descriptor'])">/</xsl:if>&#62;<xsl:if test="node()[name() = 'field' or name() = 'choice' or name() = 'descriptor']">
+<xsl:apply-templates select="node()[name()='field' or name()='choice' or name()='descriptor']" mode="type-signature"/><xsl:text>
+&#60;/type&#62;</xsl:text>
+</xsl:if>
+ </td></tr></table>
+ </xsl:template>
+
+ <xsl:template match="node()[name()='descriptor']" mode="type-signature">
+<xsl:text>
+ </xsl:text>&#60;descriptor<xsl:if test="@name"><xsl:text> </xsl:text>name="<xsl:value-of select="@name"/>"</xsl:if><xsl:if test="@code"><xsl:text> </xsl:text>code="<xsl:value-of select="@code"/>"</xsl:if><xsl:text>/&#62;</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="node()[name()='choice']" mode="type-signature">
+<xsl:text>
+ </xsl:text>&#60;choice<xsl:for-each select="attribute::node()[name() != 'label']"><xsl:text> </xsl:text><xsl:value-of select="name()"/>="<xsl:value-of select="."/>"</xsl:for-each><xsl:text>/&#62;</xsl:text>
+ </xsl:template>
+
+ <xsl:template match="node()[name()='field']" mode="type-signature">
+<xsl:text>
+ </xsl:text>&#60;field<xsl:for-each select="attribute::node()[name() != 'label']"><xsl:text> </xsl:text><xsl:value-of select="name()"/>="<xsl:value-of select="."/>"</xsl:for-each><xsl:if test="not( node()[name()='error'] )"><xsl:text>/</xsl:text></xsl:if>&#62;<xsl:if test="node()[name()='error']">
+<xsl:apply-templates mode="type-signature" select="node()[name()='error']"/><xsl:text>
+ &#60;/field&#62;</xsl:text></xsl:if>
+ </xsl:template>
+
+ <xsl:template match="node()[name()='error']" mode="type-signature">
+<xsl:text>
+ </xsl:text>&#60;error<xsl:for-each select="attribute::node()[name() != 'label']"><xsl:text> </xsl:text><xsl:value-of select="name()"/>="<xsl:value-of select="."/>"</xsl:for-each><xsl:text>/&#62;</xsl:text>
+ </xsl:template>
+
+
+ <!-- encoding -->
+ <xsl:template match="node()[name()='encoding']" mode="spec">
+ <tr><td><b><xsl:value-of select="@name"/></b></td><td><i><xsl:value-of select="@label"/></i></td><td>code <xsl:value-of select="@code"/>: <xsl:choose>
+ <xsl:when test="@category='fixed'">fixed-width, <xsl:value-of select="@width"/> byte value</xsl:when>
+ <xsl:otherwise>variable-width, <xsl:value-of select="@width"/> byte size</xsl:otherwise>
+ </xsl:choose></td></tr>
+ <tr><td class="field" colspan="3"><xsl:apply-templates select="node()[name()='doc']" mode="spec"/></td></tr>
+
+ </xsl:template>
+
+
+ <!-- TypeListTable comment -->
+ <xsl:template match="comment()[self::comment()='TypeListTable']" mode="spec">
+ <table class="" summary="Primitive Types">
+ <xsl:for-each select="//descendant-or-self::node()[name()='type']">
+ <tr><td><xsl:element name="a"><xsl:attribute name="href">#type-<xsl:value-of select="@name"/></xsl:attribute><xsl:value-of select="@name"/></xsl:element></td><td><xsl:value-of select="@label"/></td></tr>
+ </xsl:for-each>
+ </table>
+ </xsl:template>
+
+ <!-- EncodingListTable comment -->
+ <xsl:template match="comment()[self::comment()='EncodingListTable']" mode="spec">
+ <table class="" summary="Primitive Type Encodings">
+ <tr><th>Type</th><th>Encoding</th><th>Code</th><th>Category</th><th>Description</th></tr>
+ <xsl:for-each select="//descendant-or-self::node()[name()='type']">
+ <xsl:variable name="type" select="@name"/>
+ <xsl:for-each select="node()[name()='encoding']">
+ <tr>
+ <td><xsl:element name="a"><xsl:attribute name="href">#type-<xsl:value-of select="$type"/></xsl:attribute><xsl:value-of select="$type"/></xsl:element></td>
+ <td><xsl:value-of select="@name"/></td>
+ <td><xsl:value-of select="@code"/></td>
+ <td><xsl:value-of select="@category"/>/<xsl:value-of select="@width"/></td>
+ <td><xsl:value-of select="@label"/></td>
+ </tr>
+ </xsl:for-each>
+ </xsl:for-each>
+ </table>
+ </xsl:template>
+
+
+ <!-- text -->
+
+ <xsl:template match="text()" mode="spec"><xsl:value-of select="."/></xsl:template>
+
+ <!-- error -->
+
+ <xsl:template match="node()[name()='error']" mode="spec">
+ <table class="error" summary="">
+ <tr>
+ <td><b><xsl:value-of select="@name"/> error:</b> </td>
+
+ <td align="right"><xsl:element name="a"><xsl:attribute name="href"><xsl:call-template name="genErrorXref"><xsl:with-param name="type" select="@value"/></xsl:call-template></xsl:attribute><xsl:value-of select="@value"/></xsl:element></td>
+ </tr>
+ <tr><td colspan="2"><xsl:apply-templates select="node()[name()='doc']" mode="spec"/></td></tr>
+ </table>
+ </xsl:template>
+
+ <!-- definition -->
+
+ <xsl:template mode="spec" match="node()[name()='definition']">
+ <h3><a class="toc" href="#toc"><xsl:text>&#8592;</xsl:text></a> <xsl:apply-templates mode="type-number" select="."/><xsl:text> </xsl:text> <xsl:element name="a"><xsl:attribute name="name">definition-<xsl:value-of select="@name"/></xsl:attribute><xsl:call-template name="initCap"><xsl:with-param name="input" select="@name"/></xsl:call-template></xsl:element></h3>
+
+ <xsl:element name="table"><xsl:attribute name="class">signature</xsl:attribute><xsl:attribute name="summary"><xsl:value-of select="@label"/></xsl:attribute>
+ <tr>
+ <td><xsl:value-of select="@name"/>: <xsl:value-of select="@label"/></td>
+ <td align="right"><xsl:value-of select="@value"/></td>
+ </tr>
+ </xsl:element>
+ <xsl:apply-templates mode="spec" select="node()[name()='doc']"/>
+ </xsl:template>
+
+ <!-- pass through tags -->
+
+ <xsl:template match="node()[name() = 'p']" mode="spec">
+ <xsl:element name="p"><xsl:if test="@title"><xsl:attribute name="title"><xsl:value-of select="@title"/></xsl:attribute></xsl:if><xsl:apply-templates mode="spec" select="*|text()"/></xsl:element>
+ </xsl:template>
+ <xsl:template match="node()[name() = 'i']" mode="spec">
+ <xsl:element name="i"><xsl:if test="@title"><xsl:attribute name="title"><xsl:value-of select="@title"/></xsl:attribute></xsl:if><xsl:apply-templates mode="spec" select="*|text()"/></xsl:element>
+ </xsl:template>
+ <xsl:template match="node()[name() = 'term']" mode="spec">
+ <xsl:element name="i"><xsl:if test="@title"><xsl:attribute name="title"><xsl:value-of select="@title"/></xsl:attribute></xsl:if><xsl:apply-templates mode="spec" select="*|text()"/></xsl:element>
+ </xsl:template>
+ <xsl:template match="node()[name() = 'b']" mode="spec">
+ <xsl:element name="b"><xsl:if test="@title"><xsl:attribute name="title"><xsl:value-of select="@title"/></xsl:attribute></xsl:if><xsl:apply-templates mode="spec" select="*|text()"/></xsl:element>
+ </xsl:template>
+ <xsl:template match="node()[name() = 'ol']" mode="spec">
+ <xsl:element name="ol"><xsl:if test="@title"><xsl:attribute name="title"><xsl:value-of select="@title"/></xsl:attribute></xsl:if><xsl:apply-templates mode="spec" select="*|text()"/></xsl:element>
+ </xsl:template>
+ <xsl:template match="node()[name() = 'ul']" mode="spec">
+ <xsl:element name="ul"><xsl:if test="@title"><xsl:attribute name="title"><xsl:value-of select="@title"/></xsl:attribute></xsl:if><xsl:apply-templates mode="spec" select="*|text()"/></xsl:element>
+ </xsl:template>
+ <xsl:template match="node()[name() = 'li']" mode="spec">
+ <xsl:element name="li"><xsl:if test="@title"><xsl:attribute name="title"><xsl:value-of select="@title"/></xsl:attribute></xsl:if><xsl:apply-templates mode="spec" select="*|text()"/></xsl:element>
+ </xsl:template>
+ <xsl:template match="node()[name() = 'dl']" mode="spec">
+ <xsl:element name="dl"><xsl:if test="@title"><xsl:attribute name="title"><xsl:value-of select="@title"/></xsl:attribute></xsl:if><xsl:apply-templates mode="spec" select="*|text()"/></xsl:element>
+ </xsl:template>
+ <xsl:template match="node()[name() = 'dt']" mode="spec">
+ <xsl:element name="dt"><xsl:if test="@title"><xsl:attribute name="title"><xsl:value-of select="@title"/></xsl:attribute></xsl:if><xsl:apply-templates mode="spec" select="*|text()"/></xsl:element>
+ </xsl:template>
+ <xsl:template match="node()[name() = 'dd']" mode="spec">
+ <xsl:element name="dd"><xsl:if test="@title"><xsl:attribute name="title"><xsl:value-of select="@title"/></xsl:attribute></xsl:if><xsl:apply-templates mode="spec" select="*|text()"/></xsl:element>
+ </xsl:template>
+
+
+
+ <!-- Document specific utilities -->
+
+ <xsl:template mode="section-number" match="node()[name()='section']"><xsl:value-of select="count(preceding-sibling::node()[name()='section'])+1"/></xsl:template>
+ <xsl:template mode="doc-number" match="node()[name()='doc']"><xsl:apply-templates mode="section-number" select="ancestor::node()[name()='section']"/>.<xsl:value-of select="1+count(preceding-sibling::node()[name()='doc' and attribute::title])"/></xsl:template>
+ <xsl:template mode="type-number" match="node()[name()='type' or name()='definition']">
+ <xsl:choose>
+ <xsl:when test="count(preceding-sibling::node()[name()='doc' and attribute::title])>0">
+ <xsl:variable name="doc-node" select="preceding-sibling::node()[name()='doc' and attribute::title]"/>
+ <xsl:variable name="offset" select="count($doc-node/preceding-sibling::node()[name()='type' or name()='definition'])"></xsl:variable>
+ <xsl:apply-templates mode="section-number" select="ancestor::node()[name()='section']"/>.<xsl:value-of select="count(preceding-sibling::node()[name()='doc' and attribute::title])"/>.<xsl:value-of select="1+count(preceding-sibling::node()[name()='type' or name()='definition'])-$offset"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates mode="section-number" select="ancestor::node()[name()='section']"/>.<xsl:value-of select="1+count(preceding-sibling::node()[name()='type' or name()='definition'])"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+ <xsl:template name="genTypeXref">
+ <xsl:param name="type"/>
+ <xsl:choose>
+ <xsl:when test="$type='*'">*</xsl:when>
+ <xsl:otherwise><xsl:element name="a"><xsl:attribute name="href"><xsl:choose>
+ <xsl:when test="/ancestor-or-self::node()/descendant-or-self::node()[name()='type' and @name=$type]">#type-<xsl:value-of select="$type"/></xsl:when>
+ <xsl:otherwise>
+ <xsl:for-each select="document('index.xml')/descendant-or-self::node()[name()='xref' and @type='amqp']">
+ <xsl:variable name="docname"><xsl:value-of select="@name"/>.xml</xsl:variable>
+ <xsl:if test="document($docname)/descendant-or-self::node()[name()='type' and @name=$type]"><xsl:value-of select="$docname"/>#type-<xsl:value-of select="$type"/></xsl:if>
+ </xsl:for-each>
+ </xsl:otherwise>
+ </xsl:choose></xsl:attribute><xsl:value-of select="$type"/></xsl:element>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <xsl:template name="genErrorXref">
+ <xsl:param name="type"/>
+ <xsl:choose>
+ <xsl:when test="/ancestor-or-self::node()/descendant-or-self::node()[name()='type' and @provides='error']/node()[name()='choice' and @name=$type]">#choice-<xsl:value-of select="/ancestor-or-self::node()/descendant-or-self::node()[name()='type' and @provides='error' and child::node()[name()='choice' and @name=$type]]/@name"/>-<xsl:value-of select="$type"/></xsl:when>
+ <xsl:otherwise>
+ <xsl:for-each select="document('index.xml')/descendant-or-self::node()[name()='xref' and @type='amqp']">
+ <xsl:variable name="docname"><xsl:value-of select="@name"/>.xml</xsl:variable>
+ <xsl:if test="document($docname)/descendant-or-self::node()[name()='type' and @provides='error']/node()[name()='choice' and @name=$type]">
+ #choice-<xsl:value-of select="document($docname)/descendant-or-self::node()[name()='type' and @provides='error' and child::node()[name()='choice' and @name=$type]]/@name"/>-<xsl:value-of select="$type"/>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+ <!-- General Utilities -->
+
+ <xsl:template name="toUpper"><xsl:param name="input"/><xsl:value-of select="translate($input,'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')"></xsl:value-of></xsl:template>
+ <xsl:template name="toLower"><xsl:param name="input"/><xsl:value-of select="translate($input,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')"></xsl:value-of></xsl:template>
+ <xsl:template name="initCap"><xsl:param name="input"/><xsl:call-template name="toUpper"><xsl:with-param name="input" select="substring($input,1,1)"/></xsl:call-template><xsl:value-of select="substring($input,2)"/></xsl:template>
+ <xsl:template name="initCapWords">
+ <xsl:param name="input"/>
+ <xsl:choose>
+ <xsl:when test="contains($input,' ')">
+ <xsl:call-template name="initCap"><xsl:with-param name="input" select="substring-before($input, ' ')"/></xsl:call-template><xsl:text> </xsl:text><xsl:call-template name="initCapWords"><xsl:with-param name="input" select="substring-after($input, ' ')"/></xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="initCap"><xsl:with-param name="input" select="$input"/></xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+</xsl:stylesheet>
diff --git a/qpid/specs/apache-filters.xml b/qpid/specs/apache-filters.xml
new file mode 100644
index 0000000000..a4e40aa93c
--- /dev/null
+++ b/qpid/specs/apache-filters.xml
@@ -0,0 +1,227 @@
+<!--
+
+ 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.
+-->
+<?xml-stylesheet type="text/xsl" href="amqp.xsl"?>
+
+<amqp name="apache-filters" label="Apache Proposed AMQP 1-0 Filters">
+ <section name="legacy-amqp" title="Legacy AMQP Exchange Binding Support">
+ <doc>
+ <p>
+ Versions of AMQP prior to 1.0 prescribed a model of Exchanges and Queues, where Queues were
+ bound to Exchanges with a binding key whose meaning depended upon the type of the Exchange.
+ In order to allow a consistent mechanism for addressing legacy AMQP Exchanges over AMQP 1.0
+ the following filter types are defined. Use of an Exchange as an address for a Source thus
+ can be seen as equivalent to constructing exclusive queues bound to an Exchange in legacy
+ AMQP versions.
+ </p>
+ <p>
+ Containers which support the filters that are defined in this section should advertise the
+ capability <term>APACHE.ORG:LEGACY_AMQP_EXCHANGE_FILTERS</term> in their connection
+ capabilities when sending the open performative, and MUST provide this capability on
+ sources supporting these filter types.
+ </p>
+ </doc>
+ <type class="restricted" name="legacy-amqp-direct-binding" source="string" provides="filter">
+ <descriptor name="apache.org:legacy-amqp-direct-binding:string"
+ code="0x0000468C:0x00000000"/>
+ <doc>
+ <p>
+ The legacy-amqp-direct-binding filter consists of a described string value. The filter
+ matches a message if and only if the described string value exactly matches the subject
+ field of the Properties section of the message being evaluated. If the message has no
+ Properties section, or if the subject field of the Properties section is not set, then
+ the legacy-amqp-direct-binding filter does not match.
+ </p>
+ </doc>
+ </type>
+
+ <type class="restricted" name="legacy-amqp-topic-binding" source="string" provides="filter">
+ <descriptor name="apache.org:legacy-amqp-topic-binding:string"
+ code="0x0000468C:0x00000001"/>
+ <doc>
+ <p>
+ The legacy-amqp-topic-binding filter consists of a described string value. The value
+ value described by the type is interpreted as a pattern to match against the subject
+ field of the Properties section of the message being evaluated.
+ </p>
+ <ul>
+ <li>The pattern is formed using zero or more tokens, with each token delimited by the
+ "." character. The tokens "#" and "*" have special meanings.</li>
+ <li>The token consisting of the single character "*" matches a single word in the
+ subject field.</li>
+ <li>The token consisting of the single character "#" matches zero or more words in the
+ subject field.</li>
+ </ul>
+ <p>
+ Thus the filter value "*.stock.#" would match the subjects "usd.stock" and
+ "eur.stock.db" but not "stock.nasdaq".
+ </p>
+ <p>
+ If the message has no Properties section, or if the subject field of the Properties
+ section is not set, then the legacy-amqp-topic-binding filter matches only if the value
+ of the filter is a single "#".
+ </p>
+ </doc>
+ </type>
+
+ <type class="restricted" name="legacy-amqp-headers-binding" source="map" provides="filter">
+ <descriptor name="apache.org:legacy-amqp-headers-binding:map" code="0x0000468C:0x00000002"/>
+ <doc>
+ <p>
+ The legacy-amqp-headers-binding filter consists of a described map value. The map
+ value described by the type is interpreted as a pattern to match against the
+ application-properties section of the message being evaluated. The map has the same
+ restriction as the application-properties section, namely that the keys of this are
+ restricted to be of type string (which excludes the possibility of a null key) and the
+ values are restricted to be of simple types only, that is, excluding map, list, and
+ array types.
+ </p>
+ <p>
+ The key "x-match" in the described map has special meaning. This key MUST map to the
+ symbolic value "any" or the symbolic value "all" within the described map. All other
+ keys which begin "x-" MUST be ignored by the source when evaluating. If the value for
+ "x-match" is "all" then all other valid key-value pairs in the map MUST match with an
+ entry with the same key in the application-properties section. If the value for
+ "x-match" is "any" then the filter will accept the message if at least one key-value
+ pair matches the equivalent key value pair in the application-properties section.
+ </p>
+ <p>
+ A key-value pair in the filter's map matches a key-value pair in the
+ application-properties section if the keys are identical (including the same type), or
+ if the value in the filter map for the key is null.
+ </p>
+ </doc>
+ </type>
+ </section>
+ <section name="jms" title="Java Message Service Support">
+
+ <doc>
+ <p>
+ The Java Message Service defines two types of filtering of messages: firstly the ability to
+ exclude from a subscription messages sent by the same connection, secondly a more general
+ filtering syntax known as "selectors" based on an SQL like syntax.
+ </p>
+ <p>
+ AMQP filter extensions through which these two types of
+ filtering may be achieved are defined below. Their use, though
+ motivated by support for JMS, is not restricted to JMS.
+ </p>
+ </doc>
+
+ <type class="composite" name="no-local-filter" source="list" provides="filter">
+ <descriptor name="apache.org:no-local-filter:list" code="0x0000468C:0x00000003"/>
+ <doc>
+ <p>
+ A message will be accepted by the simple-no-local-filter if and only if the message
+ was originally sent to the container of the source on a separate connection from that
+ which is currently receiving from the source.
+ </p>
+ <p>
+ Containers which support this filter should advertise the
+ capability <term>APACHE.ORG:NO_LOCAL</term> in their
+ connection capabilities when sending the open
+ performative, and MUST provide this capability on sources
+ supporting these filter types.
+ </p>
+ </doc>
+ </type>
+
+ <type class="restricted" name="selector-filter" provides="filter" source="string">
+ <descriptor name="apache.org:selector-filter:string" code="0x0000468C:0x00000004"/>
+ <doc>
+ <p>
+ The Java Message Service "selector" defines an SQL like
+ syntax for filtering messages. The selector filters based
+ on the values of "headers" and "properties". The
+ selector-filter uses the selector as defined by JMS but
+ with the names of JMS headers translated into their AMQP
+ equivalents. The defined JMS headers can be mapped to
+ equivalent fields within the AMQP message sections:
+ </p>
+<picture title="Mapping JMS Headers to AMQP fields">
+JMS Header Name | AMQP 1.0 Field
+==================|====================================================
+JMSCorrelationID | correlation-id field of properties section
+JMSDeliveryMode | durable field of header section
+JMSDestination | to field of the properties section
+JMSExpiration | absolute-expiry-time of properties section
+JMSMessageID | message-id of properties section
+JMSPriority | priority field of header section
+JMSRedelivered | delivery-count > 0 in header section
+JMSReplyTo | reply-to in properties section
+JMSTimestamp | creation-time of properties section
+JMSType | annotation jms-type in message-annotations section
+</picture>
+ <p>
+ When encoding the selector string on the wire, these JMS
+ header names should be translated to amqp.<i>field_name</i>
+ where <i>field_name</i> is the appropriate AMQP 1.0 field
+ named in the table above, with the hyphen replaced by an
+ underscore. For example, the selector: "JMSCorrelationID =
+ 'abc' AND color = 'blue' AND weight > 2500" would be
+ transferred over the wire as: "amqp.correlation_id = 'abc'
+ AND color = 'blue' AND weight > 2500"
+ </p>
+ <p>
+ The "properties" of the JMS message are equivalent to the AMQP application-properties
+ section. Thus a reference to a property Foo in a message selector would be evaluated
+ as the value associated with the key "Foo" (if present) in the application-properties
+ section.
+ </p>
+ <p>
+ The operands of the JMS selector are defined in terms of the types available within JMS,
+ When evaluated against the application properties section, the values within that section
+ MUST be evaluated according to the following type mapping.
+ </p>
+<picture title="Mapping AMQP types to JMS types">
+AMQP Type | JMS Selector Type
+==================|===================
+null | null
+boolean | boolean
+ubyte | short
+ushort | int
+uint | long
+ulong | long
+byte | byte
+short | short
+int | int
+long | long
+float | float
+double | double
+decimal32 | double
+decimal64 | double
+decimal128 | double
+char | char
+timestamp | long
+uuid | byte[16]
+binary | byte[]
+string | String
+symbol | String
+</picture>
+ <p>
+ Containers which support this filter should advertise the
+ capability <term>APACHE.ORG:SELECTOR</term> in their
+ connection capabilities when sending the open
+ performative, and MUST provide this capability on sources
+ supporting these filter types.
+ </p>
+ </doc>
+ </type>
+ </section>
+</amqp>
diff --git a/qpid/specs/management-schema.xml b/qpid/specs/management-schema.xml
index 9eafbc52fa..ed0902bfc1 100644
--- a/qpid/specs/management-schema.xml
+++ b/qpid/specs/management-schema.xml
@@ -8,9 +8,9 @@
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
-
+
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -81,7 +81,6 @@
<property name="systemRef" type="objId" references="System" access="RO" desc="System ID" parentRef="y"/>
<property name="port" type="uint16" access="RO" desc="TCP Port for AMQP Service"/>
<property name="workerThreads" type="uint16" access="RO" desc="Thread pool size"/>
- <property name="maxConns" type="uint16" access="RO" desc="Maximum allowed connections"/>
<property name="connBacklog" type="uint16" access="RO" desc="Connection backlog limit for listening socket"/>
<property name="stagingThreshold" type="uint32" access="RO" desc="Broker stages messages over this size to disk"/>
<property name="mgmtPublish" type="bool" access="RO" desc="Broker's management agent sends unsolicited data on the publish interval"/>
@@ -125,8 +124,8 @@
<statistic name="abandonedViaAlt" type="count64" unit="message" desc="Messages routed to alternate exchange from a deleted queue"/>
<method name="echo" desc="Request a response to test the path to the management broker">
- <arg name="sequence" dir="IO" type="uint32" default="0"/>
- <arg name="body" dir="IO" type="lstr" default=""/>
+ <arg name="sequence" dir="IO" type="uint32"/>
+ <arg name="body" dir="IO" type="lstr"/>
</method>
<method name="connect" desc="Establish a connection to another broker">
@@ -143,7 +142,7 @@
<arg name="srcQueue" dir="I" type="sstr" desc="Source queue"/>
<arg name="destQueue" dir="I" type="sstr" desc="Destination queue"/>
<arg name="qty" dir="I" type="uint32" desc="# of messages to move. 0 means all messages"/>
- <arg name="filter" dir="I" type="map" default="{}" desc="if specified, move only those messages matching this filter"/>
+ <arg name="filter" dir="I" type="map" desc="if specified, move only those messages matching this filter"/>
</method>
<method name="setLogLevel" desc="Set the log level">
@@ -164,20 +163,20 @@
<method name="create" desc="Create an object of the specified type">
<arg name="type" dir="I" type="sstr" desc="The type of object to create"/>
- <arg name="name" dir="I" type="sstr" desc="The name of the object to create"/>
- <arg name="properties" dir="I" type="map" desc="Type specific object properties"/>
- <arg name="strict" dir="I" type="bool" desc="If specified, treat unrecognised object properties as an error"/>
+ <arg name="name" dir="I" type="sstr" desc="The name of the object to create"/>
+ <arg name="properties" dir="I" type="map" desc="Type specific object properties"/>
+ <arg name="strict" dir="I" type="bool" desc="If specified, treat unrecognised object properties as an error"/>
</method>
<method name="delete" desc="Delete an object of the specified type">
<arg name="type" dir="I" type="sstr" desc="The type of object to delete"/>
- <arg name="name" dir="I" type="sstr" desc="The name of the object to delete"/>
- <arg name="options" dir="I" type="map" desc="Type specific object options for deletion"/>
+ <arg name="name" dir="I" type="sstr" desc="The name of the object to delete"/>
+ <arg name="options" dir="I" type="map" desc="Type specific object options for deletion"/>
</method>
<method name="query" desc="Query the current state of an object.">
<arg name="type" dir="I" type="sstr" desc="The type of object to query."/>
- <arg name="name" dir="I" type="sstr" desc="The name of the object to query"/>
+ <arg name="name" dir="I" type="sstr" desc="The name of the object to query"/>
<arg name="results" dir="O" type="map" desc="A snapshot of the object's state."/>
</method>
@@ -272,14 +271,14 @@
<method name="purge" desc="Discard all or some messages on a queue">
<arg name="request" dir="I" type="uint32" desc="0 for all messages or n>0 for n messages"/>
- <arg name="filter" dir="I" type="map" default="{}" desc="if specified, purge only those messages matching this filter"/>
+ <arg name="filter" dir="I" type="map" desc="if specified, purge only those messages matching this filter"/>
</method>
<method name="reroute" desc="Remove all or some messages on this queue and route them to an exchange">
<arg name="request" dir="I" type="uint32" desc="0 for all messages or n>0 for n messages"/>
<arg name="useAltExchange" dir="I" type="bool" desc="Iff true, use the queue's configured alternate exchange; iff false, use exchange named in the 'exchange' argument"/>
<arg name="exchange" dir="I" type="sstr" desc="Name of the exchange to route the messages through"/>
- <arg name="filter" dir="I" type="map" default="{}" desc="if specified, reroute only those messages matching this filter"/>
+ <arg name="filter" dir="I" type="map" desc="if specified, reroute only those messages matching this filter"/>
</method>
</class>
@@ -321,7 +320,7 @@
<statistic name="msgMatched" type="count64"/>
</class>
-
+
<!--
===============================================================
Subscription
@@ -338,7 +337,7 @@
<property name="arguments" type="map" access="RC"/>
<statistic name="delivered" type="count64" unit="message" desc="Messages delivered"/>
</class>
-
+
<!--
===============================================================
Connection
@@ -366,7 +365,7 @@
<statistic name="msgsFromClient" type="count64"/>
<statistic name="msgsToClient" type="count64"/>
- <method name="close"/>
+ <method name="close"/>
</class>
<!--
@@ -379,15 +378,17 @@
This class represents an inter-broker connection.
<property name="vhostRef" type="objId" references="Vhost" access="RC" index="y" parentRef="y"/>
- <property name="host" type="sstr" access="RC" index="y"/>
- <property name="port" type="uint16" access="RC" index="y"/>
- <property name="transport" type="sstr" access="RC"/>
+ <property name="name" type="sstr" access="RC" index="y"/>
+ <property name="host" type="sstr" access="RO"/>
+ <property name="port" type="uint16" access="RO"/>
+ <property name="transport" type="sstr" access="RO"/>
<property name="durable" type="bool" access="RC"/>
+ <property name="connectionRef" type="objId" references="Connection" access="RO"/>
<statistic name="state" type="sstr" desc="Operational state of the link"/>
<statistic name="lastError" type="lstr" desc="Reason link is not operational"/>
- <method name="close"/>
+ <method name="close"/>
<method name="bridge" desc="Bridge messages over the link">
<arg name="durable" dir="I" type="bool"/>
@@ -411,7 +412,8 @@
-->
<class name="Bridge">
<property name="linkRef" type="objId" references="Link" access="RC" index="y" parentRef="y"/>
- <property name="channelId" type="uint16" access="RC" index="y"/>
+ <property name="name" type="sstr" access="RC" index="y"/>
+ <property name="channelId" type="uint16" access="RO"/>
<property name="durable" type="bool" access="RC"/>
<property name="src" type="sstr" access="RC"/>
<property name="dest" type="sstr" access="RC"/>
@@ -422,7 +424,7 @@
<property name="excludes" type="sstr" access="RC"/>
<property name="dynamic" type="bool" access="RC"/>
<property name="sync" type="uint16" access="RC"/>
- <method name="close"/>
+ <method name="close"/>
</class>
@@ -441,7 +443,7 @@
<property name="expireTime" type="absTime" access="RO" optional="y"/>
<property name="maxClientRate" type="uint32" access="RO" unit="msgs/sec" optional="y"/>
- <statistic name="framesOutstanding" type="count32"/>
+ <statistic name="unackedMessages" type="uint64" unit="message" desc="Unacknowledged messages in the session"/>
<statistic name="TxnStarts" type="count64" unit="transaction" desc="Total transactions started "/>
<statistic name="TxnCommits" type="count64" unit="transaction" desc="Total transactions committed"/>
diff --git a/qpid/tests/src/py/qpid_tests/broker_0_10/__init__.py b/qpid/tests/src/py/qpid_tests/broker_0_10/__init__.py
index 107b34c82b..312dc22645 100644
--- a/qpid/tests/src/py/qpid_tests/broker_0_10/__init__.py
+++ b/qpid/tests/src/py/qpid_tests/broker_0_10/__init__.py
@@ -36,3 +36,4 @@ from extensions import *
from msg_groups import *
from new_api import *
from stats import *
+from qmf_events import *
diff --git a/qpid/tests/src/py/qpid_tests/broker_0_10/management.py b/qpid/tests/src/py/qpid_tests/broker_0_10/management.py
index a17a945e29..a1316ea854 100644
--- a/qpid/tests/src/py/qpid_tests/broker_0_10/management.py
+++ b/qpid/tests/src/py/qpid_tests/broker_0_10/management.py
@@ -302,9 +302,10 @@ class ManagementTest (TestBase010):
twenty = range(1,21)
props = session.delivery_properties(routing_key="routing_key")
+ mp = session.message_properties(application_headers={'x-qpid.trace' : 'A,B,C'})
for count in twenty:
body = "Reroute Message %d" % count
- msg = Message(props, body)
+ msg = Message(props, mp, body)
session.message_transfer(destination="amq.direct", message=msg)
pq = self.qmf.getObjects(_class="queue", name="reroute-queue")[0]
@@ -317,6 +318,16 @@ class ManagementTest (TestBase010):
self.assertEqual(pq.msgDepth,19)
self.assertEqual(aq.msgDepth,1)
+ "Verify that the trace was cleared on the rerouted message"
+ url = "%s://%s:%d" % (self.broker.scheme or "amqp", self.broker.host, self.broker.port)
+ conn = qpid.messaging.Connection(url)
+ conn.open()
+ sess = conn.session()
+ rx = sess.receiver("alt-queue1;{mode:browse}")
+ rm = rx.fetch(1)
+ self.assertEqual(rm.properties['x-qpid.trace'], '')
+ conn.close()
+
"Reroute top 9 messages from reroute-queue to alt.direct2"
result = pq.reroute(9, False, "alt.direct2", {})
self.assertEqual(result.status, 0)
@@ -385,6 +396,30 @@ class ManagementTest (TestBase010):
# Cleanup
for e in ["A", "B"]: session.exchange_delete(exchange=e)
+ def test_reroute_invalid_alt_exchange(self):
+ """
+ Test that an error is returned for an attempt to reroute to
+ alternate exchange on a queue for which no such exchange has
+ been defined.
+ """
+ self.startQmf()
+ session = self.session
+ # create queue with no alt-exchange, and send a message to it
+ session.queue_declare(queue="q", exclusive=True, auto_delete=True)
+ props = session.delivery_properties(routing_key="q")
+ session.message_transfer(message=Message(props, "don't reroute me!"))
+
+ # attempt to reroute the message to alt-exchange
+ q = self.qmf.getObjects(_class="queue", name="q")[0]
+ result = q.reroute(1, True, "", {})
+ # verify the attempt fails...
+ self.assertEqual(result.status, 4) #invalid parameter
+
+ # ...and message is still on the queue
+ self.subscribe(destination="d", queue="q")
+ self.assertEqual("don't reroute me!", session.incoming("d").get(timeout=1).body)
+
+
def test_methods_async (self):
"""
"""
diff --git a/qpid/tests/src/py/qpid_tests/broker_0_10/msg_groups.py b/qpid/tests/src/py/qpid_tests/broker_0_10/msg_groups.py
index 18fb0a61a3..ace7611a2f 100644
--- a/qpid/tests/src/py/qpid_tests/broker_0_10/msg_groups.py
+++ b/qpid/tests/src/py/qpid_tests/broker_0_10/msg_groups.py
@@ -1122,6 +1122,70 @@ class MultiConsumerMsgGroupTests(Base):
snd.close()
+ def test_ttl_expire(self):
+ """ Verify that expired (TTL) group messages are skipped correctly
+ """
+ snd = self.ssn.sender("msg-group-q; {create:always, delete:sender," +
+ " node: {x-declare: {arguments:" +
+ " {'qpid.group_header_key':'THE-GROUP'," +
+ "'qpid.shared_msg_group':1}}}}")
+
+ groups = ["A","B","C","A","B","C"]
+ messages = [Message(content={}, properties={"THE-GROUP": g}) for g in groups]
+ index = 0
+ for m in messages:
+ m.content['index'] = index
+ index += 1
+ if m.properties['THE-GROUP'] == 'B':
+ m.ttl = 1;
+ snd.send(m)
+
+ sleep(2) # let all B's expire
+
+ # create consumers on separate sessions: C1,C2
+ s1 = self.setup_session()
+ c1 = s1.receiver("msg-group-q", options={"capacity":0})
+ s2 = self.setup_session()
+ c2 = s2.receiver("msg-group-q", options={"capacity":0})
+
+ # C1 should acquire A-0, then C2 should acquire C-2, Group B should
+ # expire and never be fetched
+
+ m1 = c1.fetch(0);
+ assert m1.properties['THE-GROUP'] == 'A'
+ assert m1.content['index'] == 0
+
+ m2 = c2.fetch(0);
+ assert m2.properties['THE-GROUP'] == 'C'
+ assert m2.content['index'] == 2
+
+ m1 = c1.fetch(0);
+ assert m1.properties['THE-GROUP'] == 'A'
+ assert m1.content['index'] == 3
+
+ m2 = c2.fetch(0);
+ assert m2.properties['THE-GROUP'] == 'C'
+ assert m2.content['index'] == 5
+
+ # there should be no more left for either consumer
+ try:
+ mx = c1.fetch(0)
+ assert False # should never get here
+ except Empty:
+ pass
+ try:
+ mx = c2.fetch(0)
+ assert False # should never get here
+ except Empty:
+ pass
+
+ c1.session.acknowledge()
+ c2.session.acknowledge()
+ c1.close()
+ c2.close()
+ snd.close()
+
+
class StickyConsumerMsgGroupTests(Base):
"""
Tests for the behavior of sticky-consumer message groups. These tests
diff --git a/qpid/tests/src/py/qpid_tests/broker_0_10/new_api.py b/qpid/tests/src/py/qpid_tests/broker_0_10/new_api.py
index 5e03a7b487..05c4815e57 100644
--- a/qpid/tests/src/py/qpid_tests/broker_0_10/new_api.py
+++ b/qpid/tests/src/py/qpid_tests/broker_0_10/new_api.py
@@ -57,7 +57,7 @@ class GeneralTests(Base):
sess2 = self.setup_session()
tx = sess1.sender("amq.direct/key")
- rx_main = sess1.receiver("amq.direct/key;{link:{x-declare:{alternate-exchange:'amq.fanout'}}}")
+ rx_main = sess1.receiver("amq.direct/key;{link:{reliability:at-least-once,x-declare:{alternate-exchange:'amq.fanout'}}}")
rx_alt = sess2.receiver("amq.fanout")
rx_alt.capacity = 10
diff --git a/qpid/tests/src/py/qpid_tests/broker_0_10/qmf_events.py b/qpid/tests/src/py/qpid_tests/broker_0_10/qmf_events.py
new file mode 100644
index 0000000000..ac56670044
--- /dev/null
+++ b/qpid/tests/src/py/qpid_tests/broker_0_10/qmf_events.py
@@ -0,0 +1,83 @@
+#
+# 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.
+#
+
+from qpid.messaging import *
+from qpid.tests.messaging import Base
+from qpidtoollibs.broker import EventHelper
+import math
+
+class EventTests (Base):
+ """
+ Test various qmf events
+ """
+ def setup_connection(self):
+ return Connection.establish(self.broker, **self.connection_options())
+
+ def setup_session(self):
+ return self.conn.session()
+
+ def test_queue_declare(self):
+ helper = EventHelper()
+
+ # subscribe for queue declare events
+ rcv = self.ssn.receiver(helper.eventAddress("org.apache.qpid.broker", "queueDeclare"))
+ # create a queue
+ snd = self.ssn.sender("myq; {create:always, delete:always}")
+ # ensure we got an event
+ event = helper.event(rcv.fetch(timeout=1))
+ assert event.name, "org_apache_qpid_broker:queueDeclare"
+ assert event.qName, "myq"
+
+ def test_queue_delete(self):
+ helper = EventHelper()
+
+ rcv = self.ssn.receiver(helper.eventAddress("org.apache.qpid.broker", "queueDelete"))
+ snd = self.ssn.sender("myq; {create:always, delete:always}")
+ snd.close()
+
+ event = helper.event(rcv.fetch(timeout=1))
+ assert event.name, "org_apache_qpid_broker:queueDelete"
+ assert event.qName, "myq"
+
+ def test_queue_autodelete_exclusive(self):
+ helper = EventHelper()
+
+ rcv = self.ssn.receiver(helper.eventAddress("org.apache.qpid.broker", "queueDelete"))
+
+ #create new session
+ ssn2 = self.setup_session()
+ snd = ssn2.sender("myq; {create:always, node:{x-declare:{auto-delete:True, exclusive:True}}}")
+ ssn2.close()
+
+ event = helper.event(rcv.fetch(timeout=1))
+ assert event.name, "org_apache_qpid_broker:queueDelete"
+ assert event.qName, "myq"
+
+ def test_queue_autodelete_shared(self):
+ helper = EventHelper()
+
+ rcv = self.ssn.receiver(helper.eventAddress("org.apache.qpid.broker", "queueDelete"))
+
+ rcv2 = self.ssn.receiver("myq; {create:always, node:{x-declare:{auto-delete:True}}}")
+ rcv2.close()
+
+ event = helper.event(rcv.fetch(timeout=1))
+ assert event.name, "org_apache_qpid_broker:queueDelete"
+ assert event.qName, "myq"
+
diff --git a/qpid/tools/src/py/.gitignore b/qpid/tools/src/py/.gitignore
index 97cb05dc36..b775fd83a1 100644
--- a/qpid/tools/src/py/.gitignore
+++ b/qpid/tools/src/py/.gitignore
@@ -19,4 +19,5 @@
# with the License. You may obtain a copy of the License at
/qpid-clusterc
/qpid-configc
+/qpid-hac
/qpid-routec
diff --git a/qpid/tools/src/py/qmf-tool b/qpid/tools/src/py/qmf-tool
index 8413ca2ca0..db51c96796 100755
--- a/qpid/tools/src/py/qmf-tool
+++ b/qpid/tools/src/py/qmf-tool
@@ -266,7 +266,7 @@ class QmfData:
self.conn_options = conn_options
self.qmf_options = qmf_options
self.agent_filter = '[]'
- self.connection = cqpid.Connection(self.url, self.conn_options)
+ self.connection = cqpid.Connection(self.url, **self.conn_options)
self.connection.open()
self.session = qmf2.ConsoleSession(self.connection, self.qmf_options)
self.session.setAgentFilter(self.agent_filter)
diff --git a/qpid/tools/src/py/qpid-config b/qpid/tools/src/py/qpid-config
index 896ae89faf..1308df765d 100755
--- a/qpid/tools/src/py/qpid-config
+++ b/qpid/tools/src/py/qpid-config
@@ -481,7 +481,7 @@ class BrokerManager:
if LVQ_KEY in args: print "--lvq-key=%s" % args[LVQ_KEY],
if QUEUE_EVENT_GENERATION in args: print "--generate-queue-events=%s" % args[QUEUE_EVENT_GENERATION],
if q.altExchange:
- print "--alternate-exchange=%s" % q._altExchange_.name,
+ print "--alternate-exchange=%s" % q.altExchange,
if FLOW_STOP_SIZE in args: print "--flow-stop-size=%s" % args[FLOW_STOP_SIZE],
if FLOW_RESUME_SIZE in args: print "--flow-resume-size=%s" % args[FLOW_RESUME_SIZE],
if FLOW_STOP_COUNT in args: print "--flow-stop-count=%s" % args[FLOW_STOP_COUNT],
diff --git a/qpid/tools/src/py/qpid-ha b/qpid/tools/src/py/qpid-ha
index ac35e3ed2c..36f08ba397 100755
--- a/qpid/tools/src/py/qpid-ha
+++ b/qpid/tools/src/py/qpid-ha
@@ -35,18 +35,18 @@ HA_BROKER = "org.apache.qpid.ha:habroker:ha-broker"
class Command:
commands = {}
- def __init__(self, name, help, args=[]):
+ def __init__(self, name, help, arg_names=[]):
Command.commands[name] = self
self.name = name
- self.args = args
- usage="%s [options] %s\n\n%s"%(name, " ".join(args), help)
+ self.arg_names = arg_names
+ usage="%s [options] %s\n\n%s"%(name, " ".join(arg_names), help)
self.help = help
self.op=optparse.OptionParser(usage)
self.op.add_option("-b", "--broker", metavar="<url>", help="Connect to broker at <url>")
- def execute(self):
- opts, args = self.op.parse_args()
- if len(args) != len(self.args)+1:
+ def execute(self, args):
+ opts, args = self.op.parse_args(args)
+ if len(args) != len(self.arg_names)+1:
self.op.print_help()
raise Exception("Wrong number of arguments")
broker = opts.broker or "localhost:5672"
@@ -93,29 +93,31 @@ class SetCmd(Command):
Command.__init__(self, "set", "Set HA configuration settings")
def add(optname, metavar, type, help):
self.op.add_option(optname, metavar=metavar, type=type, help=help, action="store")
- add("--brokers", "<url>", "string", "HA brokers use <url> to connect to each other")
- add("--public-brokers", "<url>", "string", "Clients use <url> to connect to HA brokers")
+ add("--brokers-url", "<url>", "string", "URL with address of each broker in the cluster. Used by brokers to connect to each other.")
+ add("--public-url", "<url>", "string", "URL advertised to clients to connect to the cluster. May be a list or a VIP.")
add("--backups", "<n>", "int", "Expect <n> backups to be running"),
def do_execute(self, qmf_broker, ha_broker, opts, args):
- if (opts.brokers): qmf_broker._method("setBrokers", {"url":opts.brokers}, HA_BROKER)
- if (opts.public_brokers): qmf_broker._method("setPublicBrokers", {"url":opts.public_brokers}, HA_BROKER)
+ if (opts.brokers_url): qmf_broker._method("setBrokersUrl", {"url":opts.brokers_url}, HA_BROKER)
+ if (opts.public_url): qmf_broker._method("setPublicUrl", {"url":opts.public_url}, HA_BROKER)
if (opts.backups): qmf_broker._method("setExpectedBackups", {"expectedBackups":opts.backups}, HA_BROKER)
SetCmd()
class QueryCmd(Command):
def __init__(self):
- Command.__init__(self, "query", "Print HA configuration settings")
+ Command.__init__(self, "query", "Print HA configuration and status")
def do_execute(self, qmf_broker, ha_broker, opts, args):
hb = ha_broker
for x in [("Status:", hb.status),
- ("Brokers URL:", hb.brokers),
- ("Public URL:", hb.publicBrokers),
- ("Expected Backups:", hb.expectedBackups)
+ ("Brokers URL:", hb.brokersUrl),
+ ("Public URL:", hb.publicUrl),
+ ("Expected Backups:", hb.expectedBackups),
+ ("Replicate: ", hb.replicateDefault)
]:
print "%-20s %s"%(x[0], x[1])
+
QueryCmd()
def print_usage(prog):
@@ -143,7 +145,7 @@ def main(argv):
if not command:
print_usage(os.path.basename(argv[0]));
return 1;
- if command.execute(): return 1
+ if command.execute(args): return 1
except Exception, e:
print e
return 1
diff --git a/qpid/tools/src/py/qpid-printevents b/qpid/tools/src/py/qpid-printevents
index d56d2899b1..7c3e2b6c23 100755
--- a/qpid/tools/src/py/qpid-printevents
+++ b/qpid/tools/src/py/qpid-printevents
@@ -21,34 +21,85 @@
import os
import optparse
-from optparse import IndentedHelpFormatter
import sys
-import socket
-from time import time, strftime, gmtime, sleep
-from qmf.console import Console, Session
+from optparse import IndentedHelpFormatter
+from time import time, strftime, gmtime, sleep
+from threading import Lock, Condition, Thread
+from qpid.messaging import Connection
+import qpid.messaging.exceptions
+home = os.environ.get("QPID_TOOLS_HOME", os.path.normpath("/usr/share/qpid-tools"))
+sys.path.append(os.path.join(home, "python"))
-class EventConsole(Console):
- def event(self, broker, event):
- print event
- sys.stdout.flush()
+from qpidtoollibs.broker import EventHelper
- def brokerConnected(self, broker):
- print strftime("%c", gmtime(time())), "NOTIC qpid-printevents:brokerConnected broker=%s" % broker.getUrl()
- sys.stdout.flush()
- def brokerConnectionFailed(self, broker):
- print strftime("%c", gmtime(time())), "NOTIC qpid-printevents:brokerConnectionFailed broker=%s %s" % (broker.getUrl(), str(broker.conn_exc))
- sys.stdout.flush()
+class Printer(object):
+ """
+ This class serializes printed lines so that events coming from different
+ threads don't overlap each other.
+ """
+ def __init__(self):
+ self.lock = Lock()
- def brokerDisconnected(self, broker):
- print strftime("%c", gmtime(time())), "NOTIC qpid-printevents:brokerDisconnected broker=%s" % broker.getUrl()
+ def pr(self, text):
+ self.lock.acquire()
+ try:
+ print text
+ finally:
+ self.lock.release()
sys.stdout.flush()
+
+
+class EventReceiver(Thread):
+ """
+ One instance of this class is created for each broker that is being monitored.
+ This class does not use the "reconnect" option because it needs to report as
+ events when the connection is established and when it's lost.
+ """
+ def __init__(self, printer, url, mechanism, options):
+ Thread.__init__(self)
+ self.printer = printer
+ self.url = url
+ self.mechanism = mechanism
+ self.options = options
+ self.running = True
+ self.helper = EventHelper()
+
+ def cancel(self):
+ self.running = False
+
+ def run(self):
+ isOpen = False
+ while self.running:
+ try:
+ conn = Connection.establish(self.url, sasl_mechanisms=self.mechanism, client_properties=self.options)
+ isOpen = True
+ self.printer.pr(strftime("%c", gmtime(time())) + " NOTIC qpid-printevents:brokerConnected broker=%s" % self.url)
+
+ sess = conn.session()
+ rx = sess.receiver(self.helper.eventAddress())
+
+ while self.running:
+ try:
+ msg = rx.fetch(1)
+ event = self.helper.event(msg)
+ self.printer.pr(event.__repr__())
+ sess.acknowledge()
+ except qpid.messaging.exceptions.Empty:
+ pass
+
+ except Exception, e:
+ if isOpen:
+ self.printer.pr(strftime("%c", gmtime(time())) + " NOTIC qpid-printevents:brokerDisconnected broker=%s" % self.url)
+ isOpen = False
+ sleep(1)
+
class JHelpFormatter(IndentedHelpFormatter):
- """Format usage and description without stripping newlines from usage strings
"""
-
+ Format usage and description without stripping newlines from usage strings
+ """
def format_usage(self, usage):
return usage
@@ -87,16 +138,23 @@ def main(argv=None):
if len(arguments) == 0:
arguments.append("localhost")
- console = EventConsole()
- session = Session(console, rcvObjects=False, rcvHeartbeats=options.heartbeats, manageConnections=True)
- brokers = []
+ brokers = []
+ mechanism = options.sasl_mechanism
+ props = {'qpid.ha-admin' : 1}
+ printer = Printer()
+
+ if options.heartbeats:
+ props['heartbeat'] = 5
+
try:
try:
for host in arguments:
- brokers.append(session.addBroker(host, None, options.sasl_mechanism))
+ er = EventReceiver(printer, host, mechanism, props)
+ brokers.append(er)
+ er.start()
- while (True):
- sleep(10)
+ while (True):
+ sleep(10)
except KeyboardInterrupt:
print
@@ -106,9 +164,10 @@ def main(argv=None):
print "Failed: %s - %s" % (e.__class__.__name__, e)
return 1
finally:
- while len(brokers):
- b = brokers.pop()
- session.delBroker(b)
+ for b in brokers:
+ b.cancel()
+ for b in brokers:
+ b.join()
if __name__ == '__main__':
sys.exit(main())
diff --git a/qpid/tools/src/py/qpid-stat b/qpid/tools/src/py/qpid-stat
index 5a816baf6e..cd2633756e 100755
--- a/qpid/tools/src/py/qpid-stat
+++ b/qpid/tools/src/py/qpid-stat
@@ -52,7 +52,16 @@ def OptionsAndArguments(argv):
global config
- parser = OptionParser(usage="usage: %prog [options] -[gcequm] [object-name]")
+ usage = \
+"""%prog -g [options]
+ %prog -c [options]
+ %prog -e [options]
+ %prog -q [options] [queue-name]
+ %prog -u [options]
+ %prog -m [options]
+ %prog --acl [options]"""
+
+ parser = OptionParser(usage=usage)
group1 = OptionGroup(parser, "General Options")
group1.add_option("-b", "--broker", action="store", type="string", default="localhost", metavar="<url>",
@@ -64,7 +73,7 @@ def OptionsAndArguments(argv):
group1.add_option("--ha-admin", action="store_true", help="Allow connection to a HA backup broker.")
parser.add_option_group(group1)
- group2 = OptionGroup(parser, "Display Options")
+ group2 = OptionGroup(parser, "Command Options")
group2.add_option("-g", "--general", help="Show General Broker Stats", action="store_const", const="g", dest="show")
group2.add_option("-c", "--connections", help="Show Connections", action="store_const", const="c", dest="show")
group2.add_option("-e", "--exchanges", help="Show Exchanges", action="store_const", const="e", dest="show")
@@ -72,12 +81,14 @@ def OptionsAndArguments(argv):
group2.add_option("-u", "--subscriptions", help="Show Subscriptions", action="store_const", const="u", dest="show")
group2.add_option("-m", "--memory", help="Show Broker Memory Stats", action="store_const", const="m", dest="show")
group2.add_option( "--acl", help="Show Access Control List Stats", action="store_const", const="acl", dest="show")
- group2.add_option("-S", "--sort-by", metavar="<colname>", help="Sort by column name")
- group2.add_option("-I", "--increasing", action="store_true", default=False, help="Sort by increasing value (default = decreasing)")
- group2.add_option("-L", "--limit", type="int", default=50, metavar="<n>", help="Limit output to n rows")
-
parser.add_option_group(group2)
+ group3 = OptionGroup(parser, "Display Options")
+ group3.add_option("-S", "--sort-by", metavar="<colname>", help="Sort by column name")
+ group3.add_option("-I", "--increasing", action="store_true", default=False, help="Sort by increasing value (default = decreasing)")
+ group3.add_option("-L", "--limit", type="int", default=50, metavar="<n>", help="Limit output to n rows")
+ parser.add_option_group(group3)
+
opts, args = parser.parse_args(args=argv)
if not opts.show:
@@ -416,7 +427,8 @@ class BrokerManager:
heads.append(Header("acked", Header.Y))
heads.append(Header("excl", Header.Y))
heads.append(Header("creditMode"))
- heads.append(Header("delivered", Header.KMG))
+ heads.append(Header("delivered", Header.COMMAS))
+ heads.append(Header("sessUnacked", Header.COMMAS))
rows = []
subscriptions = self.broker.getAllSubscriptions()
sessions = self.getSessionMap()
@@ -436,6 +448,7 @@ class BrokerManager:
row.append(s.exclusive)
row.append(s.creditMode)
row.append(s.delivered)
+ row.append(session.unackedMessages)
rows.append(row)
except:
pass
diff --git a/qpid/tools/src/py/qpid-tool b/qpid/tools/src/py/qpid-tool
index af948b13a9..b31d93594c 100755
--- a/qpid/tools/src/py/qpid-tool
+++ b/qpid/tools/src/py/qpid-tool
@@ -455,6 +455,7 @@ class QmfData(Console):
rows.append(row)
else:
print "No object found with ID %d" % dispId
+ return
finally:
self.lock.release()
self.disp.table(caption, heads, rows)
diff --git a/qpid/tools/src/py/qpidtoollibs/broker.py b/qpid/tools/src/py/qpidtoollibs/broker.py
index c2340de9dd..d34c2e6ced 100644
--- a/qpid/tools/src/py/qpidtoollibs/broker.py
+++ b/qpid/tools/src/py/qpidtoollibs/broker.py
@@ -18,6 +18,7 @@
#
from qpid.messaging import Message
+from qpidtoollibs.disp import TimeLong
try:
from uuid import uuid4
except ImportError:
@@ -190,6 +191,9 @@ class BrokerAgent(object):
def getAcl(self):
return self._getSingleObject(Acl)
+ def getMemory(self):
+ return self._getSingleObject(Memory)
+
def echo(self, sequence, body):
"""Request a response to test the path to the management broker"""
pass
@@ -268,6 +272,20 @@ class BrokerAgent(object):
def reloadAclFile(self):
self._method('reloadACLFile', {}, "org.apache.qpid.acl:acl:org.apache.qpid.broker:broker:amqp-broker")
+ def acl_lookup(self, userName, action, aclObj, aclObjName, propMap):
+ args = {'userId': userName,
+ 'action': action,
+ 'object': aclObj,
+ 'objectName': aclObjName,
+ 'propertyMap': propMap}
+ return self._method('Lookup', args, "org.apache.qpid.acl:acl:org.apache.qpid.broker:broker:amqp-broker")
+
+ def acl_lookupPublish(self, userName, exchange, key):
+ args = {'userId': userName,
+ 'exchangeName': exchange,
+ 'routingKey': key}
+ return self._method('LookupPublish', args, "org.apache.qpid.acl:acl:org.apache.qpid.broker:broker:amqp-broker")
+
def create(self, _type, name, properties, strict):
"""Create an object of the specified type"""
pass
@@ -281,6 +299,41 @@ class BrokerAgent(object):
return self._getBrokerObject(self, _type, oid)
+class EventHelper(object):
+ def eventAddress(self, pkg='*', cls='*', sev='*'):
+ return "qmf.default.topic/agent.ind.event.%s.%s.%s.#" % (pkg.replace('.', '_'), cls, sev)
+
+ def event(self, msg):
+ return BrokerEvent(msg)
+
+
+class BrokerEvent(object):
+ def __init__(self, msg):
+ self.msg = msg
+ self.content = msg.content[0]
+ self.values = self.content['_values']
+ self.schema_id = self.content['_schema_id']
+ self.name = "%s:%s" % (self.schema_id['_package_name'], self.schema_id['_class_name'])
+
+ def __repr__(self):
+ rep = "%s %s" % (TimeLong(self.getTimestamp()), self.name)
+ for k,v in self.values.items():
+ rep = rep + " %s=%s" % (k, v)
+ return rep
+
+ def __getattr__(self, key):
+ if key not in self.values:
+ return None
+ value = self.values[key]
+ return value
+
+ def getAttributes(self):
+ return self.values
+
+ def getTimestamp(self):
+ return self.content['_timestamp']
+
+
class BrokerObject(object):
def __init__(self, broker, content):
self.broker = broker
@@ -348,7 +401,7 @@ class Connection(BrokerObject):
BrokerObject.__init__(self, broker, values)
def close(self):
- pass
+ self.broker._method("close", {}, "org.apache.qpid.broker:connection:%s" % self.address)
class Session(BrokerObject):
def __init__(self, broker, values):
diff --git a/qpid/wcf/src/Apache/Qpid/DtcPlugin/DtcPlugin.cpp b/qpid/wcf/src/Apache/Qpid/DtcPlugin/DtcPlugin.cpp
index 33d125e3c6..ec98289923 100644
--- a/qpid/wcf/src/Apache/Qpid/DtcPlugin/DtcPlugin.cpp
+++ b/qpid/wcf/src/Apache/Qpid/DtcPlugin/DtcPlugin.cpp
@@ -49,6 +49,7 @@
#include "qpid/client/AsyncSession.h"
#include "qpid/client/Connection.h"
+#include "qpid/framing/FieldValue.h"
#include <map>
@@ -472,13 +473,15 @@ INT ResourceManager::recover(XID *xids, long count, long flags) {
try {
// status if we can't talk to the broker
status = XAER_RMFAIL;
- std::vector<std::string> wireFormatXids;
DtxRecoverResult dtxrr = qpidSession.dtxRecover(true);
// status if we can't process the xids
status = XAER_RMERR;
- dtxrr.getInDoubt().collect(wireFormatXids);
+
+ std::vector<std::string> wireFormatXids(dtxrr.getInDoubt().size());
+ std::transform(dtxrr.getInDoubt().begin(), dtxrr.getInDoubt().end(), wireFormatXids.begin(), Array::get<std::string, Array::ValuePtr>);
+
size_t nXids = wireFormatXids.size();
if (nXids > 0) {