summaryrefslogtreecommitdiff
path: root/cpp
diff options
context:
space:
mode:
authorKim van der Riet <kpvdr@apache.org>2012-08-03 12:13:32 +0000
committerKim van der Riet <kpvdr@apache.org>2012-08-03 12:13:32 +0000
commitd43d1912b376322e27fdcda551a73f9ff5487972 (patch)
treece493e10baa95f44be8beb5778ce51783463196d /cpp
parent04877fec0c6346edec67072d7f2d247740cf2af5 (diff)
downloadqpid-python-d43d1912b376322e27fdcda551a73f9ff5487972.tar.gz
QPID-3858: Updated branch - merged from trunk r.1368650
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/asyncstore@1368910 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'cpp')
-rw-r--r--cpp/bindings/qmf/ruby/CMakeLists.txt5
-rw-r--r--cpp/bindings/qmf2/ruby/CMakeLists.txt5
-rw-r--r--cpp/bindings/qpid/python/python.i124
-rw-r--r--cpp/bindings/qpid/ruby/CMakeLists.txt5
-rw-r--r--cpp/bindings/qpid/ruby/LICENSE29
-rw-r--r--cpp/bindings/qpid/ruby/README.rdoc2
-rw-r--r--cpp/bindings/qpid/ruby/features/creating_a_receiver.feature4
-rw-r--r--cpp/bindings/qpid/ruby/features/creating_a_sender.feature6
-rw-r--r--cpp/bindings/qpid/ruby/features/receiving_a_message.feature10
-rw-r--r--cpp/bindings/qpid/ruby/features/sending_a_message.feature6
-rw-r--r--cpp/bindings/qpid/ruby/features/step_definitions/address_steps.rb7
-rw-r--r--cpp/bindings/qpid/ruby/features/step_definitions/receiver_steps.rb8
-rw-r--r--cpp/design_docs/log-model-category-for-correlation.txt112
-rw-r--r--cpp/etc/Makefile.am4
-rw-r--r--cpp/include/qmf/BrokerImportExport.h (renamed from cpp/src/qmf/BrokerImportExport.h)0
-rw-r--r--cpp/include/qmf/posix/EventNotifier.h2
-rw-r--r--cpp/include/qpid/Options.h9
-rw-r--r--cpp/include/qpid/RangeSet.h10
-rw-r--r--cpp/include/qpid/amqp_0_10/Codecs.h2
-rw-r--r--cpp/include/qpid/client/SessionBase_0_10.h13
-rw-r--r--cpp/include/qpid/client/SubscriptionSettings.h14
-rw-r--r--cpp/include/qpid/framing/Array.h2
-rw-r--r--cpp/include/qpid/framing/Buffer.h50
-rw-r--r--cpp/include/qpid/framing/ProtocolVersion.h2
-rw-r--r--cpp/include/qpid/framing/SequenceNumber.h12
-rw-r--r--cpp/include/qpid/framing/StructHelper.h2
-rw-r--r--cpp/include/qpid/framing/Uuid.h3
-rw-r--r--cpp/include/qpid/framing/amqp_types.h2
-rw-r--r--cpp/include/qpid/log/Logger.h2
-rw-r--r--cpp/include/qpid/log/Options.h2
-rw-r--r--cpp/include/qpid/log/Selector.h27
-rw-r--r--cpp/include/qpid/log/Statement.h170
-rw-r--r--cpp/include/qpid/management/Buffer.h3
-rw-r--r--cpp/include/qpid/sys/SystemInfo.h88
-rwxr-xr-xcpp/managementgen/qmf-gen7
-rwxr-xr-xcpp/managementgen/qmfgen/generate.py3
-rw-r--r--cpp/managementgen/qmfgen/templates/Class.cpp25
-rw-r--r--cpp/src/CMakeLists.txt82
-rw-r--r--cpp/src/Makefile.am9
-rw-r--r--cpp/src/ha.mk27
-rw-r--r--cpp/src/posix/QpiddBroker.cpp10
-rw-r--r--cpp/src/qmf.mk4
-rw-r--r--cpp/src/qmf/AgentSession.cpp25
-rw-r--r--cpp/src/qmf/AgentSessionImpl.h73
-rw-r--r--cpp/src/qmf/ConsoleSessionImpl.h2
-rw-r--r--cpp/src/qmf/EventNotifierImpl.cpp4
-rw-r--r--cpp/src/qmf/PrivateImplRef.h8
-rw-r--r--cpp/src/qpid/RefCounted.h12
-rw-r--r--cpp/src/qpid/acl/Acl.cpp12
-rw-r--r--cpp/src/qpid/acl/Acl.h11
-rw-r--r--cpp/src/qpid/acl/AclConnectionCounter.cpp226
-rw-r--r--cpp/src/qpid/acl/AclConnectionCounter.h36
-rw-r--r--cpp/src/qpid/acl/AclData.cpp81
-rw-r--r--cpp/src/qpid/acl/AclData.h38
-rw-r--r--cpp/src/qpid/acl/AclPlugin.cpp8
-rw-r--r--cpp/src/qpid/acl/AclReader.cpp55
-rw-r--r--cpp/src/qpid/acl/AclReader.h1
-rw-r--r--cpp/src/qpid/acl/AclTopicMatch.h89
-rw-r--r--cpp/src/qpid/acl/AclValidator.cpp2
-rw-r--r--cpp/src/qpid/acl/AclValidator.h2
-rw-r--r--cpp/src/qpid/acl/management-schema.xml17
-rw-r--r--cpp/src/qpid/amqp_0_10/Codecs.cpp344
-rw-r--r--cpp/src/qpid/broker/AclModule.h6
-rw-r--r--cpp/src/qpid/broker/Bridge.cpp155
-rw-r--r--cpp/src/qpid/broker/Bridge.h54
-rw-r--r--cpp/src/qpid/broker/Broker.cpp266
-rw-r--r--cpp/src/qpid/broker/Broker.h46
-rw-r--r--cpp/src/qpid/broker/ConfigurationObserver.h61
-rw-r--r--cpp/src/qpid/broker/ConfigurationObservers.h72
-rw-r--r--cpp/src/qpid/broker/Connection.cpp68
-rw-r--r--cpp/src/qpid/broker/Connection.h30
-rw-r--r--cpp/src/qpid/broker/ConnectionFactory.cpp11
-rw-r--r--cpp/src/qpid/broker/ConnectionHandler.cpp53
-rw-r--r--cpp/src/qpid/broker/ConnectionHandler.h4
-rw-r--r--cpp/src/qpid/broker/ConnectionObservers.h28
-rw-r--r--cpp/src/qpid/broker/Consumer.h2
-rw-r--r--cpp/src/qpid/broker/Daemon.h1
-rw-r--r--cpp/src/qpid/broker/DirectExchange.cpp3
-rw-r--r--cpp/src/qpid/broker/Exchange.cpp4
-rw-r--r--cpp/src/qpid/broker/Exchange.h5
-rw-r--r--cpp/src/qpid/broker/ExchangeRegistry.cpp78
-rw-r--r--cpp/src/qpid/broker/FanOutExchange.cpp3
-rw-r--r--cpp/src/qpid/broker/HeadersExchange.cpp3
-rw-r--r--cpp/src/qpid/broker/Link.cpp336
-rw-r--r--cpp/src/qpid/broker/Link.h47
-rw-r--r--cpp/src/qpid/broker/LinkRegistry.cpp254
-rw-r--r--cpp/src/qpid/broker/LinkRegistry.h62
-rw-r--r--cpp/src/qpid/broker/Message.cpp12
-rw-r--r--cpp/src/qpid/broker/Message.h1
-rw-r--r--cpp/src/qpid/broker/MessageDeque.cpp45
-rw-r--r--cpp/src/qpid/broker/MessageDeque.h2
-rw-r--r--cpp/src/qpid/broker/MessageMap.cpp11
-rw-r--r--cpp/src/qpid/broker/MessageMap.h3
-rw-r--r--cpp/src/qpid/broker/Messages.h9
-rw-r--r--cpp/src/qpid/broker/NameGenerator.h1
-rw-r--r--cpp/src/qpid/broker/Observers.h69
-rw-r--r--cpp/src/qpid/broker/PriorityQueue.cpp4
-rw-r--r--cpp/src/qpid/broker/PriorityQueue.h1
-rw-r--r--cpp/src/qpid/broker/PrivateImplRef.h8
-rw-r--r--cpp/src/qpid/broker/Queue.cpp117
-rw-r--r--cpp/src/qpid/broker/Queue.h20
-rw-r--r--cpp/src/qpid/broker/QueueFlowLimit.cpp4
-rw-r--r--cpp/src/qpid/broker/QueuePolicy.cpp4
-rw-r--r--cpp/src/qpid/broker/QueueRegistry.cpp66
-rw-r--r--cpp/src/qpid/broker/QueueRegistry.h18
-rw-r--r--cpp/src/qpid/broker/RecoveryManagerImpl.cpp8
-rw-r--r--cpp/src/qpid/broker/SaslAuthenticator.cpp78
-rw-r--r--cpp/src/qpid/broker/SaslAuthenticator.h2
-rw-r--r--cpp/src/qpid/broker/SecureConnectionFactory.cpp11
-rw-r--r--cpp/src/qpid/broker/SemanticState.cpp12
-rw-r--r--cpp/src/qpid/broker/SemanticState.h4
-rw-r--r--cpp/src/qpid/broker/SessionAdapter.cpp32
-rw-r--r--cpp/src/qpid/broker/SessionAdapter.h3
-rw-r--r--cpp/src/qpid/broker/SessionContext.h1
-rw-r--r--cpp/src/qpid/broker/SessionHandler.cpp36
-rw-r--r--cpp/src/qpid/broker/SessionHandler.h21
-rw-r--r--cpp/src/qpid/broker/SessionState.cpp2
-rw-r--r--cpp/src/qpid/broker/SessionState.h5
-rw-r--r--cpp/src/qpid/broker/System.cpp10
-rw-r--r--cpp/src/qpid/broker/System.h17
-rw-r--r--cpp/src/qpid/broker/TopicExchange.cpp369
-rw-r--r--cpp/src/qpid/broker/TopicExchange.h126
-rw-r--r--cpp/src/qpid/broker/TopicKeyNode.h371
-rw-r--r--cpp/src/qpid/broker/windows/SaslAuthenticator.cpp4
-rw-r--r--cpp/src/qpid/broker/windows/SslProtocolFactory.cpp53
-rw-r--r--cpp/src/qpid/client/Connection.cpp2
-rw-r--r--cpp/src/qpid/client/ConnectionHandler.cpp6
-rw-r--r--cpp/src/qpid/client/PrivateImplRef.h8
-rw-r--r--cpp/src/qpid/client/SessionImpl.cpp2
-rw-r--r--cpp/src/qpid/client/SslConnector.cpp3
-rw-r--r--cpp/src/qpid/client/SubscriptionManagerImpl.cpp10
-rw-r--r--cpp/src/qpid/client/SubscriptionManagerImpl.h3
-rw-r--r--cpp/src/qpid/client/TCPConnector.cpp6
-rw-r--r--cpp/src/qpid/client/TCPConnector.h4
-rw-r--r--cpp/src/qpid/client/amqp0_10/AddressResolution.cpp8
-rw-r--r--cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp2
-rw-r--r--cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp15
-rw-r--r--cpp/src/qpid/client/amqp0_10/OutgoingMessage.h1
-rw-r--r--cpp/src/qpid/client/amqp0_10/ReceiverImpl.h1
-rw-r--r--cpp/src/qpid/client/amqp0_10/SenderImpl.cpp8
-rw-r--r--cpp/src/qpid/client/amqp0_10/SenderImpl.h12
-rw-r--r--cpp/src/qpid/client/windows/SslConnector.cpp6
-rw-r--r--cpp/src/qpid/cluster/Connection.cpp29
-rw-r--r--cpp/src/qpid/cluster/Connection.h10
-rw-r--r--cpp/src/qpid/cluster/ConnectionCodec.cpp14
-rw-r--r--cpp/src/qpid/cluster/Cpg.cpp54
-rw-r--r--cpp/src/qpid/cluster/Cpg.h10
-rw-r--r--cpp/src/qpid/cluster/UpdateClient.cpp18
-rw-r--r--cpp/src/qpid/cluster/types.h23
-rw-r--r--cpp/src/qpid/console/ClassKey.cpp1
-rw-r--r--cpp/src/qpid/framing/AMQCommandControlBody.h70
-rw-r--r--cpp/src/qpid/framing/AMQContentBody.cpp2
-rw-r--r--cpp/src/qpid/framing/AMQContentBody.h8
-rw-r--r--cpp/src/qpid/framing/AMQFrame.cpp4
-rw-r--r--cpp/src/qpid/framing/Buffer.cpp16
-rw-r--r--cpp/src/qpid/framing/FieldTable.cpp2
-rw-r--r--cpp/src/qpid/framing/ProtocolInitiation.cpp2
-rw-r--r--cpp/src/qpid/framing/Uuid.cpp7
-rw-r--r--cpp/src/qpid/ha/AlternateExchangeSetter.h73
-rw-r--r--cpp/src/qpid/ha/Backup.cpp82
-rw-r--r--cpp/src/qpid/ha/Backup.h6
-rw-r--r--cpp/src/qpid/ha/BackupConnectionExcluder.h (renamed from cpp/src/qpid/ha/ConnectionExcluder.h)30
-rw-r--r--cpp/src/qpid/ha/BrokerInfo.cpp118
-rw-r--r--cpp/src/qpid/ha/BrokerInfo.h84
-rw-r--r--cpp/src/qpid/ha/BrokerReplicator.cpp420
-rw-r--r--cpp/src/qpid/ha/BrokerReplicator.h46
-rw-r--r--cpp/src/qpid/ha/ConnectionExcluder.cpp40
-rw-r--r--cpp/src/qpid/ha/ConnectionObserver.cpp94
-rw-r--r--cpp/src/qpid/ha/ConnectionObserver.h74
-rw-r--r--cpp/src/qpid/ha/HaBroker.cpp300
-rw-r--r--cpp/src/qpid/ha/HaBroker.h74
-rw-r--r--cpp/src/qpid/ha/HaPlugin.cpp32
-rw-r--r--cpp/src/qpid/ha/Membership.cpp85
-rw-r--r--cpp/src/qpid/ha/Membership.h68
-rw-r--r--cpp/src/qpid/ha/Primary.cpp252
-rw-r--r--cpp/src/qpid/ha/Primary.h115
-rw-r--r--cpp/src/qpid/ha/QueueGuard.cpp139
-rw-r--r--cpp/src/qpid/ha/QueueGuard.h118
-rw-r--r--cpp/src/qpid/ha/QueueRange.h85
-rw-r--r--cpp/src/qpid/ha/QueueReplicator.cpp115
-rw-r--r--cpp/src/qpid/ha/QueueReplicator.h15
-rw-r--r--cpp/src/qpid/ha/RemoteBackup.cpp120
-rw-r--r--cpp/src/qpid/ha/RemoteBackup.h111
-rw-r--r--cpp/src/qpid/ha/ReplicateLevel.cpp72
-rw-r--r--cpp/src/qpid/ha/ReplicateLevel.h52
-rw-r--r--cpp/src/qpid/ha/ReplicatingSubscription.cpp400
-rw-r--r--cpp/src/qpid/ha/ReplicatingSubscription.h107
-rw-r--r--cpp/src/qpid/ha/ReplicationTest.cpp75
-rw-r--r--cpp/src/qpid/ha/ReplicationTest.h67
-rw-r--r--cpp/src/qpid/ha/Settings.h10
-rw-r--r--cpp/src/qpid/ha/management-schema.xml32
-rw-r--r--cpp/src/qpid/ha/types.cpp80
-rw-r--r--cpp/src/qpid/ha/types.h109
-rw-r--r--cpp/src/qpid/log/Logger.cpp7
-rw-r--r--cpp/src/qpid/log/Options.cpp18
-rw-r--r--cpp/src/qpid/log/Selector.cpp31
-rw-r--r--cpp/src/qpid/log/Statement.cpp61
-rw-r--r--cpp/src/qpid/log/posix/SinkOptions.cpp5
-rw-r--r--cpp/src/qpid/management/Buffer.cpp3
-rw-r--r--cpp/src/qpid/management/ManagementAgent.cpp17
-rw-r--r--cpp/src/qpid/management/ManagementDirectExchange.cpp4
-rw-r--r--cpp/src/qpid/management/ManagementTopicExchange.cpp6
-rw-r--r--cpp/src/qpid/messaging/PrivateImplRef.h8
-rw-r--r--cpp/src/qpid/replication/ReplicationExchange.cpp2
-rw-r--r--cpp/src/qpid/sys/AsynchIOHandler.cpp43
-rw-r--r--cpp/src/qpid/sys/AsynchIOHandler.h10
-rw-r--r--cpp/src/qpid/sys/MemStat.cpp (renamed from cpp/src/qpid/sys/windows/MemStat.cpp)4
-rw-r--r--cpp/src/qpid/sys/SslPlugin.cpp28
-rw-r--r--cpp/src/qpid/sys/TCPIOPlugin.cpp21
-rw-r--r--cpp/src/qpid/sys/Timer.cpp55
-rw-r--r--cpp/src/qpid/sys/Timer.h7
-rw-r--r--cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp1
-rwxr-xr-xcpp/src/qpid/sys/posix/LockFile.cpp2
-rw-r--r--cpp/src/qpid/sys/posix/MemStat.cpp2
-rw-r--r--cpp/src/qpid/sys/posix/SocketAddress.cpp6
-rwxr-xr-xcpp/src/qpid/sys/posix/SystemInfo.cpp108
-rw-r--r--cpp/src/qpid/sys/ssl/SslHandler.cpp41
-rw-r--r--cpp/src/qpid/sys/ssl/SslHandler.h9
-rw-r--r--cpp/src/qpid/sys/ssl/SslIo.cpp12
-rw-r--r--cpp/src/qpid/sys/ssl/SslIo.h3
-rw-r--r--cpp/src/qpid/sys/unordered_map.h2
-rwxr-xr-xcpp/src/qpid/sys/windows/IocpPoller.cpp5
-rw-r--r--cpp/src/qpid/sys/windows/Socket.cpp17
-rwxr-xr-xcpp/src/qpid/sys/windows/SystemInfo.cpp10
-rw-r--r--cpp/src/qpid/types/Variant.cpp72
-rw-r--r--cpp/src/qpid/xml/XmlExchange.cpp34
-rw-r--r--cpp/src/qpid/xml/XmlExchange.h4
-rw-r--r--cpp/src/tests/CMakeLists.txt1
-rw-r--r--cpp/src/tests/ExchangeTest.cpp2
-rw-r--r--cpp/src/tests/FieldTable.cpp9
-rw-r--r--cpp/src/tests/Makefile.am12
-rw-r--r--cpp/src/tests/MessageBuilderTest.cpp6
-rw-r--r--cpp/src/tests/MessageTest.cpp9
-rw-r--r--cpp/src/tests/MessageUtils.h4
-rw-r--r--cpp/src/tests/MessagingSessionTests.cpp18
-rw-r--r--cpp/src/tests/QueueTest.cpp244
-rw-r--r--cpp/src/tests/RangeSet.cpp134
-rw-r--r--cpp/src/tests/ReplicationTest.cpp4
-rw-r--r--cpp/src/tests/SystemInfo.cpp52
-rw-r--r--cpp/src/tests/TestMessageStore.h2
-rw-r--r--cpp/src/tests/TimerTest.cpp2
-rw-r--r--cpp/src/tests/TopicExchangeTest.cpp14
-rw-r--r--cpp/src/tests/TxPublishTest.cpp4
-rw-r--r--cpp/src/tests/Uuid.cpp12
-rwxr-xr-xcpp/src/tests/acl.py148
-rw-r--r--cpp/src/tests/asyncstore.cmake3
-rw-r--r--cpp/src/tests/brokertest.py37
-rwxr-xr-xcpp/src/tests/cluster_test_logs.py3
-rwxr-xr-xcpp/src/tests/cluster_tests.py189
-rwxr-xr-xcpp/src/tests/federation.py467
-rwxr-xr-xcpp/src/tests/ha_tests.py539
-rwxr-xr-xcpp/src/tests/ipv6_test13
-rw-r--r--cpp/src/tests/logging.cpp10
-rwxr-xr-xcpp/src/tests/ping_broker127
-rw-r--r--cpp/src/tests/qpid-latency-test.cpp18
-rw-r--r--cpp/src/tests/qpid-receive.cpp5
-rwxr-xr-xcpp/src/tests/run_acl_tests4
-rwxr-xr-xcpp/src/tests/run_federation_tests8
-rwxr-xr-xcpp/src/tests/run_ha_tests29
-rwxr-xr-xcpp/src/tests/sasl_test_setup.sh2
-rwxr-xr-xcpp/src/tests/ssl_test5
-rwxr-xr-xcpp/src/tests/storePerftools/asyncStorePerf_smoke_test.sh (renamed from cpp/src/tests/storePerftools/storePerftoolsSmokeTest.sh)29
-rwxr-xr-xcpp/src/tests/storePerftools/jrnl2Perf_smoke_test.sh33
-rw-r--r--cpp/src/tests/txjob.cpp6
-rw-r--r--cpp/src/tests/txshift.cpp2
-rw-r--r--cpp/xml/cluster.xml1
266 files changed, 9018 insertions, 3081 deletions
diff --git a/cpp/bindings/qmf/ruby/CMakeLists.txt b/cpp/bindings/qmf/ruby/CMakeLists.txt
index 4b6ba2c1c3..702606139b 100644
--- a/cpp/bindings/qmf/ruby/CMakeLists.txt
+++ b/cpp/bindings/qmf/ruby/CMakeLists.txt
@@ -21,13 +21,12 @@
## Use Swig to generate a literal binding to the C++ API
##------------------------------------------------------
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ruby.i PROPERTIES CPLUSPLUS ON)
-set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ruby.i PROPERTIES SWIG_FLAGS "-I${qpid-cpp_SOURCE_DIR}/include")
+
+include_directories(${RUBY_INCLUDE_DIRS} ${qpid-cpp_SOURCE_DIR}/include)
swig_add_module(qmfengine_ruby ruby ${CMAKE_CURRENT_SOURCE_DIR}/ruby.i)
swig_link_libraries(qmfengine_ruby qmf qmfconsole ${RUBY_LIBRARY})
-set_source_files_properties(${swig_generated_file_fullname} PROPERTIES COMPILE_FLAGS "-I${RUBY_INCLUDE_DIR} -I${qpid-cpp_SOURCE_DIR}/include")
-
##----------------------------------
## Install the complete Ruby binding
##----------------------------------
diff --git a/cpp/bindings/qmf2/ruby/CMakeLists.txt b/cpp/bindings/qmf2/ruby/CMakeLists.txt
index 3bc97cf35f..1cb969f7dc 100644
--- a/cpp/bindings/qmf2/ruby/CMakeLists.txt
+++ b/cpp/bindings/qmf2/ruby/CMakeLists.txt
@@ -21,13 +21,12 @@
## Use Swig to generate a literal binding to the C++ API
##------------------------------------------------------
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ruby.i PROPERTIES CPLUSPLUS ON)
-set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ruby.i PROPERTIES SWIG_FLAGS "-I${qpid-cpp_SOURCE_DIR}/include")
+
+include_directories(${RUBY_INCLUDE_DIRS} ${qpid-cpp_SOURCE_DIR}/include)
swig_add_module(cqmf2_ruby ruby ${CMAKE_CURRENT_SOURCE_DIR}/ruby.i)
swig_link_libraries(cqmf2_ruby qmf2 ${RUBY_LIBRARY})
-set_source_files_properties(${swig_generated_file_fullname} PROPERTIES COMPILE_FLAGS "-I${RUBY_INCLUDE_DIR} -I${qpid-cpp_SOURCE_DIR}/include")
-
##----------------------------------
## Install the complete Ruby binding
##----------------------------------
diff --git a/cpp/bindings/qpid/python/python.i b/cpp/bindings/qpid/python/python.i
index a53cf3b853..4d8a64b376 100644
--- a/cpp/bindings/qpid/python/python.i
+++ b/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());
diff --git a/cpp/bindings/qpid/ruby/CMakeLists.txt b/cpp/bindings/qpid/ruby/CMakeLists.txt
index 8a8c88b595..9b32ff5728 100644
--- a/cpp/bindings/qpid/ruby/CMakeLists.txt
+++ b/cpp/bindings/qpid/ruby/CMakeLists.txt
@@ -30,12 +30,13 @@ set(GEM_OUTPUT_FILE ${GEM_OUTPUT_PATH}/pkg/qpid-${qpidc_version}.0.gem)
## Use Swig to generate a literal binding to the C++ API
##------------------------------------------------------
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ruby.i PROPERTIES CPLUSPLUS ON)
-set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ruby.i PROPERTIES SWIG_FLAGS "-I${qpid-cpp_SOURCE_DIR}/include")
+
+include_directories(${RUBY_INCLUDE_DIRS} ${qpid-cpp_SOURCE_DIR}/include)
swig_add_module(cqpid_ruby ruby ${CMAKE_CURRENT_SOURCE_DIR}/ruby.i)
swig_link_libraries(cqpid_ruby qpidmessaging qpidtypes qmf2 ${RUBY_LIBRARY})
-set_source_files_properties(${swig_generated_file_fullname} PROPERTIES COMPILE_FLAGS "-fno-strict-aliasing -I${RUBY_INCLUDE_DIR} -I${qpid-cpp_SOURCE_DIR}/include")
+set_source_files_properties(${swig_generated_file_fullname} PROPERTIES COMPILE_FLAGS "-fno-strict-aliasing")
##----------------------------------
## Install the complete Ruby binding
diff --git a/cpp/bindings/qpid/ruby/LICENSE b/cpp/bindings/qpid/ruby/LICENSE
index cff2a5e25d..232fd660d6 100644
--- a/cpp/bindings/qpid/ruby/LICENSE
+++ b/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/cpp/bindings/qpid/ruby/README.rdoc b/cpp/bindings/qpid/ruby/README.rdoc
index 478fc939d9..5c60a15588 100644
--- a/cpp/bindings/qpid/ruby/README.rdoc
+++ b/cpp/bindings/qpid/ruby/README.rdoc
@@ -2,7 +2,7 @@
Qpid is an cross-platform enterprise messaging system.
-Version :: 0.17.0
+Version :: 0.19.0
= Links
diff --git a/cpp/bindings/qpid/ruby/features/creating_a_receiver.feature b/cpp/bindings/qpid/ruby/features/creating_a_receiver.feature
index f509f49115..1f758153af 100644
--- a/cpp/bindings/qpid/ruby/features/creating_a_receiver.feature
+++ b/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/cpp/bindings/qpid/ruby/features/creating_a_sender.feature b/cpp/bindings/qpid/ruby/features/creating_a_sender.feature
index ac75543c2d..1c09ff837d 100644
--- a/cpp/bindings/qpid/ruby/features/creating_a_sender.feature
+++ b/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/cpp/bindings/qpid/ruby/features/receiving_a_message.feature b/cpp/bindings/qpid/ruby/features/receiving_a_message.feature
index b68a78c337..7b6db4a5ac 100644
--- a/cpp/bindings/qpid/ruby/features/receiving_a_message.feature
+++ b/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/cpp/bindings/qpid/ruby/features/sending_a_message.feature b/cpp/bindings/qpid/ruby/features/sending_a_message.feature
index b1127d3664..45cbd42f06 100644
--- a/cpp/bindings/qpid/ruby/features/sending_a_message.feature
+++ b/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/cpp/bindings/qpid/ruby/features/step_definitions/address_steps.rb b/cpp/bindings/qpid/ruby/features/step_definitions/address_steps.rb
index 845cc2b116..0531e5ee69 100644
--- a/cpp/bindings/qpid/ruby/features/step_definitions/address_steps.rb
+++ b/cpp/bindings/qpid/ruby/features/step_definitions/address_steps.rb
@@ -22,3 +22,10 @@ Given /^an Address with the name "([^"]*)" and subject "([^"]*)" and option "([^
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/cpp/bindings/qpid/ruby/features/step_definitions/receiver_steps.rb b/cpp/bindings/qpid/ruby/features/step_definitions/receiver_steps.rb
index a8c8aa4a43..e454dac345 100644
--- a/cpp/bindings/qpid/ruby/features/step_definitions/receiver_steps.rb
+++ b/cpp/bindings/qpid/ruby/features/step_definitions/receiver_steps.rb
@@ -59,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/cpp/design_docs/log-model-category-for-correlation.txt b/cpp/design_docs/log-model-category-for-correlation.txt
new file mode 100644
index 0000000000..69253a417a
--- /dev/null
+++ b/cpp/design_docs/log-model-category-for-correlation.txt
@@ -0,0 +1,112 @@
+This documennt describes the new logging entries written for
+"QPID-4079 C++ Broker needs log messages to track object life cycles for auditing".
+
+Please see https://issues.apache.org/jira/browse/QPID-4079 for an overview.
+
+The basic features are simple:
+
+* A new log category, [Model], is added and only the new log entries use it.
+
+* At 'debug' log level are log entries that mirror the corresponding management
+ events. Debug level statements include user names, remote host information, and
+ other references using the user-specified names for the referenced objects.
+
+* At 'trace' log level are log entries that track the construction and destruction
+ of managed resources. Trace level statements identify the objects using the
+ internal management keys. The trace statement for each deleted object includes the
+ management statistics for that object.
+
+Enabling the Model log
+
+* Use the switch: '--log-enable trace+:Model' to receive both flavors of log
+* Use the switch: '--log-enable debug+:Model' for a less verbose log
+
+Managed Objects in the logs
+
+All managed objects are included in the trace log.
+The debug log has information for:
+ Connection, Queue, Exchange, Binding, Subscription
+
+The following section lists actual log file data sorted and paired with the
+corresponding management Event captured with qpid-printevents.
+
+1. Connection
+
+Create connection
+event: Fri Jul 13 17:46:23 2012 org.apache.qpid.broker:clientConnect rhost=[::1]:5672-[::1]:34383 user=anonymous
+debug: 2012-07-13 13:46:23 [Model] debug Create connection. user:anonymous rhost:[::1]:5672-[::1]:34383
+trace: 2012-07-13 13:46:23 [Model] trace Mgmt create connection. id:[::1]:5672-[::1]:34383
+
+Delete connection
+event: Fri Jul 13 17:46:23 2012 org.apache.qpid.broker:clientDisconnect rhost=[::1]:5672-[::1]:34383 user=anonymous
+debug: 2012-07-13 13:46:23 [Model] debug Delete connection. user:anonymous rhost:[::1]:5672-[::1]:34383
+trace: 2012-07-13 13:46:29 [Model] trace Mgmt delete connection. id:[::1]:5672-[::1]:34383
+ Statistics: {bytesFromClient:1451, bytesToClient:892, closing:False, framesFromClient:25, framesToClient:21, msgsFromClient:1, msgsToClient:1}
+
+2. Session
+
+Create session
+event: TBD
+debug: TBD
+trace: 2012-07-13 13:46:09 [Model] trace Mgmt create session. id:18f52c22-efc5-4c2f-bd09-902d2a02b948:0
+
+Delete session
+event: TBD
+debug: TBD
+trace: 2012-07-13 13:47:13 [Model] trace Mgmt delete session. id:18f52c22-efc5-4c2f-bd09-902d2a02b948:0
+ Statistics: {TxnCommits:0, TxnCount:0, TxnRejects:0, TxnStarts:0, clientCredit:0, unackedMessages:0}
+
+
+3. Exchange
+
+Create exchange
+event: Fri Jul 13 17:46:34 2012 org.apache.qpid.broker:exchangeDeclare disp=created exName=myE exType=topic durable=False args={} autoDel=False rhost=[::1]:5672-[::1]:34384 altEx= user=anonymous
+debug: 2012-07-13 13:46:34 [Model] debug Create exchange. name:myE user:anonymous rhost:[::1]:5672-[::1]:34384 type:topic alternateExchange: durable:F
+trace: 2012-07-13 13:46:34 [Model] trace Mgmt create exchange. id:myE
+
+
+Delete exchange
+event: Fri Jul 13 18:19:33 2012 org.apache.qpid.broker:exchangeDelete exName=myE rhost=[::1]:5672-[::1]:37199 user=anonymous
+debug: 2012-07-13 14:19:33 [Model] debug Delete exchange. name:myE user:anonymous rhost:[::1]:5672-[::1]:37199
+trace: 2012-07-13 14:19:42 [Model] trace Mgmt delete exchange. id:myE
+ Statistics: {bindingCount:0, bindingCountHigh:0, bindingCountLow:0, byteDrops:0, byteReceives:0, byteRoutes:0, msgDrops:0, msgReceives:0, msgRoutes:0, producerCount:0, producerCountHigh:0, producerCountLow:0}
+
+
+4. Queue
+
+Create queue
+event: Fri Jul 13 18:19:35 2012 org.apache.qpid.broker:queueDeclare disp=created durable=False args={} qName=myQ autoDel=False rhost=[::1]:5672-[::1]:37200 altEx= excl=False user=anonymous
+debug: 2012-07-13 14:19:35 [Model] debug Create queue. name:myQ user:anonymous rhost:[::1]:5672-[::1]:37200 durable:F owner:0 autodelete:F alternateExchange:
+trace: 2012-07-13 14:19:35 [Model] trace Mgmt create queue. id:myQ
+
+Delete queue
+event: Fri Jul 13 18:19:37 2012 org.apache.qpid.broker:queueDelete user=anonymous qName=myQ rhost=[::1]:5672-[::1]:37201
+debug: 2012-07-13 14:19:37 [Model] debug Delete queue. name:myQ user:anonymous rhost:[::1]:5672-[::1]:37201
+trace: 2012-07-13 14:19:42 [Model] trace Mgmt delete queue. id:myQ
+ Statistics: {acquires:0, bindingCount:0, bindingCountHigh:0, bindingCountLow:0, byteDepth:0, byteFtdDepth:0, byteFtdDequeues:0, byteFtdEnqueues:0, bytePersistDequeues:0, bytePersistEnqueues:0, byteTotalDequeues:0, byteTotalEnqueues:0, byteTxnDequeues:0, byteTxnEnqueues:0, consumerCount:0, consumerCountHigh:0, consumerCountLow:0, discardsLvq:0, discardsOverflow:0, discardsPurge:0, discardsRing:0, discardsSubscriber:0, discardsTtl:0, flowStopped:False, flowStoppedCount:0, messageLatencyAvg:0, messageLatencyCount:0, messageLatencyMax:0, messageLatencyMin:0, msgDepth:0, msgFtdDepth:0, msgFtdDequeues:0, msgFtdEnqueues:0, msgPersistDequeues:0, msgPersistEnqueues:0, msgTotalDequeues:0, msgTotalEnqueues:0, msgTxnDequeues:0, msgTxnEnqueues:0, releases:0, reroutes:0, unackedMessages:0, unackedMessagesHigh:0, unackedMessagesLow:0}
+
+5. Binding
+
+Create binding
+event: Fri Jul 13 17:46:45 2012 org.apache.qpid.broker:bind exName=myE args={} qName=myQ user=anonymous key=myKey rhost=[::1]:5672-[::1]:34385
+debug: 2012-07-13 13:46:45 [Model] debug Create binding. exchange:myE queue:myQ key:myKey user:anonymous rhost:[::1]:5672-[::1]:34385
+trace: 2012-07-13 13:46:23 [Model] trace Mgmt create binding. id:org.apache.qpid.broker:exchange:,org.apache.qpid.broker:queue:myQ,myQ
+
+Delete binding
+event: Fri Jul 13 17:47:06 2012 org.apache.qpid.broker:unbind user=anonymous exName=myE qName=myQ key=myKey rhost=[::1]:5672-[::1]:34386
+debug: 2012-07-13 13:47:06 [Model] debug Delete binding. exchange:myE queue:myQ key:myKey user:anonymous rhost:[::1]:5672-[::1]:34386
+trace: 2012-07-13 13:47:09 [Model] trace Mgmt delete binding. id:org.apache.qpid.broker:exchange:myE,org.apache.qpid.broker:queue:myQ,myKey
+ Statistics: {msgMatched:0}
+
+6, Subscription
+
+Create subscription
+event: Fri Jul 13 18:19:28 2012 org.apache.qpid.broker:subscribe dest=0 args={} qName=b78b1818-7a20-4341-a253-76216b40ab4a:0.0 user=anonymous excl=False rhost=[::1]:5672-[::1]:37198
+debug: 2012-07-13 14:19:28 [Model] debug Create subscription. queue:b78b1818-7a20-4341-a253-76216b40ab4a:0.0 destination:0 user:anonymous rhost:[::1]:5672-[::1]:37198 exclusive:F
+trace: 2012-07-13 14:19:28 [Model] trace Mgmt create subscription. id:org.apache.qpid.broker:session:b78b1818-7a20-4341-a253-76216b40ab4a:0,org.apache.qpid.broker:queue:b78b1818-7a20-4341-a253-76216b40ab4a:0.0,0
+
+Delete subscription
+event: Fri Jul 13 18:19:28 2012 org.apache.qpid.broker:unsubscribe dest=0 rhost=[::1]:5672-[::1]:37198 user=anonymous
+debug: 2012-07-13 14:19:28 [Model] debug Delete subscription. destination:0 user:anonymous rhost:[::1]:5672-[::1]:37198
+trace: 2012-07-13 14:19:32 [Model] trace Mgmt delete subscription. id:org.apache.qpid.broker:session:b78b1818-7a20-4341-a253-76216b40ab4a:0,org.apache.qpid.broker:queue:b78b1818-7a20-4341-a253-76216b40ab4a:0.0,0
+ Statistics: {delivered:1}
diff --git a/cpp/etc/Makefile.am b/cpp/etc/Makefile.am
index b154a105d4..aa41c65b37 100644
--- a/cpp/etc/Makefile.am
+++ b/cpp/etc/Makefile.am
@@ -20,8 +20,8 @@ SASL_CONF = sasl2/qpidd.conf
EXTRA_DIST = \
$(SASL_CONF) \
- qpidd qpidd-primary qpidd.conf qpidc.conf CMakeLists.txt \
- cluster.conf-example.xml
+ qpidd.in qpidd-primary.in qpidd.conf qpidc.conf CMakeLists.txt \
+ cluster.conf-example.xml.in
confdir = $(sysconfdir)/qpid
nobase_conf_DATA=\
diff --git a/cpp/src/qmf/BrokerImportExport.h b/cpp/include/qmf/BrokerImportExport.h
index ee05788063..ee05788063 100644
--- a/cpp/src/qmf/BrokerImportExport.h
+++ b/cpp/include/qmf/BrokerImportExport.h
diff --git a/cpp/include/qmf/posix/EventNotifier.h b/cpp/include/qmf/posix/EventNotifier.h
index ebc1cb5364..c8b90b6421 100644
--- a/cpp/include/qmf/posix/EventNotifier.h
+++ b/cpp/include/qmf/posix/EventNotifier.h
@@ -28,7 +28,7 @@
namespace qmf {
class PosixEventNotifierImpl;
- class PosixEventNotifierImplAccess;
+ struct PosixEventNotifierImplAccess;
namespace posix {
diff --git a/cpp/include/qpid/Options.h b/cpp/include/qpid/Options.h
index 9860076195..0bbe7b704f 100644
--- a/cpp/include/qpid/Options.h
+++ b/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. */
diff --git a/cpp/include/qpid/RangeSet.h b/cpp/include/qpid/RangeSet.h
index 36991fd784..ef0ad032da 100644
--- a/cpp/include/qpid/RangeSet.h
+++ b/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/cpp/include/qpid/amqp_0_10/Codecs.h b/cpp/include/qpid/amqp_0_10/Codecs.h
index 73846f33a8..d632a9f20a 100644
--- a/cpp/include/qpid/amqp_0_10/Codecs.h
+++ b/cpp/include/qpid/amqp_0_10/Codecs.h
@@ -69,6 +69,8 @@ class QPID_COMMON_CLASS_EXTERN ListCodec
*/
QPID_COMMON_EXTERN void translate(const qpid::types::Variant::Map& from,
qpid::framing::FieldTable& to);
+QPID_COMMON_EXTERN void translate(const qpid::types::Variant::Map& from, const std::string& efield, const qpid::types::Variant& evalue,
+ qpid::framing::FieldTable& to);
QPID_COMMON_EXTERN void translate(const qpid::framing::FieldTable& from,
qpid::types::Variant::Map& to);
diff --git a/cpp/include/qpid/client/SessionBase_0_10.h b/cpp/include/qpid/client/SessionBase_0_10.h
index ea50ab32f7..630987c11d 100644
--- a/cpp/include/qpid/client/SessionBase_0_10.h
+++ b/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/cpp/include/qpid/client/SubscriptionSettings.h b/cpp/include/qpid/client/SubscriptionSettings.h
index b4cb302b56..bee39f6816 100644
--- a/cpp/include/qpid/client/SubscriptionSettings.h
+++ b/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/cpp/include/qpid/framing/Array.h b/cpp/include/qpid/framing/Array.h
index 4f82d4dbf0..6254f6271a 100644
--- a/cpp/include/qpid/framing/Array.h
+++ b/cpp/include/qpid/framing/Array.h
@@ -82,7 +82,7 @@ class QPID_COMMON_CLASS_EXTERN Array
// For use in standard algorithms
template <typename R, typename V>
static R get(const V& v) {
- return v->get<R>();
+ return v->template get<R>();
}
private:
diff --git a/cpp/include/qpid/framing/Buffer.h b/cpp/include/qpid/framing/Buffer.h
index 8b08e60762..2ccad3bd57 100644
--- a/cpp/include/qpid/framing/Buffer.h
+++ b/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/cpp/include/qpid/framing/ProtocolVersion.h b/cpp/include/qpid/framing/ProtocolVersion.h
index 30094c165d..26d628e41c 100644
--- a/cpp/include/qpid/framing/ProtocolVersion.h
+++ b/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/cpp/include/qpid/framing/SequenceNumber.h b/cpp/include/qpid/framing/SequenceNumber.h
index dd85d97a52..00fa2469c8 100644
--- a/cpp/include/qpid/framing/SequenceNumber.h
+++ b/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/cpp/include/qpid/framing/StructHelper.h b/cpp/include/qpid/framing/StructHelper.h
index 21f9b91fa9..fe2fa64ce7 100644
--- a/cpp/include/qpid/framing/StructHelper.h
+++ b/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/cpp/include/qpid/framing/Uuid.h b/cpp/include/qpid/framing/Uuid.h
index ccfd7e9534..e9e56ed7c9 100644
--- a/cpp/include/qpid/framing/Uuid.h
+++ b/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/cpp/include/qpid/framing/amqp_types.h b/cpp/include/qpid/framing/amqp_types.h
index d9088b7a12..2072a83904 100644
--- a/cpp/include/qpid/framing/amqp_types.h
+++ b/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/cpp/include/qpid/log/Logger.h b/cpp/include/qpid/log/Logger.h
index d255b7e150..9464fa52dd 100644
--- a/cpp/include/qpid/log/Logger.h
+++ b/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/cpp/include/qpid/log/Options.h b/cpp/include/qpid/log/Options.h
index 17cbfde9bc..819f2c85f1 100644
--- a/cpp/include/qpid/log/Options.h
+++ b/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/cpp/include/qpid/log/Selector.h b/cpp/include/qpid/log/Selector.h
index 061152d7e2..498d4a7342 100644
--- a/cpp/include/qpid/log/Selector.h
+++ b/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/cpp/include/qpid/log/Statement.h b/cpp/include/qpid/log/Statement.h
index 7b3ab60b81..ad84d66db6 100644
--- a/cpp/include/qpid/log/Statement.h
+++ b/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,119 @@ 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
+ * Model <not related to a directory>
+ * Unspecified <must be last in enum>
+ */
+enum Category { security, broker, management, protocol, system, ha, messaging,
+ store, network, test, client, model, 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 +177,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 +207,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 +237,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 +273,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/cpp/include/qpid/management/Buffer.h b/cpp/include/qpid/management/Buffer.h
index c32494b8c0..1ac52bf276 100644
--- a/cpp/include/qpid/management/Buffer.h
+++ b/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/cpp/include/qpid/sys/SystemInfo.h b/cpp/include/qpid/sys/SystemInfo.h
index 23594cf650..24bc099d75 100644
--- a/cpp/include/qpid/sys/SystemInfo.h
+++ b/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/cpp/managementgen/qmf-gen b/cpp/managementgen/qmf-gen
index 6e8f864256..2f0cc0d8fd 100755
--- a/cpp/managementgen/qmf-gen
+++ b/cpp/managementgen/qmf-gen
@@ -49,6 +49,8 @@ parser.add_option("-b", "--broker-plugin", dest="brokerplugin", default=False, a
help="Generate code for use in a qpid broker plugin")
parser.add_option("-2", "--v2-style", dest="v2_style", default=False, action="store_true",
help="Generate code for use with the QMFv2 Agent API")
+parser.add_option("-l", "--qpid-logs", dest="qpidlogs", default=False, action="store_true",
+ help="Generate code for QPID_LOG statements in classes constructor/destructor")
(opts, args) = parser.parse_args()
@@ -70,6 +72,11 @@ else:
vargs["agentHeaderDir"] = "agent"
vargs["genQmfV1"] = None
+if opts.qpidlogs:
+ vargs["genLogs"] = True
+else:
+ vargs["genLogs"] = False
+
for schemafile in args:
package = SchemaPackage(typefile, schemafile, opts)
diff --git a/cpp/managementgen/qmfgen/generate.py b/cpp/managementgen/qmfgen/generate.py
index 4e688e3bc7..61111be01d 100755
--- a/cpp/managementgen/qmfgen/generate.py
+++ b/cpp/managementgen/qmfgen/generate.py
@@ -304,6 +304,9 @@ class Generator:
def testGenQMFv1 (self, variables):
return variables["genQmfV1"]
+ def testGenLogs (self, variables):
+ return variables["genLogs"]
+
def genDisclaimer (self, stream, variables):
prefix = variables["commentPrefix"]
stream.write (prefix + " This source file was created by a code generator.\n")
diff --git a/cpp/managementgen/qmfgen/templates/Class.cpp b/cpp/managementgen/qmfgen/templates/Class.cpp
index fc0b9c8177..d3033db7e1 100644
--- a/cpp/managementgen/qmfgen/templates/Class.cpp
+++ b/cpp/managementgen/qmfgen/templates/Class.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
@@ -27,8 +27,12 @@
#include "qpid//*MGEN:Class.AgentHeaderLocation*//ManagementAgent.h"
#include "/*MGEN:Class.NameCap*/.h"
/*MGEN:Class.MethodArgIncludes*/
+/*MGEN:IF(Root.GenLogs)*/
+#include "qpid/log/Statement.h"
+/*MGEN:ENDIF*/
#include <iostream>
#include <sstream>
+#include <string.h>
using namespace qmf::/*MGEN:Class.Namespace*/;
using qpid::management::ManagementAgent;
@@ -58,10 +62,26 @@ uint8_t /*MGEN:Class.NameCap*/::md5Sum[MD5_LEN] =
for (int idx = 0; idx < maxThreads; idx++)
perThreadStatsArray[idx] = 0;
/*MGEN:ENDIF*/
+/*MGEN:IF(Root.GenLogs)*/
+ QPID_LOG_CAT(trace, model, "Mgmt create " << className
+ << ". id:" << getKey());
+/*MGEN:ENDIF*/
}
/*MGEN:Class.NameCap*/::~/*MGEN:Class.NameCap*/ ()
{
+/*MGEN:IF(Root.GenLogs)*/
+ bool logEnabled;
+ QPID_LOG_TEST_CAT(trace, model, logEnabled);
+ if (logEnabled)
+ {
+ ::qpid::types::Variant::Map map;
+ mapEncodeValues(map, false, true);
+ QPID_LOG_CAT(trace, model, "Mgmt delete " << className
+ << ". id:" << getKey()
+ << " Statistics: " << map);
+ }
+/*MGEN:ENDIF*/
/*MGEN:IF(Class.ExistPerThreadStats)*/
for (int idx = 0; idx < maxThreads; idx++)
if (perThreadStatsArray[idx] != 0)
@@ -274,7 +294,6 @@ std::string /*MGEN:Class.NameCap*/::getKey() const
}
-
void /*MGEN:Class.NameCap*/::mapEncodeValues (::qpid::types::Variant::Map& _map,
bool includeProperties,
bool includeStatistics)
diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt
index 1f3c2a739d..526f191f84 100644
--- a/cpp/src/CMakeLists.txt
+++ b/cpp/src/CMakeLists.txt
@@ -220,7 +220,7 @@ execute_process(COMMAND ${RUBY_EXECUTABLE} -I ${rgen_dir} ${rgen_dir}/generate $
endforeach (spec_file ${mgmt_specs})
if (regen_mgmt)
message(STATUS "Regenerating Qpid Management Framework sources")
-execute_process(COMMAND ${PYTHON_EXECUTABLE} ${mgen_dir}/qmf-gen -c managementgen.cmake -b -q -o ${CMAKE_CURRENT_BINARY_DIR}/qmf ${mgmt_specs}
+execute_process(COMMAND ${PYTHON_EXECUTABLE} ${mgen_dir}/qmf-gen -c managementgen.cmake -b -l -q -o ${CMAKE_CURRENT_BINARY_DIR}/qmf ${mgmt_specs}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
else (regen_mgmt)
message(STATUS "No need to generate Qpid Management Framework sources")
@@ -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)
@@ -626,27 +626,42 @@ set (ha_default ON)
option(BUILD_HA "Build Active-Passive HA plugin" ${ha_default})
if (BUILD_HA)
set (ha_SOURCES
+ qpid/ha/AlternateExchangeSetter.h
+ 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/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/ReplicateLevel.h
- qpid/ha/ReplicateLevel.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 qpidcommon qpidbroker ${Boost_PROGRAM_OPTIONS_LIBRARY})
+ target_link_libraries (ha qpidtypes qpidcommon qpidbroker)
if (CMAKE_COMPILER_IS_GNUCXX)
set_target_properties (ha PROPERTIES
PREFIX ""
@@ -672,6 +687,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)
@@ -734,7 +754,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}
)
@@ -780,41 +799,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
@@ -917,6 +949,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)
@@ -1200,6 +1233,7 @@ set (qmf_SOURCES
set (qmf_HEADERS
../include/qpid/agent/ManagementAgent.h
../include/qpid/agent/QmfAgentImportExport.h
+ ../include/qmf/BrokerImportExport.h
)
add_msvc_version (qmf library dll)
diff --git a/cpp/src/Makefile.am b/cpp/src/Makefile.am
index 43e491a229..8a8e4b1928 100644
--- a/cpp/src/Makefile.am
+++ b/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/MemStat.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 \
@@ -107,7 +107,7 @@ mgen_xml=$(top_srcdir)/../specs/management-schema.xml \
$(srcdir)/qpid/cluster/management-schema.xml \
$(srcdir)/qpid/ha/management-schema.xml
mgen_cmd=$(mgen_dir)/qmf-gen -m $(srcdir)/managementgen.mk \
- -c $(srcdir)/managementgen.cmake -q -b -o qmf \
+ -c $(srcdir)/managementgen.cmake -q -b -l -o qmf \
$(mgen_xml)
$(srcdir)/managementgen.mk $(mgen_broker_cpp) $(dist_qpid_management_HEADERS): mgen.timestamp
@@ -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 \
@@ -553,6 +552,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 \
@@ -615,6 +616,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 \
@@ -684,6 +686,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/cpp/src/ha.mk b/cpp/src/ha.mk
index be1fb73e89..96a3d872e4 100644
--- a/cpp/src/ha.mk
+++ b/cpp/src/ha.mk
@@ -23,22 +23,37 @@
dmoduleexec_LTLIBRARIES += ha.la
ha_la_SOURCES = \
+ qpid/ha/AlternateExchangeSetter.h \
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/ConnectionExcluder.cpp \
- qpid/ha/ConnectionExcluder.h \
+ qpid/ha/BrokerReplicator.h \
+ qpid/ha/ConnectionObserver.cpp \
+ qpid/ha/ConnectionObserver.h \
qpid/ha/HaBroker.cpp \
qpid/ha/HaBroker.h \
qpid/ha/HaPlugin.cpp \
+ 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/QueueReplicator.h \
- qpid/ha/ReplicateLevel.cpp \
- qpid/ha/ReplicateLevel.h \
qpid/ha/ReplicatingSubscription.cpp \
qpid/ha/ReplicatingSubscription.h \
- qpid/ha/Settings.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/cpp/src/posix/QpiddBroker.cpp b/cpp/src/posix/QpiddBroker.cpp
index fd2fb6184f..76e3bb6674 100644
--- a/cpp/src/posix/QpiddBroker.cpp
+++ b/cpp/src/posix/QpiddBroker.cpp
@@ -154,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)
diff --git a/cpp/src/qmf.mk b/cpp/src/qmf.mk
index 9b5df6c808..6a4bce4087 100644
--- a/cpp/src/qmf.mk
+++ b/cpp/src/qmf.mk
@@ -30,7 +30,8 @@ lib_LTLIBRARIES += \
#
QMF_API = \
../include/qpid/agent/ManagementAgent.h \
- ../include/qpid/agent/QmfAgentImportExport.h
+ ../include/qpid/agent/QmfAgentImportExport.h \
+ ../include/qmf/BrokerImportExport.h
#
# Public headers for the QMF2 API
@@ -96,7 +97,6 @@ libqmf2_la_SOURCES = \
qmf/AgentSessionImpl.h \
qmf/AgentSubscription.cpp \
qmf/AgentSubscription.h \
- qmf/BrokerImportExport.h \
qmf/ConsoleEvent.cpp \
qmf/ConsoleEventImpl.h \
qmf/ConsoleSession.cpp \
diff --git a/cpp/src/qmf/AgentSession.cpp b/cpp/src/qmf/AgentSession.cpp
index 3b5806aea4..4d7be33188 100644
--- a/cpp/src/qmf/AgentSession.cpp
+++ b/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/cpp/src/qmf/AgentSessionImpl.h b/cpp/src/qmf/AgentSessionImpl.h
index ae512a4054..64a39ab2e8 100644
--- a/cpp/src/qmf/AgentSessionImpl.h
+++ b/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/cpp/src/qmf/ConsoleSessionImpl.h b/cpp/src/qmf/ConsoleSessionImpl.h
index e2b30602fa..2c06df030c 100644
--- a/cpp/src/qmf/ConsoleSessionImpl.h
+++ b/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/cpp/src/qmf/EventNotifierImpl.cpp b/cpp/src/qmf/EventNotifierImpl.cpp
index 20114aaa5e..81b6d637a3 100644
--- a/cpp/src/qmf/EventNotifierImpl.cpp
+++ b/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/cpp/src/qmf/PrivateImplRef.h b/cpp/src/qmf/PrivateImplRef.h
index 960cbb2e09..c0c07d7e1b 100644
--- a/cpp/src/qmf/PrivateImplRef.h
+++ b/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/cpp/src/qpid/RefCounted.h b/cpp/src/qpid/RefCounted.h
index f9e0107103..26e3e2c4ba 100644
--- a/cpp/src/qpid/RefCounted.h
+++ b/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/cpp/src/qpid/acl/Acl.cpp b/cpp/src/qpid/acl/Acl.cpp
index 917c2e3398..d941577f6a 100644
--- a/cpp/src/qpid/acl/Acl.cpp
+++ b/cpp/src/qpid/acl/Acl.cpp
@@ -51,7 +51,7 @@ 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),
- connectionCounter(new ConnectionCounter(*this, aclValues.aclMaxConnectPerUser, aclValues.aclMaxConnectPerIp))
+ connectionCounter(new ConnectionCounter(*this, aclValues.aclMaxConnectPerUser, aclValues.aclMaxConnectPerIp, aclValues.aclMaxConnectTotal))
{
agent = broker->getManagementAgent();
@@ -60,11 +60,14 @@ 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");
@@ -121,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,
diff --git a/cpp/src/qpid/acl/Acl.h b/cpp/src/qpid/acl/Acl.h
index c3451018ef..4893f71ef2 100644
--- a/cpp/src/qpid/acl/Acl.h
+++ b/cpp/src/qpid/acl/Acl.h
@@ -38,6 +38,7 @@
namespace qpid {
namespace broker {
class Broker;
+class Connection;
}
namespace acl {
@@ -45,8 +46,9 @@ class ConnectionCounter;
struct AclValues {
std::string aclFile;
- uint32_t aclMaxConnectPerUser;
- uint32_t aclMaxConnectPerIp;
+ uint16_t aclMaxConnectPerUser;
+ uint16_t aclMaxConnectPerIp;
+ uint16_t aclMaxConnectTotal;
};
@@ -66,6 +68,9 @@ private:
public:
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() {
@@ -87,6 +92,8 @@ public:
const std::string& ExchangeName,
const std::string& RoutingKey);
+ virtual bool approveConnection(const broker::Connection& connection);
+
virtual ~Acl();
private:
bool result(
diff --git a/cpp/src/qpid/acl/AclConnectionCounter.cpp b/cpp/src/qpid/acl/AclConnectionCounter.cpp
index 5d4e3c1544..052fa3c222 100644
--- a/cpp/src/qpid/acl/AclConnectionCounter.cpp
+++ b/cpp/src/qpid/acl/AclConnectionCounter.cpp
@@ -34,37 +34,82 @@ namespace acl {
//
// This module instantiates a broker::ConnectionObserver and limits client
-// connections by counting connections per user name and per client IP address.
+// connections by counting connections per user name, per client IP address
+// and per total connection count.
//
//
//
//
-ConnectionCounter::ConnectionCounter(Acl& a, uint32_t nl, uint32_t hl) :
- acl(a), nameLimit(nl), hostLimit(hl) {}
+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() {}
//
-// limitCheckLH
+// 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::limitCheckLH(
- connectCountsMap_t& theMap, const std::string& theName, uint32_t theLimit) {
+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()) {
- uint32_t count = (uint32_t)(*eRef).second + 1;
+ count = (uint16_t)(*eRef).second + 1;
(*eRef).second = count;
result = count <= theLimit;
} else {
- theMap[theName] = 1;
+ theMap[theName] = count = 1;
+ }
+ if (emitLog) {
+ QPID_LOG(trace, "ACL ConnectionApprover user=" << theName
+ << " limit=" << theLimit
+ << " curValue=" << count
+ << " result=" << (result ? "allow" : "deny"));
}
}
return result;
@@ -78,12 +123,12 @@ bool ConnectionCounter::limitCheckLH(
// called with dataLock already taken
//
void ConnectionCounter::releaseLH(
- connectCountsMap_t& theMap, const std::string& theName, uint32_t theLimit) {
+ connectCountsMap_t& theMap, const std::string& theName, uint16_t theLimit) {
if (theLimit > 0) {
connectCountsMap_t::iterator eRef = theMap.find(theName);
if (eRef != theMap.end()) {
- uint32_t count = (uint32_t) (*eRef).second;
+ uint16_t count = (uint16_t) (*eRef).second;
assert (count > 0);
if (1 == count) {
theMap.erase (eRef);
@@ -103,52 +148,20 @@ void ConnectionCounter::releaseLH(
// connection - called during Connection's constructor
//
void ConnectionCounter::connection(broker::Connection& connection) {
- QPID_LOG(trace, "ACL ConnectionCounter connection IP:" << connection.getMgmtId()
- << ", userId:" << connection.getUserId());
+ QPID_LOG(trace, "ACL ConnectionCounter new connection: " << connection.getMgmtId());
- Mutex::ScopedLock locker(dataLock);
-
- connectProgressMap[connection.getMgmtId()] = C_CREATED;
-}
-
-
-//
-// opened - called when first AMQP frame is received over Connection
-//
-void ConnectionCounter::opened(broker::Connection& connection) {
- QPID_LOG(trace, "ACL ConnectionCounter Opened IP:" << connection.getMgmtId()
- << ", userId:" << connection.getUserId());
+ const std::string& hostName(getClientHost(connection.getMgmtId()));
Mutex::ScopedLock locker(dataLock);
- const std::string& userName( connection.getUserId());
- const std::string& hostName(getClientHost(connection.getMgmtId()));
+ // Total connections goes up
+ totalCurrentConnections += 1;
- // Bump state from CREATED to OPENED
- (void) limitCheckLH(connectProgressMap, connection.getMgmtId(), C_OPENED);
-
- bool nameOk = limitCheckLH(connectByNameMap, userName, nameLimit);
- bool hostOk = limitCheckLH(connectByHostMap, hostName, hostLimit);
-
- if (!nameOk) {
- // User has too many
- acl.reportConnectLimit(userName, hostName);
- QPID_LOG(notice, "ACL ConnectionCounter User '" << userName
- << "' exceeded maximum allowed connections");
- throw Exception(
- QPID_MSG("User '" << userName
- << "' exceeded maximum allowed connections"));
- }
+ // Record the fact that this connection exists
+ connectProgressMap[connection.getMgmtId()] = C_CREATED;
- if (!hostOk) {
- // Host has too many
- acl.reportConnectLimit(userName, hostName);
- QPID_LOG(notice, "ACL ConnectionCounter Client host '" << hostName
- << "' exceeded maximum allowed connections");
- throw Exception(
- QPID_MSG("Client host '" << hostName
- << "' exceeded maximum allowed connections"));
- }
+ // Count the connection from this host.
+ (void) countConnectionLH(connectByHostMap, hostName, hostLimit, false);
}
@@ -156,7 +169,7 @@ void ConnectionCounter::opened(broker::Connection& connection) {
// closed - called during Connection's destructor
//
void ConnectionCounter::closed(broker::Connection& connection) {
- QPID_LOG(trace, "ACL ConnectionCounter Closed IP:" << connection.getMgmtId()
+ QPID_LOG(trace, "ACL ConnectionCounter closed: " << connection.getMgmtId()
<< ", userId:" << connection.getUserId());
Mutex::ScopedLock locker(dataLock);
@@ -165,32 +178,129 @@ void ConnectionCounter::closed(broker::Connection& connection) {
if (eRef != connectProgressMap.end()) {
if ((*eRef).second == C_OPENED){
// Normal case: connection was created and opened.
- // Decrement in-use counts
+ // Decrement user in-use counts
releaseLH(connectByNameMap,
connection.getUserId(),
nameLimit);
-
- releaseLH(connectByHostMap,
- getClientHost(connection.getMgmtId()),
- hostLimit);
} else {
// Connection was created but not opened.
- // Don't decrement any connection counts.
+ // 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 info for '" << connection.getMgmtId()
+ 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)
{
diff --git a/cpp/src/qpid/acl/AclConnectionCounter.h b/cpp/src/qpid/acl/AclConnectionCounter.h
index 31d11540fd..eec8e90256 100644
--- a/cpp/src/qpid/acl/AclConnectionCounter.h
+++ b/cpp/src/qpid/acl/AclConnectionCounter.h
@@ -48,32 +48,52 @@ private:
enum CONNECTION_PROGRESS { C_CREATED=1, C_OPENED=2 };
Acl& acl;
- uint32_t nameLimit;
- uint32_t hostLimit;
+ 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);
- bool limitCheckLH(connectCountsMap_t& theMap,
- const std::string& theName,
- uint32_t theLimit);
+ /** 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,
- uint32_t theLimit);
+ uint16_t theLimit);
public:
- ConnectionCounter(Acl& acl, uint32_t nl, uint32_t hl);
+ ConnectionCounter(Acl& acl, uint16_t nl, uint16_t hl, uint16_t tl);
~ConnectionCounter();
+ // ConnectionObserver interface
void connection(broker::Connection& connection);
- void opened(broker::Connection& connection);
void closed(broker::Connection& connection);
+ // Connection counting
+ bool approveConnection(const broker::Connection& conn);
};
}} // namespace qpid::ha
diff --git a/cpp/src/qpid/acl/AclData.cpp b/cpp/src/qpid/acl/AclData.cpp
index da7f240a9b..a07176dc76 100644
--- a/cpp/src/qpid/acl/AclData.cpp
+++ b/cpp/src/qpid/acl/AclData.cpp
@@ -305,7 +305,9 @@ namespace acl {
// lookup
//
// The ACL main business logic function of matching rules and declaring
- // an allow or deny result.
+ // an allow or deny result. This lookup is the fastpath per-message
+ // lookup to verify if a user is allowed to publish to an exchange with
+ // a given key.
//
AclResult AclData::lookup(
const std::string& id,
@@ -331,7 +333,8 @@ namespace acl {
if (itrRule != actionList[action][objType]->end() )
{
- //loop the vector
+ // Found a rule list for this user-action-object set.
+ // Search the rule list for a matching rule.
ruleSetItr rsItr = itrRule->second.end();
for (int cnt = itrRule->second.size(); cnt != 0; cnt--)
{
@@ -339,56 +342,46 @@ namespace acl {
QPID_LOG(debug, "ACL: checking rule " << rsItr->toString());
- // loop the names looking for match
+ // Search on exchange name and routing key only if specfied in rule.
bool match =true;
- for (specPropertyMapItr pMItr = rsItr->props.begin();
- (pMItr != rsItr->props.end()) && match;
- pMItr++)
+ if (rsItr->pubExchNameInRule)
{
- //match name is exists first
- switch (pMItr->first)
+ if (matchProp(rsItr->pubExchName, name))
{
- case acl::SPECPROP_NAME:
- if (matchProp(pMItr->second, name))
- {
- QPID_LOG(debug, "ACL: lookup exchange name '"
- << name << "' matched with rule name '"
- << pMItr->second << "'");
-
- }
- else
- {
- match= false;
- QPID_LOG(debug, "ACL: lookup exchange name '"
- << name << "' did not match with rule name '"
- << pMItr->second << "'");
- }
- break;
+ QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup exchange name '"
+ << name << "' matched with rule name '"
+ << rsItr->pubExchName << "'");
- case acl::SPECPROP_ROUTINGKEY:
- if (matchProp(pMItr->second, routingKey))
- {
- QPID_LOG(debug, "ACL: lookup key name '"
- << routingKey << "' matched with rule routing key '"
- << pMItr->second << "'");
- }
- else
- {
- match= false;
- QPID_LOG(debug, "ACL: lookup key name '"
- << routingKey << "' did not match with rule routing key '"
- << pMItr->second << "'");
- }
- break;
+ }
+ else
+ {
+ match= false;
+ QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup exchange name '"
+ << name << "' did not match with rule name '"
+ << rsItr->pubExchName << "'");
+ }
+ }
- default:
- // Don't care
- break;
- };
+ if (match && rsItr->pubRoutingKeyInRule)
+ {
+ if (rsItr->matchRoutingKey(routingKey))
+ {
+ QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup key name '"
+ << routingKey << "' matched with rule routing key '"
+ << rsItr->pubRoutingKey << "'");
+ }
+ else
+ {
+ QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup key name '"
+ << routingKey << "' did not match with rule routing key '"
+ << rsItr->pubRoutingKey << "'");
+ match = false;
+ }
}
+
if (match){
aclresult = rsItr->ruleMode;
- QPID_LOG(debug,"ACL: Successful match, the decision is:"
+ QPID_LOG(debug,"ACL: Rule: " << rsItr->rawRuleNum << " Successful match, the decision is:"
<< AclHelper::getAclResultStr(aclresult));
return aclresult;
}
diff --git a/cpp/src/qpid/acl/AclData.h b/cpp/src/qpid/acl/AclData.h
index 1c1cb3e9c6..ca0a676a1c 100644
--- a/cpp/src/qpid/acl/AclData.h
+++ b/cpp/src/qpid/acl/AclData.h
@@ -21,6 +21,9 @@
*/
#include "qpid/broker/AclModule.h"
+#include "AclTopicMatch.h"
+#include "qpid/log/Statement.h"
+#include "boost/shared_ptr.hpp"
#include <vector>
#include <sstream>
@@ -48,18 +51,29 @@ public:
// A single ACL file entry may create many rule entries in
// many ruleset vectors.
//
- struct rule {
+ struct Rule {
+ typedef broker::TopicExchange::TopicExchangeTester topicTester;
int rawRuleNum; // rule number in ACL file
qpid::acl::AclResult ruleMode; // combined allow/deny log/nolog
specPropertyMap props; //
+ bool pubRoutingKeyInRule;
+ std::string pubRoutingKey;
+ boost::shared_ptr<topicTester> pTTest;
+ bool pubExchNameInRule;
+ std::string pubExchName;
-
- rule (int ruleNum, qpid::acl::AclResult res, specPropertyMap& p) :
+ Rule (int ruleNum, qpid::acl::AclResult res, specPropertyMap& p) :
rawRuleNum(ruleNum),
ruleMode(res),
- props(p)
- {};
+ props(p),
+ pubRoutingKeyInRule(false),
+ pubRoutingKey(),
+ pTTest(boost::shared_ptr<topicTester>(new topicTester())),
+ pubExchNameInRule(false),
+ pubExchName()
+ {}
+
std::string toString () const {
std::ostringstream ruleStr;
@@ -76,9 +90,21 @@ public:
ruleStr << " }]";
return ruleStr.str();
}
+
+ void addTopicTest(const std::string& pattern) {
+ pTTest->addBindingKey(broker::TopicExchange::normalize(pattern));
+ }
+
+ // Topic Exchange tester
+ // return true if any bindings match 'pattern'
+ bool matchRoutingKey(const std::string& pattern) const
+ {
+ topicTester::BindingVec bv;
+ return pTTest->findMatches(pattern, bv);
+ }
};
- typedef std::vector<rule> ruleSet;
+ typedef std::vector<Rule> ruleSet;
typedef ruleSet::const_iterator ruleSetItr;
typedef std::map<std::string, ruleSet > actionObject; // user
typedef actionObject::iterator actObjItr;
diff --git a/cpp/src/qpid/acl/AclPlugin.cpp b/cpp/src/qpid/acl/AclPlugin.cpp
index 6c18cd2749..ebf5e90afe 100644
--- a/cpp/src/qpid/acl/AclPlugin.cpp
+++ b/cpp/src/qpid/acl/AclPlugin.cpp
@@ -39,10 +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-max-connect-per-user", optValue(values.aclMaxConnectPerUser, "N"), "The maximum number of connections allowed per user")
- ("acl-max-connect-per-ip" , optValue(values.aclMaxConnectPerIp, "N"), "The maximum number of connections allowed per host IP address");
+ ("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.")
+ ;
}
};
@@ -69,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/cpp/src/qpid/acl/AclReader.cpp b/cpp/src/qpid/acl/AclReader.cpp
index 80debf1bd1..f9be49b88d 100644
--- a/cpp/src/qpid/acl/AclReader.cpp
+++ b/cpp/src/qpid/acl/AclReader.cpp
@@ -101,7 +101,7 @@ namespace acl {
<< AclHelper::getAclResultStr(d->decisionMode));
foundmode = true;
} else {
- AclData::rule rule(cnt, (*i)->res, (*i)->props);
+ AclData::Rule rule(cnt, (*i)->res, (*i)->props);
// Action -> Object -> map<user -> set<Rule> >
std::ostringstream actionstr;
@@ -110,8 +110,27 @@ namespace acl {
(*i)->actionAll ? acnt++ : acnt = acl::ACTIONSIZE) {
if (acnt == acl::ACT_PUBLISH)
+ {
d->transferAcl = true; // we have transfer ACL
-
+ // For Publish the only object should be Exchange
+ // and the only property should be routingkey.
+ // Go through the rule properties and find the name and the key.
+ // If found then place them specially for the lookup engine.
+ for (pmCitr pItr=(*i)->props.begin(); pItr!=(*i)->props.end(); pItr++) {
+ if (acl::SPECPROP_ROUTINGKEY == pItr->first)
+ {
+ rule.pubRoutingKeyInRule = true;
+ rule.pubRoutingKey = (std::string)pItr->second;
+ rule.addTopicTest(rule.pubRoutingKey);
+ break;
+ }
+ if (acl::SPECPROP_NAME == pItr->first)
+ {
+ rule.pubExchNameInRule = true;
+ rule.pubExchName = pItr->second;
+ }
+ }
+ }
actionstr << AclHelper::getActionStr((Action) acnt) << ",";
//find the Action, create if not exist
@@ -285,7 +304,7 @@ namespace acl {
if (ws) {
ret = true;
} else {
- errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
<< ", Non-continuation line must start with \"group\" or \"acl\".";
ret = false;
}
@@ -314,13 +333,23 @@ 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 {
const unsigned minimumSize = (cont ? 2 : 3);
if (toksSize < minimumSize) {
- errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
<< ", Insufficient tokens for group definition.";
return false;
}
@@ -332,7 +361,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 +395,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 {
@@ -459,7 +498,7 @@ namespace acl {
nvPair propNvp = splitNameValuePair(toks[i]);
if (propNvp.second.size() == 0) {
errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
- <<", Badly formed property name-value pair \""
+ <<", Badly formed property name-value pair \""
<< propNvp.first << "\". (Must be name=value)";
return false;
}
diff --git a/cpp/src/qpid/acl/AclReader.h b/cpp/src/qpid/acl/AclReader.h
index 730013f4ed..6351c1e509 100644
--- a/cpp/src/qpid/acl/AclReader.h
+++ b/cpp/src/qpid/acl/AclReader.h
@@ -26,6 +26,7 @@
#include <string>
#include <vector>
#include <sstream>
+#include <memory>
#include "qpid/acl/AclData.h"
#include "qpid/broker/AclModule.h"
diff --git a/cpp/src/qpid/acl/AclTopicMatch.h b/cpp/src/qpid/acl/AclTopicMatch.h
new file mode 100644
index 0000000000..486c229ad5
--- /dev/null
+++ b/cpp/src/qpid/acl/AclTopicMatch.h
@@ -0,0 +1,89 @@
+#ifndef QPID_ACL_TOPIC_MATCH_H
+#define QPID_ACL_TOPIC_MATCH_H
+
+/*
+ *
+ * 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 "qpid/broker/TopicKeyNode.h"
+#include "qpid/broker/TopicExchange.h"
+#include "qpid/log/Statement.h"
+#include "boost/shared_ptr.hpp"
+#include <vector>
+#include <sstream>
+
+namespace qpid {
+namespace broker {
+
+// Class for executing topic exchange routing key matching rules in
+// Acl code the allows or denies users publishing to an exchange.
+class TopicExchange::TopicExchangeTester {
+
+class boundNode;
+
+public:
+ typedef std::vector<bool> BindingVec;
+ typedef TopicKeyNode<boundNode> TestBindingNode;
+
+private:
+ // Target class to be bound into topic key tree
+ class boundNode {
+ public:
+ BindingVec bindingVector;
+ };
+
+ // Acl binding trees contain only one node each.
+ // When the iterator sees it then the node matches the caller's spec.
+ class TestFinder : public TestBindingNode::TreeIterator {
+ public:
+ TestFinder(BindingVec& m) : bv(m), found(false) {};
+ ~TestFinder() {};
+ bool visit(TestBindingNode& /*node*/) {
+ assert(!found);
+ found = true;
+ return true;
+ }
+ BindingVec& bv;
+ bool found;
+ };
+
+public:
+ TopicExchangeTester() {};
+ ~TopicExchangeTester() {};
+ bool addBindingKey(const std::string& bKey) {
+ std::string routingPattern = normalize(bKey);
+ boundNode *mbn = bindingTree.add(routingPattern);
+ if (mbn) {
+ // push a dummy binding to mark this node as "non-leaf"
+ mbn->bindingVector.push_back(true);
+ return true;
+ }
+ return false;
+ }
+
+ bool findMatches(const std::string& rKey, BindingVec& matches) {
+ TestFinder testFinder(matches);
+ bindingTree.iterateMatch( rKey, testFinder );
+ return testFinder.found;
+ }
+
+private:
+ TestBindingNode bindingTree;
+};
+}} // namespace qpid::broker
+
+#endif // QPID_ACL_TOPIC_MATCH_H
diff --git a/cpp/src/qpid/acl/AclValidator.cpp b/cpp/src/qpid/acl/AclValidator.cpp
index 49bb65db4b..85f0f7c240 100644
--- a/cpp/src/qpid/acl/AclValidator.cpp
+++ b/cpp/src/qpid/acl/AclValidator.cpp
@@ -131,7 +131,7 @@ namespace acl {
boost::bind(&AclValidator::validateRule, this, _1));
}
- void AclValidator::validateRule(qpid::acl::AclData::rule& rule){
+ void AclValidator::validateRule(qpid::acl::AclData::Rule& rule){
std::for_each(rule.props.begin(),
rule.props.end(),
boost::bind(&AclValidator::validateProperty, this, _1));
diff --git a/cpp/src/qpid/acl/AclValidator.h b/cpp/src/qpid/acl/AclValidator.h
index f85c241b06..76eb222d2f 100644
--- a/cpp/src/qpid/acl/AclValidator.h
+++ b/cpp/src/qpid/acl/AclValidator.h
@@ -71,7 +71,7 @@ class AclValidator {
public:
void validateRuleSet(std::pair<const std::string, qpid::acl::AclData::ruleSet>& rules);
- void validateRule(qpid::acl::AclData::rule& rule);
+ void validateRule(qpid::acl::AclData::Rule& rule);
void validateProperty(std::pair<const qpid::acl::SpecProperty, std::string>& prop);
void validate(boost::shared_ptr<AclData> d);
AclValidator();
diff --git a/cpp/src/qpid/acl/management-schema.xml b/cpp/src/qpid/acl/management-schema.xml
index 19fe37333c..f52c251bed 100644
--- a/cpp/src/qpid/acl/management-schema.xml
+++ b/cpp/src/qpid/acl/management-schema.xml
@@ -17,13 +17,16 @@
-->
<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"/>
- <statistic name="connectionDenyCount" type="count64" unit="connection" desc="Number of connections 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"/>
diff --git a/cpp/src/qpid/amqp_0_10/Codecs.cpp b/cpp/src/qpid/amqp_0_10/Codecs.cpp
index b976a5d09b..1288652ee1 100644
--- a/cpp/src/qpid/amqp_0_10/Codecs.cpp
+++ b/cpp/src/qpid/amqp_0_10/Codecs.cpp
@@ -52,9 +52,7 @@ template <class T, class U, class F> void convert(const T& from, U& to, F f)
}
Variant::Map::value_type toVariantMapEntry(const FieldTable::value_type& in);
-FieldTable::value_type toFieldTableEntry(const Variant::Map::value_type& in);
Variant toVariant(boost::shared_ptr<FieldValue> in);
-boost::shared_ptr<FieldValue> toFieldValue(const Variant& in);
template <class T, class U, class F> void translate(boost::shared_ptr<FieldValue> in, U& u, F f)
{
@@ -70,20 +68,6 @@ template <class T, class U, class F> T* toFieldValueCollection(const U& u, F f)
return new T(t);
}
-FieldTableValue* toFieldTableValue(const Variant::Map& map)
-{
- FieldTable ft;
- convert(map, ft, &toFieldTableEntry);
- return new FieldTableValue(ft);
-}
-
-ListValue* toListValue(const Variant::List& list)
-{
- List l;
- convert(list, l, &toFieldValue);
- return new ListValue(l);
-}
-
void setEncodingFor(Variant& out, uint8_t code)
{
switch(code){
@@ -151,7 +135,7 @@ Variant toVariant(boost::shared_ptr<FieldValue> in)
case 0xf0: break;//void, which is the default value for Variant
case 0xf1: out.setEncoding(amqp0_10_bit); break;//treat 'bit' as void, which is the default value for Variant
-
+
//Variable Width types:
//strings:
case 0x80:
@@ -217,89 +201,254 @@ boost::shared_ptr<FieldValue> convertString(const std::string& value, const std:
}
}
-boost::shared_ptr<FieldValue> toFieldValue(const Variant& in)
+Variant::Map::value_type toVariantMapEntry(const FieldTable::value_type& in)
{
- boost::shared_ptr<FieldValue> out;
- switch (in.getType()) {
- case VAR_VOID: out = boost::shared_ptr<FieldValue>(new VoidValue()); break;
- case VAR_BOOL: out = boost::shared_ptr<FieldValue>(new BoolValue(in.asBool())); break;
- case VAR_UINT8: out = boost::shared_ptr<FieldValue>(new Unsigned8Value(in.asUint8())); break;
- case VAR_UINT16: out = boost::shared_ptr<FieldValue>(new Unsigned16Value(in.asUint16())); break;
- case VAR_UINT32: out = boost::shared_ptr<FieldValue>(new Unsigned32Value(in.asUint32())); break;
- case VAR_UINT64: out = boost::shared_ptr<FieldValue>(new Unsigned64Value(in.asUint64())); break;
- case VAR_INT8: out = boost::shared_ptr<FieldValue>(new Integer8Value(in.asInt8())); break;
- case VAR_INT16: out = boost::shared_ptr<FieldValue>(new Integer16Value(in.asInt16())); break;
- case VAR_INT32: out = boost::shared_ptr<FieldValue>(new Integer32Value(in.asInt32())); break;
- case VAR_INT64: out = boost::shared_ptr<FieldValue>(new Integer64Value(in.asInt64())); break;
- case VAR_FLOAT: out = boost::shared_ptr<FieldValue>(new FloatValue(in.asFloat())); break;
- case VAR_DOUBLE: out = boost::shared_ptr<FieldValue>(new DoubleValue(in.asDouble())); break;
- case VAR_STRING: out = convertString(in.asString(), in.getEncoding()); break;
- case VAR_UUID: out = boost::shared_ptr<FieldValue>(new UuidValue(in.asUuid().data())); break;
- case VAR_MAP:
- out = boost::shared_ptr<FieldValue>(toFieldTableValue(in.asMap()));
- break;
- case VAR_LIST:
- out = boost::shared_ptr<FieldValue>(toListValue(in.asList()));
+ return Variant::Map::value_type(in.first, toVariant(in.second));
+}
+
+struct DecodeBuffer
+{
+ Buffer buffer;
+
+ DecodeBuffer(const std::string& s) : buffer(const_cast<char*>(s.data()), s.size()) {}
+
+ template <class T> void decode(T& t) { t.decode(buffer); }
+
+};
+
+template <class T, class U, class F> void _decode(const std::string& data, U& value, F f)
+{
+ T t;
+ DecodeBuffer buffer(data);
+ buffer.decode(t);
+ convert(t, value, f);
+}
+
+uint32_t encodedSize(const Variant::Map& values);
+uint32_t encodedSize(const Variant::List& values);
+uint32_t encodedSize(const std::string& value);
+
+uint32_t encodedSize(const Variant& value)
+{
+ switch (value.getType()) {
+ case VAR_VOID:
+ return 0;
+ case VAR_BOOL:
+ case VAR_UINT8:
+ case VAR_INT8:
+ return 1;
+ case VAR_UINT16:
+ case VAR_INT16:
+ return 2;
break;
+ case VAR_UINT32:
+ case VAR_INT32:
+ case VAR_FLOAT:
+ return 4;
+ case VAR_UINT64:
+ case VAR_INT64:
+ case VAR_DOUBLE:
+ return 8;
+ case VAR_UUID:
+ return 16;
+ case VAR_MAP:
+ return encodedSize(value.asMap());
+ case VAR_LIST:
+ return encodedSize(value.asList());
+ case VAR_STRING:
+ return encodedSize(value.getString());
+ default:
+ throw Exception("Couldn't encode Variant: Illegal type code");
}
- return out;
}
-Variant::Map::value_type toVariantMapEntry(const FieldTable::value_type& in)
+uint32_t encodedSize(const Variant::Map& values)
{
- return Variant::Map::value_type(in.first, toVariant(in.second));
+ uint32_t size = 4/*size field*/ + 4/*count field*/;
+ for(Variant::Map::const_iterator i = values.begin(); i != values.end(); ++i) {
+ size += 1/*size of key*/ + (i->first).size() + 1/*typecode*/ + encodedSize(i->second);
+ }
+ return size;
}
-FieldTable::value_type toFieldTableEntry(const Variant::Map::value_type& in)
+uint32_t encodedSize(const Variant::Map& values, const std::string& efield, const Variant& evalue)
{
- return FieldTable::value_type(in.first, toFieldValue(in.second));
+ uint32_t size = 4/*size field*/ + 4/*count field*/;
+ for(Variant::Map::const_iterator i = values.begin(); i != values.end(); ++i) {
+ size += 1/*size of key*/ + (i->first).size() + 1/*typecode*/ + encodedSize(i->second);
+ }
+ size += 1/*size of key*/ + efield.size() + 1/*typecode*/ + encodedSize(evalue);
+ return size;
}
-struct EncodeBuffer
+uint32_t encodedSize(const Variant::List& values)
{
- char* data;
- Buffer buffer;
+ uint32_t size = 4/*size field*/ + 4/*count field*/;
+ for(Variant::List::const_iterator i = values.begin(); i != values.end(); ++i) {
+ size += 1/*typecode*/ + encodedSize(*i);
+ }
+ return size;
+}
+
+uint32_t encodedSize(const std::string& value)
+{
+ uint32_t size = value.size();
+ if (size > std::numeric_limits<uint16_t>::max()) {
+ return size + 4; /*Long size*/
+ } else {
+ return size + 2; /*Short size*/
+ }
+}
- EncodeBuffer(size_t size) : data(new char[size]), buffer(data, size) {}
- ~EncodeBuffer() { delete[] data; }
+void encode(const std::string& value, const std::string& encoding, qpid::framing::Buffer& buffer)
+{
+ uint32_t size = value.size();
+ if (size > std::numeric_limits<uint16_t>::max()) {
+ if (encoding == utf8 || encoding == utf16 || encoding == iso885915) {
+ throw Exception(QPID_MSG("Could not encode " << encoding << " character string - too long (" << size << " bytes)"));
+ } else {
+ buffer.putOctet(0xa0);
+ buffer.putLong(size);
+ buffer.putRawData(value);
+ }
+ } else {
+ if (encoding == utf8) {
+ buffer.putOctet(0x95);
+ } else if (encoding == utf16) {
+ buffer.putOctet(0x96);
+ } else if (encoding == iso885915) {
+ buffer.putOctet(0x94);
+ } else {
+ buffer.putOctet(0x90);
+ }
+ buffer.putShort(size);
+ buffer.putRawData(value);
+ }
+}
- template <class T> void encode(T& t) { t.encode(buffer); }
+void encode(const Variant::Map& map, uint32_t len, qpid::framing::Buffer& buffer);
+void encode(const Variant::List& list, uint32_t len, qpid::framing::Buffer& buffer);
- void getData(std::string& s) {
- s.assign(data, buffer.getSize());
+void encode(const Variant& value, qpid::framing::Buffer& buffer)
+{
+ switch (value.getType()) {
+ case VAR_VOID:
+ buffer.putOctet(0xf0);
+ break;
+ case VAR_BOOL:
+ buffer.putOctet(0x08);
+ buffer.putOctet(value.asBool());
+ break;
+ case VAR_INT8:
+ buffer.putOctet(0x01);
+ buffer.putInt8(value.asInt8());
+ break;
+ case VAR_UINT8:
+ buffer.putOctet(0x02);
+ buffer.putOctet(value.asUint8());
+ break;
+ case VAR_INT16:
+ buffer.putOctet(0x11);
+ buffer.putInt16(value.asInt16());
+ break;
+ case VAR_UINT16:
+ buffer.putOctet(0x12);
+ buffer.putShort(value.asUint16());
+ break;
+ case VAR_INT32:
+ buffer.putOctet(0x21);
+ buffer.putInt32(value.asInt32());
+ break;
+ case VAR_UINT32:
+ buffer.putOctet(0x22);
+ buffer.putLong(value.asUint32());
+ break;
+ case VAR_FLOAT:
+ buffer.putOctet(0x23);
+ buffer.putFloat(value.asFloat());
+ break;
+ case VAR_INT64:
+ buffer.putOctet(0x31);
+ buffer.putInt64(value.asInt64());
+ break;
+ case VAR_UINT64:
+ buffer.putOctet(0x32);
+ buffer.putLongLong(value.asUint64());
+ break;
+ case VAR_DOUBLE:
+ buffer.putOctet(0x33);
+ buffer.putDouble(value.asDouble());
+ break;
+ case VAR_UUID:
+ buffer.putOctet(0x48);
+ buffer.putBin128(value.asUuid().data());
+ break;
+ case VAR_MAP:
+ buffer.putOctet(0xa8);
+ encode(value.asMap(), encodedSize(value.asMap()), buffer);
+ break;
+ case VAR_LIST:
+ buffer.putOctet(0xa9);
+ encode(value.asList(), encodedSize(value.asList()), buffer);
+ break;
+ case VAR_STRING:
+ encode(value.getString(), value.getEncoding(), buffer);
+ break;
}
-};
+}
-struct DecodeBuffer
+void encode(const Variant::Map& map, uint32_t len, qpid::framing::Buffer& buffer)
{
- Buffer buffer;
+ uint32_t s = buffer.getPosition();
+ buffer.putLong(len - 4);//exclusive of the size field itself
+ buffer.putLong(map.size());
+ for (Variant::Map::const_iterator i = map.begin(); i != map.end(); ++i) {
+ buffer.putShortString(i->first);
+ encode(i->second, buffer);
+ }
+ (void) s; assert(s + len == buffer.getPosition());
+}
- DecodeBuffer(const std::string& s) : buffer(const_cast<char*>(s.data()), s.size()) {}
+void encode(const Variant::Map& map, const std::string& efield, const Variant& evalue, uint32_t len, qpid::framing::Buffer& buffer)
+{
+ uint32_t s = buffer.getPosition();
+ buffer.putLong(len - 4);//exclusive of the size field itself
+ buffer.putLong(map.size() + 1 /* The extra field */ );
+ for (Variant::Map::const_iterator i = map.begin(); i != map.end(); ++i) {
+ buffer.putShortString(i->first);
+ encode(i->second, buffer);
+ }
+ buffer.putShortString(efield);
+ encode(evalue, buffer);
- template <class T> void decode(T& t) { t.decode(buffer); }
-
-};
+ (void) s; assert(s + len == buffer.getPosition());
+}
-template <class T, class U, class F> void _encode(const U& value, std::string& data, F f)
+void encode(const Variant::List& list, uint32_t len, qpid::framing::Buffer& buffer)
{
- T t;
- convert(value, t, f);
- EncodeBuffer buffer(t.encodedSize());
- buffer.encode(t);
- buffer.getData(data);
+ uint32_t s = buffer.getPosition();
+ buffer.putLong(len - 4);//exclusive of the size field itself
+ buffer.putLong(list.size());
+ for (Variant::List::const_iterator i = list.begin(); i != list.end(); ++i) {
+ encode(*i, buffer);
+ }
+ (void) s; assert(s + len == buffer.getPosition());
}
-template <class T, class U, class F> void _decode(const std::string& data, U& value, F f)
+void decode(qpid::framing::Buffer&, Variant::Map&)
{
- T t;
- DecodeBuffer buffer(data);
- buffer.decode(t);
- convert(t, value, f);
}
+
void MapCodec::encode(const Variant::Map& value, std::string& data)
{
- _encode<FieldTable>(value, data, &toFieldTableEntry);
+ uint32_t len = qpid::amqp_0_10::encodedSize(value);
+ std::vector<char> space(len);
+ qpid::framing::Buffer buff(&space[0], len);
+
+ qpid::amqp_0_10::encode(value, len, buff);
+ assert( len == buff.getPosition() );
+ data.assign(&space[0], len);
}
void MapCodec::decode(const std::string& data, Variant::Map& value)
@@ -309,14 +458,18 @@ void MapCodec::decode(const std::string& data, Variant::Map& value)
size_t MapCodec::encodedSize(const Variant::Map& value)
{
- std::string encoded;
- encode(value, encoded);
- return encoded.size();
+ return qpid::amqp_0_10::encodedSize(value);
}
void ListCodec::encode(const Variant::List& value, std::string& data)
{
- _encode<List>(value, data, &toFieldValue);
+ uint32_t len = qpid::amqp_0_10::encodedSize(value);
+ std::vector<char> space(len);
+ qpid::framing::Buffer buff(&space[0], len);
+
+ qpid::amqp_0_10::encode(value, len, buff);
+ assert( len == buff.getPosition() );
+ data.assign(&space[0], len);
}
void ListCodec::decode(const std::string& data, Variant::List& value)
@@ -326,14 +479,47 @@ void ListCodec::decode(const std::string& data, Variant::List& value)
size_t ListCodec::encodedSize(const Variant::List& value)
{
- std::string encoded;
- encode(value, encoded);
- return encoded.size();
+ return qpid::amqp_0_10::encodedSize(value);
}
void translate(const Variant::Map& from, FieldTable& to)
{
- convert(from, to, &toFieldTableEntry);
+ // Create buffer of correct size to encode Variant::Map
+ uint32_t len = encodedSize(from);
+ std::vector<char> space(len);
+ qpid::framing::Buffer buff(&space[0], len);
+
+ // Encode Variant::Map into buffer directly -
+ // We pass the already calculated length in to avoid
+ // recalculating it.
+ encode(from, len, buff);
+
+ // Give buffer to FieldTable
+ // Could speed this up a bit by avoiding copying
+ // the buffer we just created into the FieldTable
+ assert( len == buff.getPosition() );
+ buff.reset();
+ to.decode(buff);
+}
+
+void translate(const Variant::Map& from, const std::string& efield, const Variant& evalue, FieldTable& to)
+{
+ // Create buffer of correct size to encode Variant::Map
+ uint32_t len = encodedSize(from, efield, evalue);
+ std::vector<char> space(len);
+ qpid::framing::Buffer buff(&space[0], len);
+
+ // Encode Variant::Map into buffer directly -
+ // We pass the already calculated length in to avoid
+ // recalculating it.
+ encode(from, efield, evalue, len, buff);
+
+ // Give buffer to FieldTable
+ // Could speed this up a bit by avoiding copying
+ // the buffer we just created into the FieldTable
+ assert( len == buff.getPosition() );
+ buff.reset();
+ to.decode(buff);
}
void translate(const FieldTable& from, Variant::Map& to)
diff --git a/cpp/src/qpid/broker/AclModule.h b/cpp/src/qpid/broker/AclModule.h
index ff9281b6fc..7c180439cf 100644
--- a/cpp/src/qpid/broker/AclModule.h
+++ b/cpp/src/qpid/broker/AclModule.h
@@ -113,6 +113,7 @@ namespace acl {
namespace broker {
+ class Connection;
class AclModule
{
@@ -139,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
diff --git a/cpp/src/qpid/broker/Bridge.cpp b/cpp/src/qpid/broker/Bridge.cpp
index 5b531e4636..d1706b5907 100644
--- a/cpp/src/qpid/broker/Bridge.cpp
+++ b/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), detached(false)
+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);
@@ -90,23 +93,22 @@ void Bridge::create(Connection& c)
conn = &c;
FieldTable options;
if (args.i_sync) options.setInt("qpid.sync_frequency", args.i_sync);
- SessionHandler& sessionHandler = c.getChannel(id);
- sessionHandler.setDetachedCallback(
- boost::bind(&Bridge::sessionDetached, shared_from_this()));
+ 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));
}
@@ -115,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 {
@@ -138,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) {
@@ -163,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) {
@@ -177,9 +181,10 @@ 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
}
void Bridge::setPersistenceId(uint64_t pId) const
@@ -187,8 +192,21 @@ void Bridge::setPersistenceId(uint64_t pId) const
persistenceId = pId;
}
+
+const std::string Bridge::ENCODED_IDENTIFIER("bridge.v2");
+const std::string Bridge::ENCODED_IDENTIFIER_V1("bridge");
+
+bool Bridge::isEncodedBridge(const std::string& key)
+{
+ 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;
@@ -196,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);
@@ -210,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);
@@ -233,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
@@ -259,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;
@@ -306,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();
@@ -318,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();
}
}
@@ -333,8 +382,38 @@ const string& Bridge::getLocalTag() const
return link->getBroker()->getFederationTag();
}
-void Bridge::sessionDetached() {
+// 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/cpp/src/qpid/broker/Bridge.h b/cpp/src/qpid/broker/Bridge.h
index 32b9fd1781..ee298afd45 100644
--- a/cpp/src/qpid/broker/Bridge.h
+++ b/cpp/src/qpid/broker/Bridge.h
@@ -29,6 +29,7 @@
#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"
@@ -43,29 +44,31 @@ class Connection;
class ConnectionState;
class Link;
class LinkRegistry;
-class SessionHandler;
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 isDetached() const { return detached; }
@@ -80,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);
@@ -93,10 +100,20 @@ public:
std::string getQueueName() const { return queueName; }
const qmf::org::apache::qpid::broker::ArgsLinkBridge& getArgs() { return args; }
-private:
- // Callback when the bridge's session is detached.
- void sessionDetached();
+ /** 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);
@@ -108,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/cpp/src/qpid/broker/Broker.cpp b/cpp/src/qpid/broker/Broker.cpp
index f20cce18a2..b763dd4119 100644
--- a/cpp/src/qpid/broker/Broker.cpp
+++ b/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;
}
@@ -443,7 +450,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;
@@ -458,6 +465,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);
@@ -466,13 +481,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;
}
@@ -543,6 +569,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");
@@ -554,6 +582,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
@@ -603,6 +651,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)
{
@@ -674,6 +741,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);
}
@@ -696,6 +870,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);
}
@@ -920,6 +1104,13 @@ std::pair<boost::shared_ptr<Queue>, bool> Broker::createQueue(
ManagementAgent::toMap(arguments),
"created"));
}
+ QPID_LOG_CAT(debug, model, "Create queue. name:" << name
+ << " user:" << userId
+ << " rhost:" << connectionId
+ << " durable:" << (durable ? "T" : "F")
+ << " owner:" << owner
+ << " autodelete:" << (autodelete ? "T" : "F")
+ << " alternateExchange:" << alternateExchange );
}
return result;
}
@@ -942,6 +1133,10 @@ void Broker::deleteQueue(const std::string& name, const std::string& userId,
if (managementAgent.get())
managementAgent->raiseEvent(_qmf::EventQueueDelete(connectionId, userId, name));
+ QPID_LOG_CAT(debug, model, "Delete queue. name:" << name
+ << " user:" << userId
+ << " rhost:" << connectionId
+ );
}
@@ -993,6 +1188,12 @@ std::pair<Exchange::shared_ptr, bool> Broker::createExchange(
ManagementAgent::toMap(arguments),
"created"));
}
+ QPID_LOG_CAT(debug, model, "Create exchange. name:" << name
+ << " user:" << userId
+ << " rhost:" << connectionId
+ << " type:" << type
+ << " alternateExchange:" << alternateExchange
+ << " durable:" << (durable ? "T" : "F"));
}
return result;
}
@@ -1017,7 +1218,9 @@ void Broker::deleteExchange(const std::string& name, const std::string& userId,
if (managementAgent.get())
managementAgent->raiseEvent(_qmf::EventExchangeDelete(connectionId, userId, name));
-
+ QPID_LOG_CAT(debug, model, "Delete exchange. name:" << name
+ << " user:" << userId
+ << " rhost:" << connectionId);
}
void Broker::bind(const std::string& queueName,
@@ -1047,10 +1250,16 @@ 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)));
}
+ QPID_LOG_CAT(debug, model, "Create binding. exchange:" << exchangeName
+ << " queue:" << queueName
+ << " key:" << key
+ << " user:" << userId
+ << " rhost:" << connectionId);
}
}
}
@@ -1082,12 +1291,33 @@ 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));
}
+ QPID_LOG_CAT(debug, model, "Delete binding. exchange:" << exchangeName
+ << " queue:" << queueName
+ << " key:" << key
+ << " user:" << userId
+ << " rhost:" << connectionId);
}
}
}
+// 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/cpp/src/qpid/broker/Broker.h b/cpp/src/qpid/broker/Broker.h
index 135b9340f9..922d0558e5 100644
--- a/cpp/src/qpid/broker/Broker.h
+++ b/cpp/src/qpid/broker/Broker.h
@@ -40,6 +40,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"
@@ -64,8 +65,8 @@
namespace qpid {
namespace sys {
- class ProtocolFactory;
- class Poller;
+class ProtocolFactory;
+class Poller;
}
struct Url;
@@ -91,7 +92,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;
@@ -103,7 +104,6 @@ public:
std::string dataDir;
uint16_t port;
int workerThreads;
- int maxConnections;
int connectionBacklog;
bool enableMgmt;
bool mgmtPublish;
@@ -127,31 +127,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;
@@ -183,6 +166,7 @@ public:
AclModule* acl;
DataDir dataDir;
ConnectionObservers connectionObservers;
+ ConfigurationObservers configurationObservers;
QueueRegistry queues;
ExchangeRegistry exchanges;
@@ -203,9 +187,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();
@@ -317,8 +303,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
@@ -377,6 +361,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/cpp/src/qpid/broker/ConfigurationObserver.h b/cpp/src/qpid/broker/ConfigurationObserver.h
new file mode 100644
index 0000000000..701043db40
--- /dev/null
+++ b/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/cpp/src/qpid/broker/ConfigurationObservers.h b/cpp/src/qpid/broker/ConfigurationObservers.h
new file mode 100644
index 0000000000..4c1159747d
--- /dev/null
+++ b/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/cpp/src/qpid/broker/Connection.cpp b/cpp/src/qpid/broker/Connection.cpp
index 5e339cec03..8d250a32e5 100644
--- a/cpp/src/qpid/broker/Connection.cpp
+++ b/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() {
@@ -141,6 +143,8 @@ Connection::~Connection()
// a cluster-unsafe context. Don't raise an event in that case.
if (!link && isClusterSafe())
agent->raiseEvent(_qmf::EventClientDisconnect(mgmtId, ConnectionState::getUserId()));
+ QPID_LOG_CAT(debug, model, "Delete connection. user:" << ConnectionState::getUserId()
+ << " rhost:" << mgmtId );
}
broker.getConnectionObservers().closed(*this);
@@ -148,8 +152,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) {
@@ -284,6 +289,10 @@ void Connection::raiseConnectEvent() {
mgmtObject->set_authIdentity(userId);
agent->raiseEvent(_qmf::EventClientConnect(mgmtId, userId));
}
+
+ QPID_LOG_CAT(debug, model, "Create connection. user:" << userId
+ << " rhost:" << mgmtId );
+
}
void Connection::setUserProxyAuth(bool b)
@@ -300,6 +309,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 +325,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 +341,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 +453,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 +503,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/cpp/src/qpid/broker/Connection.h b/cpp/src/qpid/broker/Connection.h
index 1b8bd83139..d01599ce54 100644
--- a/cpp/src/qpid/broker/Connection.h
+++ b/cpp/src/qpid/broker/Connection.h
@@ -27,8 +27,7 @@
#include <vector>
#include <queue>
-#include <boost/ptr_container/ptr_map.hpp>
-
+#include "qpid/broker/BrokerImportExport.h"
#include "qpid/broker/ConnectionHandler.h"
#include "qpid/broker/ConnectionState.h"
#include "qpid/broker/SessionHandler.h"
@@ -86,15 +85,22 @@ 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 ();
/** Get the SessionHandler for channel. Create if it does not already exist */
SessionHandler& getChannel(framing::ChannelId channel);
- /** Close the connection */
- void close(framing::connection::CloseCode code, const std::string& text);
+ /** Close the connection. Waits for the client to respond with close-ok
+ * before actually destroying the connection.
+ */
+ QPID_BROKER_EXTERN void close(
+ framing::connection::CloseCode code, const std::string& text);
+
+ /** Abort the connection. Close abruptly and immediately. */
+ QPID_BROKER_EXTERN void abort();
// ConnectionInputHandler methods
void received(framing::AMQFrame& frame);
@@ -138,8 +144,7 @@ class Connection : public sys::ConnectionInputHandler,
void setHeartbeatInterval(uint16_t heartbeat);
void sendHeartbeat();
void restartTimeout();
- void abort();
-
+
template <class F> void eachSessionHandler(F f) {
for (ChannelMap::iterator i = channels.begin(); i != channels.end(); ++i)
f(*ptr_map_ptr(i));
@@ -149,7 +154,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; }
@@ -166,6 +174,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();
@@ -179,6 +188,8 @@ class Connection : public sys::ConnectionInputHandler,
ChannelMap channels;
qpid::sys::SecuritySettings securitySettings;
+ bool shadow;
+ bool authenticated;
ConnectionHandler adapter;
const bool link;
bool mgmtClosing;
@@ -189,11 +200,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/cpp/src/qpid/broker/ConnectionFactory.cpp b/cpp/src/qpid/broker/ConnectionFactory.cpp
index 9e0020812b..d5d24ca629 100644
--- a/cpp/src/qpid/broker/ConnectionFactory.cpp
+++ b/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/cpp/src/qpid/broker/ConnectionHandler.cpp b/cpp/src/qpid/broker/ConnectionHandler.cpp
index 6894324117..06f442a47f 100644
--- a/cpp/src/qpid/broker/ConnectionHandler.cpp
+++ b/cpp/src/qpid/broker/ConnectionHandler.cpp
@@ -36,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;
@@ -103,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)
@@ -116,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;
@@ -149,12 +152,20 @@ void ConnectionHandler::Handler::startOk(const ConnectionStartOkBody& body)
authenticator->start(body.getMechanism(), body.hasResponse() ? &body.getResponse() : 0);
} catch (std::exception& /*e*/) {
management::ManagementAgent* agent = connection.getAgent();
- if (agent) {
+ bool logEnabled;
+ QPID_LOG_TEST_CAT(debug, model, logEnabled);
+ if (logEnabled || agent)
+ {
string error;
string uid;
authenticator->getError(error);
authenticator->getUid(uid);
- agent->raiseEvent(_qmf::EventClientConnectFail(connection.getMgmtId(), uid, error));
+ if (agent) {
+ agent->raiseEvent(_qmf::EventClientConnectFail(connection.getMgmtId(), uid, error));
+ }
+ QPID_LOG_CAT(debug, model, "Failed connection. rhost:" << connection.getMgmtId()
+ << " user:" << uid
+ << " reason:" << error );
}
throw;
}
@@ -169,7 +180,9 @@ void ConnectionHandler::Handler::startOk(const ConnectionStartOkBody& body)
AclModule* acl = connection.getBroker().getAcl();
FieldTable properties;
if (acl && !acl->authorise(connection.getUserId(),acl::ACT_CREATE,acl::OBJ_LINK,"")){
- proxy.close(framing::connection::CLOSE_CODE_CONNECTION_FORCED,"ACL denied creating a federation link");
+ proxy.close(framing::connection::CLOSE_CODE_CONNECTION_FORCED,
+ QPID_MSG("ACL denied " << connection.getUserId()
+ << " creating a federation link"));
return;
}
QPID_LOG(info, "Connection is a federation link");
@@ -195,12 +208,20 @@ void ConnectionHandler::Handler::secureOk(const string& response)
authenticator->step(response);
} catch (std::exception& /*e*/) {
management::ManagementAgent* agent = connection.getAgent();
- if (agent) {
+ bool logEnabled;
+ QPID_LOG_TEST_CAT(debug, model, logEnabled);
+ if (logEnabled || agent)
+ {
string error;
string uid;
authenticator->getError(error);
authenticator->getUid(uid);
- agent->raiseEvent(_qmf::EventClientConnectFail(connection.getMgmtId(), uid, error));
+ if (agent) {
+ agent->raiseEvent(_qmf::EventClientConnectFail(connection.getMgmtId(), uid, error));
+ }
+ QPID_LOG_CAT(debug, model, "Failed connection. rhost:" << connection.getMgmtId()
+ << " user:" << uid
+ << " reason:" << error );
}
throw;
}
@@ -278,7 +299,7 @@ void ConnectionHandler::Handler::start(const FieldTable& serverProperties,
service,
host,
0, // TODO -- mgoulish Fri Sep 24 2010
- 256,
+ 256,
false ); // disallow interaction
}
std::string supportedMechanismsList;
@@ -318,7 +339,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());
@@ -367,8 +388,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/cpp/src/qpid/broker/ConnectionHandler.h b/cpp/src/qpid/broker/ConnectionHandler.h
index 2e25543308..9346e7b1ac 100644
--- a/cpp/src/qpid/broker/ConnectionHandler.h
+++ b/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/cpp/src/qpid/broker/ConnectionObservers.h b/cpp/src/qpid/broker/ConnectionObservers.h
index 07e515f3c9..e9014c80c3 100644
--- a/cpp/src/qpid/broker/ConnectionObservers.h
+++ b/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/cpp/src/qpid/broker/Consumer.h b/cpp/src/qpid/broker/Consumer.h
index 682c75ed4f..64073621be 100644
--- a/cpp/src/qpid/broker/Consumer.h
+++ b/cpp/src/qpid/broker/Consumer.h
@@ -54,7 +54,9 @@ class Consumer
bool preAcquires() const { return acquires; }
const std::string& getName() const { return name; }
+ /**@return the position of the last message seen by this consumer */
virtual framing::SequenceNumber getPosition() const { return position; }
+
virtual void setPosition(framing::SequenceNumber pos) { position = pos; }
virtual bool deliver(QueuedMessage& msg) = 0;
diff --git a/cpp/src/qpid/broker/Daemon.h b/cpp/src/qpid/broker/Daemon.h
index a9cd98bce2..2bb9fc5577 100644
--- a/cpp/src/qpid/broker/Daemon.h
+++ b/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/cpp/src/qpid/broker/DirectExchange.cpp b/cpp/src/qpid/broker/DirectExchange.cpp
index 5d9aea7509..2fa7ce0fc5 100644
--- a/cpp/src/qpid/broker/DirectExchange.cpp
+++ b/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/cpp/src/qpid/broker/Exchange.cpp b/cpp/src/qpid/broker/Exchange.cpp
index 8d20b0df81..82d4b4df15 100644
--- a/cpp/src/qpid/broker/Exchange.cpp
+++ b/cpp/src/qpid/broker/Exchange.cpp
@@ -35,6 +35,8 @@
namespace qpid {
namespace broker {
+using std::string;
+
using namespace qpid::framing;
using qpid::framing::Buffer;
using qpid::framing::FieldTable;
@@ -167,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)
diff --git a/cpp/src/qpid/broker/Exchange.h b/cpp/src/qpid/broker/Exchange.h
index 7376f814ed..fba752210f 100644
--- a/cpp/src/qpid/broker/Exchange.h
+++ b/cpp/src/qpid/broker/Exchange.h
@@ -174,8 +174,9 @@ public:
bool isDurable() { return durable; }
qpid::framing::FieldTable& getArgs() { return args; }
- Exchange::shared_ptr getAlternate() { return alternate; }
- void setAlternate(Exchange::shared_ptr _alternate);
+ QPID_BROKER_EXTERN Exchange::shared_ptr getAlternate() { return alternate; }
+ QPID_BROKER_EXTERN void setAlternate(Exchange::shared_ptr _alternate);
+
void incAlternateUsers() { alternateUsers++; }
void decAlternateUsers() { alternateUsers--; }
bool inUseAsAlternate() { return alternateUsers > 0; }
diff --git a/cpp/src/qpid/broker/ExchangeRegistry.cpp b/cpp/src/qpid/broker/ExchangeRegistry.cpp
index 43d7268dfb..b31c7bd7b8 100644
--- a/cpp/src/qpid/broker/ExchangeRegistry.cpp
+++ b/cpp/src/qpid/broker/ExchangeRegistry.cpp
@@ -19,6 +19,7 @@
*
*/
+#include "qpid/broker/Broker.h"
#include "qpid/broker/ExchangeRegistry.h"
#include "qpid/broker/DirectExchange.h"
#include "qpid/broker/FanOutExchange.h"
@@ -42,38 +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 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);
+ 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){
@@ -82,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/cpp/src/qpid/broker/FanOutExchange.cpp b/cpp/src/qpid/broker/FanOutExchange.cpp
index 2bce99b6fe..56c894c129 100644
--- a/cpp/src/qpid/broker/FanOutExchange.cpp
+++ b/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/cpp/src/qpid/broker/HeadersExchange.cpp b/cpp/src/qpid/broker/HeadersExchange.cpp
index 6648ae0422..9975d26c72 100644
--- a/cpp/src/qpid/broker/HeadersExchange.cpp
+++ b/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/cpp/src/qpid/broker/Link.cpp b/cpp/src/qpid/broker/Link.cpp
index f21c861149..84dd163ac3 100644
--- a/cpp/src/qpid/broker/Link.cpp
+++ b/cpp/src/qpid/broker/Link.cpp
@@ -125,18 +125,20 @@ boost::shared_ptr<Exchange> Link::linkExchangeFactory( const std::string& _name
return Exchange::shared_ptr(new LinkExchange(_name));
}
-Link::Link(LinkRegistry* _links,
- MessageStore* _store,
+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),
+ Manageable* parent,
+ bool failover_)
+ : name(_name), links(_links),
configuredTransport(_transport), configuredHost(_host), configuredPort(_port),
host(_host), port(_port), transport(_transport),
durable(_durable),
@@ -149,7 +151,9 @@ Link::Link(LinkRegistry* _links,
channelCounter(1),
connection(0),
agent(0),
+ listener(l),
timerTask(new LinkTimerTask(*this, broker->getTimer())),
+ failover(failover_),
failoverChannel(0)
{
if (parent != 0 && broker != 0)
@@ -157,7 +161,10 @@ Link::Link(LinkRegistry* _links,
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);
}
}
@@ -169,13 +176,15 @@ Link::Link(LinkRegistry* _links,
}
broker->getTimer().add(timerTask);
- stringstream _name;
- _name << "qpid.link." << transport << ":" << host << ":" << port;
- std::pair<Exchange::shared_ptr, bool> rc = broker->getExchanges().declare(_name.str(),
- exchangeTypeName);
- failoverExchange = boost::static_pointer_cast<LinkExchange>(rc.first);
- assert(failoverExchange);
- failoverExchange->setLink(this);
+ 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 ()
@@ -187,7 +196,8 @@ Link::~Link ()
if (mgmtObject != 0)
mgmtObject->resourceDestroy ();
- broker->getExchanges().destroy(failoverExchange->getName());
+ if (failover)
+ broker->getExchanges().destroy(failoverExchange->getName());
}
void Link::setStateLH (int newState)
@@ -239,16 +249,19 @@ void Link::established(Connection* c)
if (!hideManagement() && agent)
agent->raiseEvent(_qmf::EventBrokerLinkUp(addr.str()));
-
- Mutex::ScopedLock mutex(lock);
- setStateLH(STATE_OPERATIONAL);
- currentInterval = 1;
- visitCount = 0;
- connection = c;
- if (closing)
+ bool isClosing = false;
+ {
+ Mutex::ScopedLock mutex(lock);
+ setStateLH(STATE_OPERATIONAL);
+ currentInterval = 1;
+ visitCount = 0;
+ connection = c;
+ isClosing = closing;
+ }
+ if (isClosing)
destroy();
else // Process any IO tasks bridges added before established.
- connection->requestIOProcessing (boost::bind(&Link::ioThreadProcessing, this));
+ c->requestIOProcessing (boost::bind(&Link::ioThreadProcessing, this));
}
@@ -261,16 +274,26 @@ void Link::setUrl(const Url& u) {
namespace {
- /** invoked when session used to subscribe to remote's amq.failover exchange detaches */
- void sessionDetached(Link *link) {
- QPID_LOG(debug, "detached from 'amq.failover' for link: " << link->getName());
- }
+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();
@@ -282,80 +305,82 @@ void Link::opened() {
QPID_LOG(debug, "Known hosts for peer of inter-broker link: " << url);
}
- //
- // 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.setDetachedCallback( boost::bind(&sessionDetached, 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
+ 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().flow(failoverExchange->getName(), 0, 0xFFFFFFFF);
- remoteBroker.getMessage().flow(failoverExchange->getName(), 1, 0xFFFFFFFF);
+ 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)
{
- bool isClosing = false;
- {
- Mutex::ScopedLock mutex(lock);
- QPID_LOG (info, "Inter-broker link disconnected from " << host << ":" << port << " " << text);
+ Mutex::ScopedLock mutex(lock);
+ QPID_LOG (info, "Inter-broker link disconnected from " << host << ":" << port << " " << text);
- connection = 0;
- if (state == STATE_OPERATIONAL) {
+ connection = 0;
+
+ if (!hideManagement()) {
+ mgmtObject->set_connectionRef(qpid::management::ObjectId());
+ if (state == STATE_OPERATIONAL && agent) {
stringstream addr;
addr << host << ":" << port;
- if (!hideManagement() && agent)
- agent->raiseEvent(_qmf::EventBrokerLinkDown(addr.str()));
+ agent->raiseEvent(_qmf::EventBrokerLinkDown(addr.str()));
}
+ }
- for (Bridges::iterator i = active.begin(); i != active.end(); i++) {
- (*i)->closed();
- created.push_back(*i);
- }
- active.clear();
+ for (Bridges::iterator i = active.begin(); i != active.end(); i++) {
+ (*i)->closed();
+ created.push_back(*i);
+ }
+ active.clear();
- if (state != STATE_FAILED && state != STATE_PASSIVE)
- {
- setStateLH(STATE_WAITING);
- if (!hideManagement())
- mgmtObject->set_lastError (text);
- }
+ if (state != STATE_FAILED && state != STATE_PASSIVE)
+ {
+ setStateLH(STATE_WAITING);
+ if (!hideManagement())
+ mgmtObject->set_lastError (text);
}
- // Call destroy outside of the lock, don't want to be deleted with lock held.
- if (isClosing)
- 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);
@@ -374,14 +399,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 (configuredHost, configuredPort);
+ listener(this); // notify LinkRegistry that this Link has been destroyed
}
void Link::add(Bridge::shared_ptr bridge)
@@ -423,7 +447,7 @@ void Link::ioThreadProcessing()
{
Mutex::ScopedLock mutex(lock);
- if (state != STATE_OPERATIONAL)
+ if (state != STATE_OPERATIONAL || closing)
return;
// check for bridge session errors and recover
@@ -460,7 +484,7 @@ void Link::ioThreadProcessing()
void Link::maintenanceVisit ()
{
Mutex::ScopedLock mutex(lock);
-
+ if (closing) return;
if (state == STATE_WAITING)
{
visitCount++;
@@ -476,21 +500,27 @@ void Link::maintenanceVisit ()
}
}
}
- else if (state == STATE_OPERATIONAL && (!active.empty() || !created.empty() || !cancellations.empty()) && connection != 0)
+ else if (state == STATE_OPERATIONAL &&
+ (!active.empty() || !created.empty() || !cancellations.empty()) &&
+ connection && connection->isOpen())
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() {
@@ -499,15 +529,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());
@@ -536,18 +565,34 @@ void Link::setPersistenceId(uint64_t id) const
const string& Link::getName() const
{
- return configuredHost;
+ 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);
@@ -556,12 +601,21 @@ 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(ENCODED_IDENTIFIER);
+ buffer.putShortString(name);
buffer.putShortString(configuredHost);
buffer.putShort(configuredPort);
buffer.putShortString(configuredTransport);
@@ -573,8 +627,9 @@ void Link::encode(Buffer& buffer) const
uint32_t Link::encodedSize() const
{
- return configuredHost.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
+ configuredTransport.size() + 1 // short-string(transport)
+ 1 // durable
@@ -589,6 +644,7 @@ ManagementObject* Link::GetManagementObject (void) const
}
void Link::close() {
+ QPID_LOG(debug, "Link::close(), link=" << name );
Mutex::ScopedLock mutex(lock);
if (!closing) {
closing = true;
@@ -609,36 +665,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 (configuredHost, configuredPort, 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;
}
@@ -666,11 +717,13 @@ void Link::closeConnection( const std::string& reason)
{
if (connection != 0) {
// cancel our subscription to the failover exchange
- SessionHandler& sessionHandler = connection->getChannel(failoverChannel);
- if (sessionHandler.getSession()) {
- framing::AMQP_ServerProxy remoteBroker(sessionHandler.out);
- remoteBroker.getMessage().cancel(failoverExchange->getName());
- remoteBroker.getSession().detach(failoverSession);
+ 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;
@@ -716,6 +769,23 @@ void Link::setState(const framing::FieldTable& state)
}
}
+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");
diff --git a/cpp/src/qpid/broker/Link.h b/cpp/src/qpid/broker/Link.h
index a97fa48664..f0cb90e73b 100644
--- a/cpp/src/qpid/broker/Link.h
+++ b/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"
@@ -52,8 +51,8 @@ class LinkExchange;
class Link : public PersistableConfig, public management::Manageable {
private:
mutable sys::Mutex lock;
+ const std::string name;
LinkRegistry* links;
- MessageStore* store;
// these remain constant across failover - used to identify this link
const std::string configuredTransport;
@@ -64,7 +63,8 @@ class Link : public PersistableConfig, public management::Manageable {
uint16_t port;
std::string transport;
- bool durable;
+ bool durable;
+
std::string authMechanism;
std::string username;
std::string password;
@@ -85,8 +85,10 @@ 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;
@@ -101,33 +103,39 @@ 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
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();
/** these return the *configured* transport/host/port, which does not change over the
@@ -139,7 +147,7 @@ class Link : public PersistableConfig, public management::Manageable {
/** 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 */
- bool getRemoteAddress(qpid::Address& addr) const;
+ QPID_BROKER_EXTERN bool getRemoteAddress(qpid::Address& addr) const;
bool isDurable() { return durable; }
void maintenanceVisit ();
@@ -148,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;
@@ -165,7 +175,10 @@ 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;
@@ -178,6 +191,16 @@ class Link : public PersistableConfig, public management::Manageable {
// 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);
+
+ /** The current connction for this link. Note returns 0 if the link is not
+ * presently connected.
+ */
+ Connection* getConnection() { return connection; }
};
}
}
diff --git a/cpp/src/qpid/broker/LinkRegistry.cpp b/cpp/src/qpid/broker/LinkRegistry.cpp
index d89f220d1b..0507fe6521 100644
--- a/cpp/src/qpid/broker/LinkRegistry.cpp
+++ b/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,26 +266,71 @@ 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 = 0;
+ 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();
+ break;
+ }
+ }
+ }
+
if (link) {
link->established(c);
c->setUserId(str(format("%1%@%2%") % link->getUsername() % realm));
@@ -343,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);
@@ -369,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/cpp/src/qpid/broker/LinkRegistry.h b/cpp/src/qpid/broker/LinkRegistry.h
index 8e9d2f4b0d..5a39b62bd1 100644
--- a/cpp/src/qpid/broker/LinkRegistry.h
+++ b/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/cpp/src/qpid/broker/Message.cpp b/cpp/src/qpid/broker/Message.cpp
index 40dfba39f4..4dd8a349dd 100644
--- a/cpp/src/qpid/broker/Message.cpp
+++ b/cpp/src/qpid/broker/Message.cpp
@@ -384,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/cpp/src/qpid/broker/Message.h b/cpp/src/qpid/broker/Message.h
index dda45d73e6..90e4eec889 100644
--- a/cpp/src/qpid/broker/Message.h
+++ b/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/cpp/src/qpid/broker/MessageDeque.cpp b/cpp/src/qpid/broker/MessageDeque.cpp
index f70c996975..83c8ca6868 100644
--- a/cpp/src/qpid/broker/MessageDeque.cpp
+++ b/cpp/src/qpid/broker/MessageDeque.cpp
@@ -40,13 +40,16 @@ size_t MessageDeque::index(const framing::SequenceNumber& position)
bool MessageDeque::deleted(const QueuedMessage& m)
{
size_t i = index(m.position);
- if (i < messages.size() && messages[i].status != QueuedMessage::DELETED) {
- messages[i].status = QueuedMessage::DELETED;
- clean();
- return true;
- } else {
- return false;
+ if (i < messages.size()) {
+ 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()
@@ -144,6 +147,7 @@ QueuedMessage* MessageDeque::pushPtr(const QueuedMessage& added) {
messages.back().status = QueuedMessage::AVAILABLE;
if (head >= messages.size()) head = messages.size() - 1;
++available;
+ clean(); // QPID-4046: let producer help clean the backlog of deleted messages
return &messages.back();
}
@@ -173,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/cpp/src/qpid/broker/MessageDeque.h b/cpp/src/qpid/broker/MessageDeque.h
index 9b53716d4e..c5670b2a72 100644
--- a/cpp/src/qpid/broker/MessageDeque.h
+++ b/cpp/src/qpid/broker/MessageDeque.h
@@ -44,7 +44,7 @@ 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);
diff --git a/cpp/src/qpid/broker/MessageMap.cpp b/cpp/src/qpid/broker/MessageMap.cpp
index 9b164d4e5c..592f3fefde 100644
--- a/cpp/src/qpid/broker/MessageMap.cpp
+++ b/cpp/src/qpid/broker/MessageMap.cpp
@@ -21,6 +21,7 @@
#include "qpid/broker/MessageMap.h"
#include "qpid/broker/QueuedMessage.h"
#include "qpid/log/Statement.h"
+#include <algorithm>
namespace qpid {
namespace broker {
@@ -130,18 +131,24 @@ bool MessageMap::push(const QueuedMessage& added, QueuedMessage& removed)
QueuedMessage& a = messages[added.position];
a = added;
a.status = QueuedMessage::AVAILABLE;
- QPID_LOG(debug, "Added message at " << a.position);
+ 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 at " << removed.position << " with " << result.first->second.position << ": " << result.first->first);
+ 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.
+ (void) seq; assert(messages.empty() || (--messages.end())->first <= seq);
+}
+
void MessageMap::foreach(Functor f)
{
for (Ordering::iterator i = messages.begin(); i != messages.end(); ++i) {
diff --git a/cpp/src/qpid/broker/MessageMap.h b/cpp/src/qpid/broker/MessageMap.h
index a668450250..1f0481cb6b 100644
--- a/cpp/src/qpid/broker/MessageMap.h
+++ b/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
@@ -50,6 +50,7 @@ class MessageMap : public Messages
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/cpp/src/qpid/broker/Messages.h b/cpp/src/qpid/broker/Messages.h
index 61e9fa110a..45f5e6cd81 100644
--- a/cpp/src/qpid/broker/Messages.h
+++ b/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/cpp/src/qpid/broker/NameGenerator.h b/cpp/src/qpid/broker/NameGenerator.h
index 6ea25c9797..2e9f7febe2 100644
--- a/cpp/src/qpid/broker/NameGenerator.h
+++ b/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/cpp/src/qpid/broker/Observers.h b/cpp/src/qpid/broker/Observers.h
new file mode 100644
index 0000000000..c62f75d6d0
--- /dev/null
+++ b/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/cpp/src/qpid/broker/PriorityQueue.cpp b/cpp/src/qpid/broker/PriorityQueue.cpp
index ab5ec7235a..9a0fead744 100644
--- a/cpp/src/qpid/broker/PriorityQueue.cpp
+++ b/cpp/src/qpid/broker/PriorityQueue.cpp
@@ -121,6 +121,10 @@ void PriorityQueue::updateAcquired(const QueuedMessage& acquired) {
fifo.updateAcquired(acquired);
}
+void PriorityQueue::setPosition(const framing::SequenceNumber& n) {
+ fifo.setPosition(n);
+}
+
void PriorityQueue::foreach(Functor f)
{
fifo.foreach(f);
diff --git a/cpp/src/qpid/broker/PriorityQueue.h b/cpp/src/qpid/broker/PriorityQueue.h
index 8628745db1..301367358b 100644
--- a/cpp/src/qpid/broker/PriorityQueue.h
+++ b/cpp/src/qpid/broker/PriorityQueue.h
@@ -52,6 +52,7 @@ class PriorityQueue : 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);
diff --git a/cpp/src/qpid/broker/PrivateImplRef.h b/cpp/src/qpid/broker/PrivateImplRef.h
index 5932ab882b..d20c2f4608 100644
--- a/cpp/src/qpid/broker/PrivateImplRef.h
+++ b/cpp/src/qpid/broker/PrivateImplRef.h
@@ -88,15 +88,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/cpp/src/qpid/broker/Queue.cpp b/cpp/src/qpid/broker/Queue.cpp
index e7305c021d..d5267c78dc 100644
--- a/cpp/src/qpid/broker/Queue.cpp
+++ b/cpp/src/qpid/broker/Queue.cpp
@@ -49,6 +49,7 @@
#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>
@@ -67,6 +68,7 @@ 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;
@@ -176,7 +178,8 @@ Queue::Queue(const string& _name, bool _autodelete,
ManagementAgent* agent = broker->getManagementAgent();
if (agent != 0) {
- mgmtObject = new _qmf::Queue(agent, this, parent, _name, _store != 0, _autodelete, _owner != 0);
+ mgmtObject = new _qmf::Queue(agent, this, parent, _name, _store != 0, _autodelete);
+ mgmtObject->set_exclusive(_owner != 0);
agent->addObject(mgmtObject, 0, store != 0);
brokerMgmtObject = (qmf::org::apache::qpid::broker::Broker*) broker->GetManagementObject();
if (brokerMgmtObject)
@@ -587,21 +590,51 @@ QueuedMessage Queue::get(){
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.
@@ -609,37 +642,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));
- }
-
- if (!expired.empty()) {
+ std::deque<QueuedMessage> dequeued;
+ dequeueIf(boost::bind(&isExpired, _1), dequeued);
+ if (dequeued.size()) {
if (mgmtObject) {
- mgmtObject->inc_acquires(expired.size());
- mgmtObject->inc_discardsTtl(expired.size());
- if (brokerMgmtObject) {
- brokerMgmtObject->inc_acquires(expired.size());
- brokerMgmtObject->inc_discardsTtl(expired.size());
- }
- }
-
- for (std::deque<QueuedMessage>::const_iterator i = expired.begin();
- i != expired.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 );
+ mgmtObject->inc_discardsTtl(dequeued.size());
+ if (brokerMgmtObject)
+ brokerMgmtObject->inc_discardsTtl(dequeued.size());
}
}
}
}
-
namespace {
// for use with purge/move below - collect messages that match a given filter
//
@@ -797,6 +811,7 @@ uint32_t Queue::purge(const uint32_t purge_request, boost::shared_ptr<Exchange>
// now reroute if necessary
if (dest.get()) {
assert(qmsg->payload);
+ qmsg->payload->clearTrace();
DeliverableMessage dmsg(qmsg->payload);
dest->routeWithAlternate(dmsg);
}
@@ -888,9 +903,10 @@ void Queue::push(boost::intrusive_ptr<Message>& msg, bool isRecovery){
if (mgmtObject) {
mgmtObject->inc_acquires();
mgmtObject->inc_discardsLvq();
- if (brokerMgmtObject)
+ if (brokerMgmtObject) {
brokerMgmtObject->inc_acquires();
brokerMgmtObject->inc_discardsLvq();
+ }
}
if (isRecovery) {
//can't issue new requests for the store until
@@ -1470,12 +1486,18 @@ 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()));
+ QPID_LOG_CAT(debug, model, "Delete queue. name:" << queue->getName()
+ << " user:" << userId
+ << " rhost:" << connectionId );
}
}
@@ -1483,9 +1505,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:"+q->getName()), 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()
{
@@ -1493,19 +1517,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);
}
}
@@ -1659,13 +1683,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;
}
diff --git a/cpp/src/qpid/broker/Queue.h b/cpp/src/qpid/broker/Queue.h
index 9869a698c1..a31e0002ea 100644
--- a/cpp/src/qpid/broker/Queue.h
+++ b/cpp/src/qpid/broker/Queue.h
@@ -175,6 +175,7 @@ class Queue : public boost::enable_shared_from_this<Queue>,
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:
@@ -343,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);
@@ -375,12 +376,21 @@ class Queue : public boost::enable_shared_from_this<Queue>,
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/cpp/src/qpid/broker/QueueFlowLimit.cpp b/cpp/src/qpid/broker/QueueFlowLimit.cpp
index f15bb45c01..14fe5f4022 100644
--- a/cpp/src/qpid/broker/QueueFlowLimit.cpp
+++ b/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/cpp/src/qpid/broker/QueuePolicy.cpp b/cpp/src/qpid/broker/QueuePolicy.cpp
index d5b4c1ae86..3978420f4e 100644
--- a/cpp/src/qpid/broker/QueuePolicy.cpp
+++ b/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/cpp/src/qpid/broker/QueueRegistry.cpp b/cpp/src/qpid/broker/QueueRegistry.cpp
index 236d5ae34c..1401356444 100644
--- a/cpp/src/qpid/broker/QueueRegistry.cpp
+++ b/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,40 +47,49 @@ 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::destroy (const string& name){
- RWlock::ScopedWlock locker(lock);
- destroyLH (name);
+void QueueRegistry::destroy(const string& name) {
+ Queue::shared_ptr q;
+ {
+ qpid::sys::RWlock::ScopedWlock locker(lock);
+ QueueMap::iterator i = queues.find(name);
+ if (i != queues.end()) {
+ Queue::shared_ptr q = i->second;
+ queues.erase(i);
+ }
+ }
+ if (broker && q) broker->getConfigurationObservers().queueDestroy(q);
}
Queue::shared_ptr QueueRegistry::find(const string& name){
diff --git a/cpp/src/qpid/broker/QueueRegistry.h b/cpp/src/qpid/broker/QueueRegistry.h
index f724e6b10c..a354513c5f 100644
--- a/cpp/src/qpid/broker/QueueRegistry.h
+++ b/cpp/src/qpid/broker/QueueRegistry.h
@@ -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
@@ -61,7 +61,7 @@ class QueueRegistry {
QPID_BROKER_EXTERN std::pair<boost::shared_ptr<Queue>, bool> declare(
const std::string& name,
bool durable = false,
- bool autodelete = false,
+ bool autodelete = false,
const OwnershipToken* owner = 0,
boost::shared_ptr<Exchange> alternateExchange = boost::shared_ptr<Exchange>(),
const qpid::framing::FieldTable& args = framing::FieldTable(),
@@ -82,9 +82,8 @@ class QueueRegistry {
QPID_BROKER_EXTERN void destroy(const std::string& name);
template <class Test> bool destroyIf(const std::string& name, Test test)
{
- qpid::sys::RWlock::ScopedWlock locker(lock);
if (test()) {
- destroyLH (name);
+ destroy(name);
return true;
} else {
return false;
@@ -127,13 +126,13 @@ class QueueRegistry {
for (QueueMap::const_iterator i = queues.begin(); i != queues.end(); ++i)
f(i->second);
}
-
+
/**
* Change queue mode when cluster size drops to 1 node, expands again
* in practice allows flow queue to disk when last name to be exectuted
*/
void updateQueueClusterState(bool lastNode);
-
+
private:
typedef std::map<std::string, boost::shared_ptr<Queue> > QueueMap;
QueueMap queues;
@@ -144,12 +143,9 @@ private:
management::Manageable* parent;
bool lastNode; //used to set mode on queue declare
Broker* broker;
-
- //destroy impl that assumes lock is already held:
- void destroyLH (const std::string& name);
};
-
+
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/RecoveryManagerImpl.cpp b/cpp/src/qpid/broker/RecoveryManagerImpl.cpp
index d08409695e..858535637a 100644
--- a/cpp/src/qpid/broker/RecoveryManagerImpl.cpp
+++ b/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/cpp/src/qpid/broker/SaslAuthenticator.cpp b/cpp/src/qpid/broker/SaslAuthenticator.cpp
index 80fa5e1c0e..2d7c820b63 100644
--- a/cpp/src/qpid/broker/SaslAuthenticator.cpp
+++ b/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,6 +23,7 @@
# 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"
@@ -37,6 +38,7 @@
using qpid::sys::cyrus::CyrusSecurityLayer;
#endif
+using std::string;
using namespace qpid::framing;
using qpid::sys::SecurityLayer;
using qpid::sys::SecuritySettings;
@@ -164,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));
@@ -178,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() {}
@@ -214,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);
@@ -226,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());
}
@@ -240,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();
@@ -271,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;
@@ -319,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));
@@ -371,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");
@@ -382,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++;
@@ -404,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;
@@ -439,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);
@@ -490,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/cpp/src/qpid/broker/SaslAuthenticator.h b/cpp/src/qpid/broker/SaslAuthenticator.h
index 4e5d43214c..e5ecc9f6ec 100644
--- a/cpp/src/qpid/broker/SaslAuthenticator.h
+++ b/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/cpp/src/qpid/broker/SecureConnectionFactory.cpp b/cpp/src/qpid/broker/SecureConnectionFactory.cpp
index 754b443c22..757f6efc59 100644
--- a/cpp/src/qpid/broker/SecureConnectionFactory.cpp
+++ b/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/cpp/src/qpid/broker/SemanticState.cpp b/cpp/src/qpid/broker/SemanticState.cpp
index 64924bdd4c..9a84db547c 100644
--- a/cpp/src/qpid/broker/SemanticState.cpp
+++ b/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/cpp/src/qpid/broker/SemanticState.h b/cpp/src/qpid/broker/SemanticState.h
index e5e1d2da16..15928ce599 100644
--- a/cpp/src/qpid/broker/SemanticState.h
+++ b/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/cpp/src/qpid/broker/SessionAdapter.cpp b/cpp/src/qpid/broker/SessionAdapter.cpp
index 78f2e43ce0..ae994a6bd5 100644
--- a/cpp/src/qpid/broker/SessionAdapter.cpp
+++ b/cpp/src/qpid/broker/SessionAdapter.cpp
@@ -41,6 +41,8 @@
namespace qpid {
namespace broker {
+using std::string;
+
using namespace qpid;
using namespace qpid::framing;
using namespace qpid::framing::dtx;
@@ -107,6 +109,12 @@ void SessionAdapter::ExchangeHandlerImpl::declare(const string& exchange, const
false,
ManagementAgent::toMap(args),
"existing"));
+ QPID_LOG_CAT(debug, model, "Create exchange. name:" << exchange
+ << " user:" << getConnection().getUserId()
+ << " rhost:" << getConnection().getUrl()
+ << " type:" << type
+ << " alternateExchange:" << alternateExchange
+ << " durable:" << (durable ? "T" : "F"));
}
}catch(UnknownExchangeTypeException& /*e*/){
throw NotFoundException(QPID_MSG("Exchange type not implemented: " << type));
@@ -204,7 +212,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())
{}
@@ -223,7 +234,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());
}
@@ -307,6 +318,14 @@ void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string&
agent->raiseEvent(_qmf::EventQueueDeclare(getConnection().getUrl(), getConnection().getUserId(),
name, durable, exclusive, autoDelete, alternateExchange, ManagementAgent::toMap(arguments),
"existing"));
+ QPID_LOG_CAT(debug, model, "Create queue. name:" << name
+ << " user:" << getConnection().getUserId()
+ << " rhost:" << getConnection().getUrl()
+ << " durable:" << (durable ? "T" : "F")
+ << " exclusive:" << (exclusive ? "T" : "F")
+ << " autodelete:" << (autoDelete ? "T" : "F")
+ << " alternateExchange:" << alternateExchange
+ );
}
}
@@ -411,6 +430,12 @@ SessionAdapter::MessageHandlerImpl::subscribe(const string& queueName,
if (agent)
agent->raiseEvent(_qmf::EventSubscribe(getConnection().getUrl(), getConnection().getUserId(),
queueName, destination, exclusive, ManagementAgent::toMap(arguments)));
+ QPID_LOG_CAT(debug, model, "Create subscription. queue:" << queueName
+ << " destination:" << destination
+ << " user:" << getConnection().getUserId()
+ << " rhost:" << getConnection().getUrl()
+ << " exclusive:" << (exclusive ? "T" : "F")
+ );
}
void
@@ -423,6 +448,9 @@ SessionAdapter::MessageHandlerImpl::cancel(const string& destination )
ManagementAgent* agent = getBroker().getManagementAgent();
if (agent)
agent->raiseEvent(_qmf::EventUnsubscribe(getConnection().getUrl(), getConnection().getUserId(), destination));
+ QPID_LOG_CAT(debug, model, "Delete subscription. destination:" << destination
+ << " user:" << getConnection().getUserId()
+ << " rhost:" << getConnection().getUrl() );
}
void
diff --git a/cpp/src/qpid/broker/SessionAdapter.h b/cpp/src/qpid/broker/SessionAdapter.h
index bc056538b1..3cc745f96c 100644
--- a/cpp/src/qpid/broker/SessionAdapter.h
+++ b/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);
diff --git a/cpp/src/qpid/broker/SessionContext.h b/cpp/src/qpid/broker/SessionContext.h
index 253ce8dcf2..ee98da1878 100644
--- a/cpp/src/qpid/broker/SessionContext.h
+++ b/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/cpp/src/qpid/broker/SessionHandler.cpp b/cpp/src/qpid/broker/SessionHandler.cpp
index b58c7c01c5..23fa2ee0ca 100644
--- a/cpp/src/qpid/broker/SessionHandler.cpp
+++ b/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,7 +80,7 @@ void SessionHandler::handleDetach() {
if (session.get())
connection.getBroker().getSessionManager().detach(session);
assert(!session.get());
- if (detachedCallback) detachedCallback();
+ if (errorListener) errorListener->detach();
connection.closeChannel(channel.get());
}
@@ -118,8 +134,4 @@ void SessionHandler::attached(const std::string& name)
}
}
-void SessionHandler::setDetachedCallback(boost::function<void()> cb) {
- detachedCallback = cb;
-}
-
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/SessionHandler.h b/cpp/src/qpid/broker/SessionHandler.h
index 4e2cfaa963..ab87cf41a4 100644
--- a/cpp/src/qpid/broker/SessionHandler.h
+++ b/cpp/src/qpid/broker/SessionHandler.h
@@ -25,7 +25,7 @@
#include "qpid/amqp_0_10/SessionHandler.h"
#include "qpid/broker/SessionHandler.h"
#include "qpid/framing/AMQP_ClientProxy.h"
-#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
namespace qpid {
class SessionState;
@@ -43,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();
@@ -71,7 +86,7 @@ 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 setDetachedCallback(boost::function<void()> cb);
+ void setErrorListener(boost::shared_ptr<ErrorListener> e) { errorListener = e; }
protected:
virtual void setState(const std::string& sessionName, bool force);
@@ -94,7 +109,7 @@ class SessionHandler : public amqp_0_10::SessionHandler {
framing::AMQP_ClientProxy proxy;
std::auto_ptr<SessionState> session;
std::auto_ptr<SetChannelProxy> clusterOrderProxy;
- boost::function<void ()> detachedCallback;
+ boost::shared_ptr<ErrorListener> errorListener;
};
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/SessionState.cpp b/cpp/src/qpid/broker/SessionState.cpp
index 99407bc3a6..cc02d9ec94 100644
--- a/cpp/src/qpid/broker/SessionState.cpp
+++ b/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/cpp/src/qpid/broker/SessionState.h b/cpp/src/qpid/broker/SessionState.h
index 8db232a2d6..a8ff7feff9 100644
--- a/cpp/src/qpid/broker/SessionState.h
+++ b/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/cpp/src/qpid/broker/System.cpp b/cpp/src/qpid/broker/System.cpp
index 8cd2edda76..fa8df6406b 100644
--- a/cpp/src/qpid/broker/System.cpp
+++ b/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/cpp/src/qpid/broker/System.h b/cpp/src/qpid/broker/System.h
index 0fc2c2bd88..6847c662ae 100644
--- a/cpp/src/qpid/broker/System.h
+++ b/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/cpp/src/qpid/broker/TopicExchange.cpp b/cpp/src/qpid/broker/TopicExchange.cpp
index dd3ec13019..c11389bb17 100644
--- a/cpp/src/qpid/broker/TopicExchange.cpp
+++ b/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/cpp/src/qpid/broker/TopicExchange.h b/cpp/src/qpid/broker/TopicExchange.h
index cc24e1411e..46871a1c6b 100644
--- a/cpp/src/qpid/broker/TopicExchange.h
+++ b/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/cpp/src/qpid/broker/TopicKeyNode.h b/cpp/src/qpid/broker/TopicKeyNode.h
new file mode 100644
index 0000000000..7671ed069d
--- /dev/null
+++ b/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/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp b/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp
index a38e6ac12a..40e74be018 100644
--- a/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp
+++ b/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp
@@ -32,6 +32,8 @@
using namespace qpid::framing;
using qpid::sys::SecurityLayer;
+using std::string;
+
namespace qpid {
namespace broker {
@@ -79,7 +81,7 @@ void SaslAuthenticator::fini(void)
return;
}
-std::auto_ptr<SaslAuthenticator> SaslAuthenticator::createAuthenticator(Connection& c, bool)
+std::auto_ptr<SaslAuthenticator> SaslAuthenticator::createAuthenticator(Connection& c)
{
if (c.getBroker().getOptions().auth) {
return std::auto_ptr<SaslAuthenticator>(new SspiAuthenticator(c));
diff --git a/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp b/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp
index 1dff1ddc8f..fb59d058f8 100644
--- a/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp
+++ b/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/cpp/src/qpid/client/Connection.cpp b/cpp/src/qpid/client/Connection.cpp
index 83a4a35b53..8b4eafccaa 100644
--- a/cpp/src/qpid/client/Connection.cpp
+++ b/cpp/src/qpid/client/Connection.cpp
@@ -136,7 +136,7 @@ const ConnectionSettings& Connection::getNegotiatedSettings()
Session Connection::newSession(const std::string& name, uint32_t timeout) {
if (!isOpen())
- throw Exception(QPID_MSG("Connection has not yet been opened"));
+ throw TransportFailure("Can't create session, connection is not open");
Session s;
SessionBase_0_10Access(s).set(impl->newSession(name, timeout));
return s;
diff --git a/cpp/src/qpid/client/ConnectionHandler.cpp b/cpp/src/qpid/client/ConnectionHandler.cpp
index 94561f8079..91838d8e8b 100644
--- a/cpp/src/qpid/client/ConnectionHandler.cpp
+++ b/cpp/src/qpid/client/ConnectionHandler.cpp
@@ -258,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 {
@@ -272,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);
}
}
@@ -280,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/cpp/src/qpid/client/PrivateImplRef.h b/cpp/src/qpid/client/PrivateImplRef.h
index 503a383c31..fa89b1bfa0 100644
--- a/cpp/src/qpid/client/PrivateImplRef.h
+++ b/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/cpp/src/qpid/client/SessionImpl.cpp b/cpp/src/qpid/client/SessionImpl.cpp
index 3f3ad617f4..91e728d5ae 100644
--- a/cpp/src/qpid/client/SessionImpl.cpp
+++ b/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/cpp/src/qpid/client/SslConnector.cpp b/cpp/src/qpid/client/SslConnector.cpp
index ab0c5c4957..4c6fadd28a 100644
--- a/cpp/src/qpid/client/SslConnector.cpp
+++ b/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;
@@ -176,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/cpp/src/qpid/client/SubscriptionManagerImpl.cpp b/cpp/src/qpid/client/SubscriptionManagerImpl.cpp
index a558d90be8..7dead112e5 100644
--- a/cpp/src/qpid/client/SubscriptionManagerImpl.cpp
+++ b/cpp/src/qpid/client/SubscriptionManagerImpl.cpp
@@ -39,6 +39,16 @@ SubscriptionManagerImpl::SubscriptionManagerImpl(const Session& s)
: dispatcher(s), session(s), autoStop(true)
{}
+SubscriptionManagerImpl::~SubscriptionManagerImpl()
+{
+ sys::Mutex::ScopedLock l(lock);
+ for (std::map<std::string, Subscription>::iterator i = subscriptions.begin(); i != subscriptions.end(); ++i) {
+ boost::intrusive_ptr<SubscriptionImpl> s = PrivateImplRef<Subscription>::get(i->second);
+ if (s) s->cancelDiversion();
+ }
+ subscriptions.clear();
+}
+
Subscription SubscriptionManagerImpl::subscribe(
MessageListener& listener, const std::string& q, const SubscriptionSettings& ss, const std::string& n)
{
diff --git a/cpp/src/qpid/client/SubscriptionManagerImpl.h b/cpp/src/qpid/client/SubscriptionManagerImpl.h
index 6376a05c45..64d922e387 100644
--- a/cpp/src/qpid/client/SubscriptionManagerImpl.h
+++ b/cpp/src/qpid/client/SubscriptionManagerImpl.h
@@ -99,7 +99,8 @@ class SubscriptionManagerImpl : public sys::Runnable, public RefCounted
public:
/** Create a new SubscriptionManagerImpl associated with a session */
SubscriptionManagerImpl(const Session& session);
-
+ ~SubscriptionManagerImpl();
+
/**
* Subscribe a MessagesListener to receive messages from queue.
*
diff --git a/cpp/src/qpid/client/TCPConnector.cpp b/cpp/src/qpid/client/TCPConnector.cpp
index 4660a41c07..1dd951d339 100644
--- a/cpp/src/qpid/client/TCPConnector.cpp
+++ b/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)
@@ -265,7 +266,7 @@ size_t TCPConnector::encode(const char* buffer, size_t size)
return bytesWritten;
}
-bool TCPConnector::readbuff(AsynchIO& aio, AsynchIO::BufferBase* buff)
+void TCPConnector::readbuff(AsynchIO& aio, AsynchIO::BufferBase* buff)
{
Codec* codec = securityLayer.get() ? (Codec*) securityLayer.get() : (Codec*) this;
int32_t decoded = codec->decode(buff->bytes+buff->dataStart, buff->dataCount);
@@ -280,10 +281,9 @@ bool TCPConnector::readbuff(AsynchIO& aio, AsynchIO::BufferBase* buff)
// Give whole buffer back to aio subsystem
aio.queueReadBuffer(buff);
}
- return true;
}
-size_t TCPConnector::decode(const char* buffer, size_t size)
+size_t TCPConnector::decode(const char* buffer, size_t size)
{
framing::Buffer in(const_cast<char*>(buffer), size);
if (!initiated) {
diff --git a/cpp/src/qpid/client/TCPConnector.h b/cpp/src/qpid/client/TCPConnector.h
index eb3f696013..c87d544816 100644
--- a/cpp/src/qpid/client/TCPConnector.h
+++ b/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;
@@ -102,7 +100,7 @@ protected:
void start(sys::AsynchIO* aio_);
void initAmqp();
virtual void connectFailed(const std::string& msg);
- bool readbuff(qpid::sys::AsynchIO&, qpid::sys::AsynchIOBufferBase*);
+ void readbuff(qpid::sys::AsynchIO&, qpid::sys::AsynchIOBufferBase*);
void writebuff(qpid::sys::AsynchIO&);
void eof(qpid::sys::AsynchIO&);
void disconnected(qpid::sys::AsynchIO&);
diff --git a/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp b/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp
index a8f4fb5237..2627c178f9 100644
--- a/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp
+++ b/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp
@@ -241,6 +241,7 @@ class Subscription : public Exchange, public MessageSource
const std::string actualType;
const bool exclusiveQueue;
const bool exclusiveSubscription;
+ const std::string alternateExchange;
FieldTable queueOptions;
FieldTable subscriptionOptions;
Bindings bindings;
@@ -507,7 +508,8 @@ Subscription::Subscription(const Address& address, const std::string& type)
durable(Opt(address)/LINK/DURABLE),
actualType(type.empty() ? (specifiedType.empty() ? TOPIC_EXCHANGE : specifiedType) : type),
exclusiveQueue((Opt(address)/LINK/X_DECLARE/EXCLUSIVE).asBool(true)),
- exclusiveSubscription((Opt(address)/LINK/X_SUBSCRIBE/EXCLUSIVE).asBool(exclusiveQueue))
+ exclusiveSubscription((Opt(address)/LINK/X_SUBSCRIBE/EXCLUSIVE).asBool(exclusiveQueue)),
+ alternateExchange((Opt(address)/LINK/X_DECLARE/ALTERNATE_EXCHANGE).str())
{
(Opt(address)/LINK/X_DECLARE/ARGUMENTS).collect(queueOptions);
(Opt(address)/LINK/X_SUBSCRIBE/ARGUMENTS).collect(subscriptionOptions);
@@ -568,7 +570,9 @@ void Subscription::subscribe(qpid::client::AsyncSession& session, const std::str
//create subscription queue:
session.queueDeclare(arg::queue=queue, arg::exclusive=exclusiveQueue,
- arg::autoDelete=!reliable, arg::durable=durable, arg::arguments=queueOptions);
+ arg::autoDelete=!reliable, arg::durable=durable,
+ arg::alternateExchange=alternateExchange,
+ arg::arguments=queueOptions);
//'default' binding:
bindings.bind(session);
//any explicit bindings:
diff --git a/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp b/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp
index 2ea4dc0c61..aaebec0720 100644
--- a/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp
+++ b/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/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp b/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp
index d93416da75..dd14d11c4c 100644
--- a/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp
+++ b/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp
@@ -58,7 +58,12 @@ void OutgoingMessage::convert(const qpid::messaging::Message& from)
if (address) {
message.getMessageProperties().setReplyTo(AddressResolution::convert(address));
}
- translate(from.getProperties(), message.getMessageProperties().getApplicationHeaders());
+ if (!subject.empty()) {
+ Variant v(subject); v.setEncoding("utf8");
+ translate(from.getProperties(), SUBJECT, v, message.getMessageProperties().getApplicationHeaders());
+ } else {
+ translate(from.getProperties(), message.getMessageProperties().getApplicationHeaders());
+ }
if (from.getTtl().getMilliseconds()) {
message.getDeliveryProperties().setTtl(from.getTtl().getMilliseconds());
}
@@ -89,16 +94,14 @@ void OutgoingMessage::convert(const qpid::messaging::Message& from)
}
}
-void OutgoingMessage::setSubject(const std::string& subject)
+void OutgoingMessage::setSubject(const std::string& s)
{
- if (!subject.empty()) {
- message.getMessageProperties().getApplicationHeaders().setString(SUBJECT, subject);
- }
+ subject = s;
}
std::string OutgoingMessage::getSubject() const
{
- return message.getMessageProperties().getApplicationHeaders().getAsString(SUBJECT);
+ return subject;
}
}}} // namespace qpid::client::amqp0_10
diff --git a/cpp/src/qpid/client/amqp0_10/OutgoingMessage.h b/cpp/src/qpid/client/amqp0_10/OutgoingMessage.h
index 0cdd2a2336..2191f45546 100644
--- a/cpp/src/qpid/client/amqp0_10/OutgoingMessage.h
+++ b/cpp/src/qpid/client/amqp0_10/OutgoingMessage.h
@@ -35,6 +35,7 @@ struct OutgoingMessage
{
qpid::client::Message message;
qpid::client::Completion status;
+ std::string subject;
void convert(const qpid::messaging::Message&);
void setSubject(const std::string& subject);
diff --git a/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h b/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h
index 5693b7b71f..76da4f31a9 100644
--- a/cpp/src/qpid/client/amqp0_10/ReceiverImpl.h
+++ b/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/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp b/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp
index f2f0f1a9e5..b275db38d7 100644
--- a/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp
+++ b/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp
@@ -37,10 +37,10 @@ SenderImpl::SenderImpl(SessionImpl& _parent, const std::string& _name,
void SenderImpl::send(const qpid::messaging::Message& message, bool sync)
{
if (unreliable) { // immutable, don't need lock
- UnreliableSend f(*this, &message);
+ UnreliableSend f(*this, message);
parent->execute(f);
} else {
- Send f(*this, &message);
+ Send f(*this, message);
while (f.repeat) parent->execute(f);
}
if (sync) parent->sync(true);
@@ -117,8 +117,8 @@ void SenderImpl::sendImpl(const qpid::messaging::Message& m)
{
sys::Mutex::ScopedLock l(lock);
std::auto_ptr<OutgoingMessage> msg(new OutgoingMessage());
- msg->convert(m);
msg->setSubject(m.getSubject().empty() ? address.getSubject() : m.getSubject());
+ msg->convert(m);
outgoing.push_back(msg.release());
sink->send(session, name, outgoing.back());
}
@@ -127,8 +127,8 @@ void SenderImpl::sendUnreliable(const qpid::messaging::Message& m)
{
sys::Mutex::ScopedLock l(lock);
OutgoingMessage msg;
- msg.convert(m);
msg.setSubject(m.getSubject().empty() ? address.getSubject() : m.getSubject());
+ msg.convert(m);
sink->send(session, name, msg);
}
diff --git a/cpp/src/qpid/client/amqp0_10/SenderImpl.h b/cpp/src/qpid/client/amqp0_10/SenderImpl.h
index c10c77ae18..d75863c743 100644
--- a/cpp/src/qpid/client/amqp0_10/SenderImpl.h
+++ b/cpp/src/qpid/client/amqp0_10/SenderImpl.h
@@ -99,32 +99,32 @@ class SenderImpl : public qpid::messaging::SenderImpl
struct Send : Command
{
- const qpid::messaging::Message* message;
+ const qpid::messaging::Message& message;
bool repeat;
- Send(SenderImpl& i, const qpid::messaging::Message* m) : Command(i), message(m), repeat(true) {}
+ Send(SenderImpl& i, const qpid::messaging::Message& m) : Command(i), message(m), repeat(true) {}
void operator()()
{
impl.waitForCapacity();
//from this point message will be recorded if there is any
//failure (and replayed) so need not repeat the call
repeat = false;
- impl.sendImpl(*message);
+ impl.sendImpl(message);
}
};
struct UnreliableSend : Command
{
- const qpid::messaging::Message* message;
+ const qpid::messaging::Message& message;
- UnreliableSend(SenderImpl& i, const qpid::messaging::Message* m) : Command(i), message(m) {}
+ UnreliableSend(SenderImpl& i, const qpid::messaging::Message& m) : Command(i), message(m) {}
void operator()()
{
//TODO: ideally want to put messages on the outbound
//queue and pull them off in io thread, but the old
//0-10 client doesn't support that option so for now
//we simply don't queue unreliable messages
- impl.sendUnreliable(*message);
+ impl.sendUnreliable(message);
}
};
diff --git a/cpp/src/qpid/client/windows/SslConnector.cpp b/cpp/src/qpid/client/windows/SslConnector.cpp
index 785c817928..2aa31e8202 100644
--- a/cpp/src/qpid/client/windows/SslConnector.cpp
+++ b/cpp/src/qpid/client/windows/SslConnector.cpp
@@ -68,7 +68,7 @@ class SslConnector : public qpid::client::TCPConnector
// A number of AsynchIO callbacks go right through to TCPConnector, but
// we can't boost::bind to a protected ancestor, so these methods redirect
// to those TCPConnector methods.
- bool redirectReadbuff(qpid::sys::AsynchIO&, qpid::sys::AsynchIOBufferBase*);
+ void redirectReadbuff(qpid::sys::AsynchIO&, qpid::sys::AsynchIOBufferBase*);
void redirectWritebuff(qpid::sys::AsynchIO&);
void redirectEof(qpid::sys::AsynchIO&);
@@ -111,9 +111,9 @@ void SslConnector::negotiationDone(SECURITY_STATUS status)
connectFailed(QPID_MSG(qpid::sys::strError(status)));
}
-bool SslConnector::redirectReadbuff(qpid::sys::AsynchIO& a,
+void SslConnector::redirectReadbuff(qpid::sys::AsynchIO& a,
qpid::sys::AsynchIOBufferBase* b) {
- return readbuff(a, b);
+ readbuff(a, b);
}
void SslConnector::redirectWritebuff(qpid::sys::AsynchIO& a) {
diff --git a/cpp/src/qpid/cluster/Connection.cpp b/cpp/src/qpid/cluster/Connection.cpp
index 512e0f03cb..ff855eef18 100644
--- a/cpp/src/qpid/cluster/Connection.cpp
+++ b/cpp/src/qpid/cluster/Connection.cpp
@@ -58,6 +58,8 @@
namespace qpid {
namespace cluster {
+using std::string;
+
using namespace framing;
using namespace framing::cluster;
using amqp_0_10::ListCodec;
@@ -83,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()),
@@ -100,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()),
@@ -272,6 +277,8 @@ void Connection::closed() {
if (announced)
cluster.getMulticast().mcastControl(
ClusterConnectionDeliverCloseBody(), self);
+ else
+ close();
}
}
catch (const std::exception& e) {
@@ -404,11 +411,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);
@@ -522,6 +530,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;
}
@@ -782,16 +791,18 @@ 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));
diff --git a/cpp/src/qpid/cluster/Connection.h b/cpp/src/qpid/cluster/Connection.h
index 26514c76e2..b0e7b3bd9e 100644
--- a/cpp/src/qpid/cluster/Connection.h
+++ b/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.
//
@@ -228,6 +228,7 @@ class Connection :
uint64_t objectId;
bool shadow;
bool delayManagement;
+ bool authenticated;
ConnectionCtor(
sys::ConnectionOutputHandler* out_,
@@ -237,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/cpp/src/qpid/cluster/ConnectionCodec.cpp b/cpp/src/qpid/cluster/ConnectionCodec.cpp
index d0ba8abfb3..54327fbfe2 100644
--- a/cpp/src/qpid/cluster/ConnectionCodec.cpp
+++ b/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/cpp/src/qpid/cluster/Cpg.cpp b/cpp/src/qpid/cluster/Cpg.cpp
index 0856bcd824..6e9e22a42f 100644
--- a/cpp/src/qpid/cluster/Cpg.cpp
+++ b/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/cpp/src/qpid/cluster/Cpg.h b/cpp/src/qpid/cluster/Cpg.h
index 6b81c602bd..1afbce8d75 100644
--- a/cpp/src/qpid/cluster/Cpg.h
+++ b/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/cpp/src/qpid/cluster/UpdateClient.cpp b/cpp/src/qpid/cluster/UpdateClient.cpp
index 20684fd8a7..8737418570 100644
--- a/cpp/src/qpid/cluster/UpdateClient.cpp
+++ b/cpp/src/qpid/cluster/UpdateClient.cpp
@@ -74,6 +74,8 @@
namespace qpid {
namespace cluster {
+using std::string;
+
using amqp_0_10::ListCodec;
using broker::Broker;
using broker::Exchange;
@@ -87,6 +89,8 @@ using namespace framing;
namespace arg=client::arg;
using client::SessionBase_0_10Access;
+namespace _qmf = qmf::org::apache::qpid::broker;
+
// Reserved exchange/queue name for catch-up, avoid clashes with user queues/exchanges.
const std::string UpdateClient::UPDATE("x-qpid.cluster-update");
// Name for header used to carry expiration information.
@@ -226,14 +230,6 @@ template <class T> std::string encode(const T& t) {
t.encode(buf);
return encoded;
}
-
-template <class T> std::string encode(const T& t, bool encodeKind) {
- std::string encoded;
- encoded.resize(t.encodedSize());
- framing::Buffer buf(const_cast<char*>(encoded.data()), encoded.size());
- t.encode(buf, encodeKind);
- return encoded;
-}
} // namespace
@@ -377,13 +373,14 @@ class MessageUpdater {
void UpdateClient::updateQueue(client::AsyncSession& s, const boost::shared_ptr<Queue>& q) {
broker::Exchange::shared_ptr alternateExchange = q->getAlternateExchange();
+ _qmf::Queue* mgmtQueue = dynamic_cast<_qmf::Queue*>(q->GetManagementObject());
s.queueDeclare(
arg::queue = q->getName(),
arg::durable = q->isDurable(),
arg::autoDelete = q->isAutoDelete(),
arg::alternateExchange = alternateExchange ? alternateExchange->getName() : "",
arg::arguments = q->getSettings(),
- arg::exclusive = q->hasExclusiveOwner()
+ arg::exclusive = mgmtQueue && mgmtQueue->get_exclusive()
);
MessageUpdater updater(q->getName(), s, expiry);
q->eachMessage(boost::bind(&MessageUpdater::updateQueuedMessage, &updater, _1));
@@ -545,7 +542,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());
diff --git a/cpp/src/qpid/cluster/types.h b/cpp/src/qpid/cluster/types.h
index bfb4fd5b9e..c8ffb0b804 100644
--- a/cpp/src/qpid/cluster/types.h
+++ b/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/cpp/src/qpid/console/ClassKey.cpp b/cpp/src/qpid/console/ClassKey.cpp
index 7a16113bae..d4b59fc413 100644
--- a/cpp/src/qpid/console/ClassKey.cpp
+++ b/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/cpp/src/qpid/framing/AMQCommandControlBody.h b/cpp/src/qpid/framing/AMQCommandControlBody.h
deleted file mode 100644
index d12b70a168..0000000000
--- a/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/cpp/src/qpid/framing/AMQContentBody.cpp b/cpp/src/qpid/framing/AMQContentBody.cpp
index 72f7d9978e..18f6994f8f 100644
--- a/cpp/src/qpid/framing/AMQContentBody.cpp
+++ b/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/cpp/src/qpid/framing/AMQContentBody.h b/cpp/src/qpid/framing/AMQContentBody.h
index e25451e354..148b293a2f 100644
--- a/cpp/src/qpid/framing/AMQContentBody.h
+++ b/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/cpp/src/qpid/framing/AMQFrame.cpp b/cpp/src/qpid/framing/AMQFrame.cpp
index 5b9673f0d0..5e065d598c 100644
--- a/cpp/src/qpid/framing/AMQFrame.cpp
+++ b/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/cpp/src/qpid/framing/Buffer.cpp b/cpp/src/qpid/framing/Buffer.cpp
index 5a5bc0325e..b71915aeb7 100644
--- a/cpp/src/qpid/framing/Buffer.cpp
+++ b/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/cpp/src/qpid/framing/FieldTable.cpp b/cpp/src/qpid/framing/FieldTable.cpp
index 0f7140b627..cd38db1ee8 100644
--- a/cpp/src/qpid/framing/FieldTable.cpp
+++ b/cpp/src/qpid/framing/FieldTable.cpp
@@ -196,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();
diff --git a/cpp/src/qpid/framing/ProtocolInitiation.cpp b/cpp/src/qpid/framing/ProtocolInitiation.cpp
index e617015d64..00ddb55a3b 100644
--- a/cpp/src/qpid/framing/ProtocolInitiation.cpp
+++ b/cpp/src/qpid/framing/ProtocolInitiation.cpp
@@ -20,6 +20,8 @@
*/
#include "qpid/framing/ProtocolInitiation.h"
+#include <iostream>
+
namespace qpid {
namespace framing {
diff --git a/cpp/src/qpid/framing/Uuid.cpp b/cpp/src/qpid/framing/Uuid.cpp
index b3d1e2e1e4..e377c1172d 100644
--- a/cpp/src/qpid/framing/Uuid.cpp
+++ b/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/cpp/src/qpid/ha/AlternateExchangeSetter.h b/cpp/src/qpid/ha/AlternateExchangeSetter.h
new file mode 100644
index 0000000000..08690e68bc
--- /dev/null
+++ b/cpp/src/qpid/ha/AlternateExchangeSetter.h
@@ -0,0 +1,73 @@
+#ifndef QPID_HA_ALTERNATEEXCHANGESETTER_H
+#define QPID_HA_ALTERNATEEXCHANGESETTER_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/log/Statement.h"
+#include "qpid/broker/Exchange.h"
+#include "qpid/broker/ExchangeRegistry.h"
+#include "boost/function.hpp"
+#include <map>
+
+namespace qpid {
+namespace ha {
+
+/**
+ * Sets the alternate exchange on queues and exchanges.
+ * Holds onto queues/exchanges if necessary till the alternate exchange is available.
+ * THREAD UNSAFE
+ */
+class AlternateExchangeSetter
+{
+ public:
+ typedef boost::function<void(boost::shared_ptr<broker::Exchange>)> SetFunction;
+
+ AlternateExchangeSetter(broker::ExchangeRegistry& er) : exchanges(er) {}
+
+ void setAlternate(const std::string& altEx, const SetFunction& setter) {
+ broker::Exchange::shared_ptr ex = exchanges.find(altEx);
+ if (ex) setter(ex); // Set immediately.
+ else setters.insert(Setters::value_type(altEx, setter)); // Save for later.
+ }
+
+ void addExchange(boost::shared_ptr<broker::Exchange> exchange) {
+ // Update the setters for this exchange
+ std::pair<Setters::iterator, Setters::iterator> range = setters.equal_range(exchange->getName());
+ for (Setters::iterator i = range.first; i != range.second; ++i)
+ i->second(exchange);
+ setters.erase(range.first, range.second);
+ }
+
+ void clear() {
+ if (!setters.empty())
+ QPID_LOG(warning, "Some alternate exchanges were not resolved.");
+ setters.clear();
+ }
+
+ private:
+ typedef std::multimap<std::string, SetFunction> Setters;
+ broker::ExchangeRegistry& exchanges;
+ Setters setters;
+};
+}} // namespace qpid::ha
+
+#endif /*!QPID_HA_ALTERNATEEXCHANGESETTER_H*/
diff --git a/cpp/src/qpid/ha/Backup.cpp b/cpp/src/qpid/ha/Backup.cpp
index 3f3fa87a01..bac6fd23c8 100644
--- a/cpp/src/qpid/ha/Backup.cpp
+++ b/cpp/src/qpid/ha/Backup.cpp
@@ -20,7 +20,6 @@
*/
#include "Backup.h"
#include "BrokerReplicator.h"
-#include "ConnectionExcluder.h"
#include "HaBroker.h"
#include "ReplicatingSubscription.h"
#include "Settings.h"
@@ -34,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 {
@@ -45,47 +45,85 @@ using types::Variant;
using std::string;
Backup::Backup(HaBroker& hb, const Settings& s) :
- haBroker(hb), broker(hb.getBroker()), settings(s), excluder(new ConnectionExcluder())
+ logPrefix("Backup: "), haBroker(hb), broker(hb.getBroker()), settings(s)
{
- // Exclude client connections before starting the link to avoid self-connection.
- broker.getConnectionObservers().add(excluder);
- // 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) {
- if (url.empty()) throw Url::Invalid("HA broker URL is empty");
- QPID_LOG(notice, "HA: Backup initialized: " << 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 << "Connecting to cluster, 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);
- link = result.first;
- link->setUrl(url);
- replicator.reset(new BrokerReplicator(haBroker, link));
- broker.getExchanges().registerExchange(replicator);
+ false, // durable
+ settings.mechanism, settings.username, settings.password,
+ false); // no amq.failover - don't want to use client URL.
+ {
+ sys::Mutex::ScopedLock l(lock);
+ link = result.first;
+ replicator.reset(new BrokerReplicator(haBroker, link));
+ replicator->initialize();
+ broker.getExchanges().registerExchange(replicator);
+ }
+ link->setUrl(url); // Outside the lock, once set link doesn't change.
}
Backup::~Backup() {
if (link) link->close();
if (replicator.get()) broker.getExchanges().destroy(replicator->getName());
- replicator.reset();
- broker.getConnectionObservers().remove(excluder); // This allows client connections.
}
-
+// Called via management.
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 broker URL set to " << url);
- link->setUrl(url);
+ bool linkSet = false;
+ {
+ sys::Mutex::ScopedLock l(lock);
+ linkSet = link;
+ }
+ if (linkSet) {
+ QPID_LOG(info, logPrefix << "Broker URL set to: " << url);
+ link->setUrl(removeSelf(url)); // Outside lock, once set link doesn't change
}
- else {
+ else
initialize(url); // Deferred initialization
+}
+
+void Backup::setStatus(BrokerStatus status) {
+ switch (status) {
+ case READY:
+ QPID_LOG(notice, logPrefix << "Ready to become primary.");
+ break;
+ case CATCHUP:
+ QPID_LOG(notice, logPrefix << "Catching up on primary, cannot be promoted.");
+ default:
+ assert(0);
}
}
diff --git a/cpp/src/qpid/ha/Backup.h b/cpp/src/qpid/ha/Backup.h
index 6c36996914..1233a473ec 100644
--- a/cpp/src/qpid/ha/Backup.h
+++ b/cpp/src/qpid/ha/Backup.h
@@ -36,7 +36,6 @@ class Link;
namespace ha {
class Settings;
-class ConnectionExcluder;
class BrokerReplicator;
class HaBroker;
@@ -51,9 +50,13 @@ class Backup
Backup(HaBroker&, const Settings&);
~Backup();
void setBrokerUrl(const Url&);
+ void setStatus(BrokerStatus);
private:
+ bool isSelf(const Address& a) const;
+ Url removeSelf(const Url&) const;
void initialize(const Url&);
+ std::string logPrefix;
sys::Mutex lock;
HaBroker& haBroker;
@@ -61,7 +64,6 @@ class Backup
Settings settings;
boost::shared_ptr<broker::Link> link;
boost::shared_ptr<BrokerReplicator> replicator;
- boost::shared_ptr<ConnectionExcluder> excluder;
};
}} // namespace qpid::ha
diff --git a/cpp/src/qpid/ha/ConnectionExcluder.h b/cpp/src/qpid/ha/BackupConnectionExcluder.h
index f8f2843a0c..ef537ab90a 100644
--- a/cpp/src/qpid/ha/ConnectionExcluder.h
+++ b/cpp/src/qpid/ha/BackupConnectionExcluder.h
@@ -1,5 +1,5 @@
-#ifndef QPID_HA_CONNECTIONEXCLUDER_H
-#define QPID_HA_CONNECTIONEXCLUDER_H
+#ifndef QPID_HA_BACKUPCONNECTIONEXCLUDER_H
+#define QPID_HA_BACKUPCONNECTIONEXCLUDER_H
/*
*
@@ -23,32 +23,26 @@
*/
#include "qpid/broker/ConnectionObserver.h"
-#include <boost/function.hpp>
+#include "qpid/broker/Connection.h"
+#include "qpid/log/Statement.h"
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.
+ * Exclude connections to a backup broker.
*/
-class ConnectionExcluder : public broker::ConnectionObserver
+class BackupConnectionExcluder : public broker::ConnectionObserver
{
public:
- ConnectionExcluder();
-
- void opened(broker::Connection& connection);
+ void opened(broker::Connection& connection) {
+ QPID_LOG(debug, "Backup broker rejected connection "+connection.getMgmtId());
+ connection.abort();
+ }
- private:
- static const std::string ADMIN_TAG;
+ void closed(broker::Connection&) {}
};
}} // namespace qpid::ha
-#endif /*!QPID_HA_CONNECTIONEXCLUDER_H*/
+#endif /*!QPID_HA_BACKUPCONNECTIONEXCLUDER_H*/
diff --git a/cpp/src/qpid/ha/BrokerInfo.cpp b/cpp/src/qpid/ha/BrokerInfo.cpp
new file mode 100644
index 0000000000..c8bd1a14be
--- /dev/null
+++ b/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/cpp/src/qpid/ha/BrokerInfo.h b/cpp/src/qpid/ha/BrokerInfo.h
new file mode 100644
index 0000000000..642f7c1361
--- /dev/null
+++ b/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/cpp/src/qpid/ha/BrokerReplicator.cpp b/cpp/src/qpid/ha/BrokerReplicator.cpp
index d0c99cbdb6..c8c4a42d72 100644
--- a/cpp/src/qpid/ha/BrokerReplicator.cpp
+++ b/cpp/src/qpid/ha/BrokerReplicator.cpp
@@ -22,6 +22,7 @@
#include "HaBroker.h"
#include "QueueReplicator.h"
#include "qpid/broker/Broker.h"
+#include "qpid/broker/Connection.h"
#include "qpid/broker/Queue.h"
#include "qpid/broker/Link.h"
#include "qpid/framing/FieldTable.h"
@@ -37,8 +38,11 @@
#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 <iostream>
+#include <assert.h>
namespace qpid {
namespace ha {
@@ -50,15 +54,16 @@ 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 std::ostream;
using types::Variant;
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");
@@ -69,10 +74,13 @@ const string SCHEMA_ID("_schema_id");
const string VALUES("_values");
const string ALTEX("altEx");
+const string ALTEXCHANGE("altExchange");
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");
@@ -86,12 +94,12 @@ const string KEY("key");
const string NAME("name");
const string QNAME("qName");
const string QUEUE("queue");
-const string RHOST("rhost");
const string TYPE("type");
-const string USER("user");
const string HA_BROKER("habroker");
+const string PARTIAL("partial");
-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");
@@ -107,6 +115,7 @@ 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>();
@@ -117,7 +126,9 @@ template <class T> bool match(Variant::Map& schema) {
return T::match(schema[CLASS_NAME], schema[PACKAGE_NAME]);
}
-void sendQuery(const string& packageName, 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;
@@ -137,6 +148,7 @@ void sendQuery(const string& packageName, const string& className, const string&
props->setAppId(QMF2);
props->getApplicationHeaders().setString(QMF_OPCODE, _QUERY_REQUEST);
headerBody.get<qpid::framing::DeliveryProperties>(true)->setRoutingKey(BROKER);
+ headerBody.get<qpid::framing::MessageProperties>(true)->setCorrelationId(className);
AMQFrame header(headerBody);
header.setBof(false);
header.setEof(false);
@@ -159,39 +171,23 @@ Variant::Map asMapVoid(const Variant& value) {
if (!value.isVoid()) return value.asMap();
else return Variant::Map();
}
-
} // namespace
-
-ReplicateLevel BrokerReplicator::replicateLevel(const std::string& str) {
- ReplicateLevel rl;
- if (qpid::ha::replicateLevel(str, rl)) return rl;
- else return haBroker.getSettings().replicateDefault;
-}
-
-ReplicateLevel BrokerReplicator::replicateLevel(const framing::FieldTable& f) {
- if (f.isSet(QPID_REPLICATE))
- return replicateLevel(f.getAsString(QPID_REPLICATE));
- else
- return haBroker.getSettings().replicateDefault;
-}
-
-ReplicateLevel BrokerReplicator::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 haBroker.getSettings().replicateDefault;
-}
-
-BrokerReplicator::~BrokerReplicator() {}
-
BrokerReplicator::BrokerReplicator(HaBroker& hb, const boost::shared_ptr<Link>& l)
: Exchange(QPID_CONFIGURATION_REPLICATOR),
- haBroker(hb), broker(hb.getBroker()), link(l)
-{
+ logPrefix("Backup: "), replicationTest(hb.getReplicationTest()),
+ haBroker(hb), broker(hb.getBroker()), link(l),
+ initialized(false),
+ alternates(hb.getBroker().getExchanges())
+{}
+
+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
@@ -202,21 +198,41 @@ BrokerReplicator::BrokerReplicator(HaBroker& hb, const boost::shared_ptr<Link>&
"", // 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);
+ // Use the credentials of the outgoing Link connection for creating queues,
+ // exchanges etc. We know link->getConnection() is non-zero because we are
+ // being called in the connections thread context.
+ //
+ assert(link->getConnection());
+ userId = link->getConnection()->getUserId();
+ remoteHost = link->getConnection()->getUrl();
+
+ link->getRemoteAddress(primary);
string queueName = bridge.getQueueName();
+
+ QPID_LOG(info, logPrefix << (initialized ? "Connecting" : "Failing over")
+ << " to primary " << primary
+ << " status:" << printable(haBroker.getStatus()));
+ initialized = true;
+
+ framing::AMQP_ServerProxy peer(sessionHandler.out);
const qmf::org::apache::qpid::broker::ArgsLinkBridge& args(bridge.getArgs());
//declare and bind an event queue
FieldTable declareArgs;
- declareArgs.setString(QPID_REPLICATE, str(RL_NONE));
+ declareArgs.setString(QPID_REPLICATE, printable(NONE).str());
peer.getQueue().declare(queueName, "", false, false, true, true, declareArgs);
- peer.getExchange().bind(queueName, QMF_DEFAULT_TOPIC, AGENT_IND_EVENT_ORG_APACHE_QPID_BROKER, FieldTable());
+ 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);
@@ -228,23 +244,30 @@ void BrokerReplicator::initializeBridge(Bridge& bridge, SessionHandler& sessionH
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, "HA: Backup configuration bridge: " << queueName);
}
void BrokerReplicator::route(Deliverable& msg) {
+ // We transition from JOINING->CATCHUP on the first message received from the primary.
+ // Until now we couldn't be sure if we had a good connection to the primary.
+ if (haBroker.getStatus() == JOINING) {
+ haBroker.setStatus(CATCHUP);
+ QPID_LOG(notice, logPrefix << "Connected to primary " << primary);
+ }
+
const framing::FieldTable* headers = msg.getMessage().getApplicationHeaders();
+ const MessageProperties* messageProperties = msg.getMessage().getProperties<MessageProperties>();
Variant::List list;
try {
- if (!isQMFv2(msg.getMessage()) || !headers)
+ if (!isQMFv2(msg.getMessage()) || !headers || !messageProperties)
throw Exception("Unexpected message, not QMF2 event or query response.");
// decode as list
string content = msg.getMessage().getFrames().getContent();
amqp_0_10::ListCodec::decode(content, list);
-
+ QPID_LOG(trace, "Broker replicator received: " << *messageProperties);
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, "HA: Backup received event: " << map);
+ 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);
@@ -253,12 +276,13 @@ 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) {
Variant::Map& map = i->asMap();
- QPID_LOG(trace, "HA: Backup received event: " << map);
- string type = map[SCHEMA_ID].asMap()[CLASS_NAME];
+ 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);
@@ -267,85 +291,82 @@ void BrokerReplicator::route(Deliverable& msg) {
else if (type == BINDING) doResponseBind(values);
else if (type == HA_BROKER) doResponseHaBroker(values);
}
+ if (messageProperties->getCorrelationId() == EXCHANGE && !headers->isSet(PARTIAL)) {
+ // We have received all of the exchange response.
+ alternates.clear();
+ }
}
} catch (const std::exception& e) {
- QPID_LOG(critical, "HA: Backup configuration failed: " << e.what()
+ QPID_LOG(critical, logPrefix << "Configuration failed: " << e.what()
<< ": while handling: " << list);
+ haBroker.shutdown();
throw;
}
}
+
void BrokerReplicator::doEventQueueDeclare(Variant::Map& 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);
- 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*/,
- values[ALTEX].asString(),
- args,
- values[USER].asString(),
- values[RHOST].asString());
- if (result.second) {
- QPID_LOG(debug, "HA: Backup queue declare event: " << name);
- startQueueReplicator(result.first);
- } else {
- // FIXME aconway 2011-12-02: what's the right way to handle this?
- // Should we delete the old & re-create form the event? Responses
- // may be old but events are always up-to-date.
- QPID_LOG(warning, "HA: Backup queue declare event, already exists: " << name);
+ // 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);
+ stopQueueReplicator(name);
}
+ boost::shared_ptr<Queue> queue = createQueue(
+ name, values[DURABLE].asBool(), autoDel, args, values[ALTEX].asString());
+ assert(queue); // Should be created since we destroed the previous queue above.
+ if (queue) startQueueReplicator(queue);
}
}
+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) {
// 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) {
- QPID_LOG(warning, "HA: Backup queue delete event, does not exist: " << name);
- } else if (!replicateLevel(queue->getSettings())) {
- QPID_LOG(warning, "HA: Backup queue delete event, not replicated: " << name);
- } else {
- 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);
- broker.deleteQueue(name, values[USER].asString(), values[RHOST].asString());
- QPID_LOG(debug, "HA: Backup queue delete event: " << name);
+ if (queue && replicationTest.replicateLevel(queue->getSettings())) {
+ QPID_LOG(debug, logPrefix << "Queue delete event: " << name);
+ stopQueueReplicator(name);
+ broker.deleteQueue(name, userId, remoteHost);
}
}
void BrokerReplicator::doEventExchangeDeclare(Variant::Map& 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(
- name,
- values[EXTYPE].asString(),
- values[DURABLE].asBool(),
- values[ALTEX].asString(),
- args,
- values[USER].asString(),
- values[RHOST].asString()).second)
- {
- QPID_LOG(debug, "HA: Backup exchange declare event: " << name);
- } else {
- // FIXME aconway 2011-11-22: should delete pre-existing exchange
- // and re-create from event. See comment in doEventQueueDeclare.
- QPID_LOG(debug, "HA: Backup exchange declare event, already exists: " << name);
+ // 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);
}
+ boost::shared_ptr<Exchange> exchange =
+ createExchange(name, values[EXTYPE].asString(), values[DURABLE].asBool(), args, values[ALTEX].asString());
+ assert(exchange);
}
}
@@ -353,15 +374,12 @@ void BrokerReplicator::doEventExchangeDelete(Variant::Map& values) {
string name = values[EXNAME].asString();
boost::shared_ptr<Exchange> exchange = broker.getExchanges().find(name);
if (!exchange) {
- QPID_LOG(warning, "HA: Backup exchange delete event, does not exist: " << name);
- } else if (!replicateLevel(exchange->getArgs())) {
- QPID_LOG(warning, "HA: Backup exchange delete event, not replicated: " << name);
+ 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, "HA: Backup exchange delete event:" << name);
- broker.deleteExchange(
- name,
- values[USER].asString(),
- values[RHOST].asString());
+ QPID_LOG(debug, logPrefix << "Exchange delete event:" << name);
+ broker.deleteExchange(name, userId, remoteHost);
}
}
@@ -372,16 +390,16 @@ void BrokerReplicator::doEventBind(Variant::Map& values) {
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();
- exchange->bind(queue, key, &args);
- QPID_LOG(debug, "HA: Backup bind event: exchange=" << exchange->getName()
+ QPID_LOG(debug, logPrefix << "Bind event: exchange=" << exchange->getName()
<< " queue=" << queue->getName()
<< " key=" << key);
+ exchange->bind(queue, key, &args);
}
}
@@ -392,64 +410,71 @@ void BrokerReplicator::doEventUnbind(Variant::Map& values) {
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();
- exchange->unbind(queue, key, &args);
- QPID_LOG(debug, "HA: Backup unbind event: 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();
+ haBroker.setMembership(members);
+}
+
+namespace {
+
+// Get the alternate exchange from the exchange field of a queue or exchange response.
+static const string EXCHANGE_KEY_PREFIX("org.apache.qpid.broker:exchange:");
+
+string getAltExchange(const types::Variant& var) {
+ if (!var.isVoid()) {
+ management::ObjectId oid(var);
+ string key = oid.getV2Key();
+ if (key.find(EXCHANGE_KEY_PREFIX) != 0) throw Exception("Invalid exchange reference: "+key);
+ return key.substr(EXCHANGE_KEY_PREFIX.size());
+ }
+ else return string();
+}
+}
+
void BrokerReplicator::doResponseQueue(Variant::Map& values) {
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,
- values[DURABLE].asBool(),
- values[AUTODELETE].asBool(),
- 0 /*i.e. no owner regardless of exclusivity on master*/,
- ""/*TODO: need to include alternate-exchange*/,
- args,
- ""/*TODO: who is the user?*/,
- ""/*TODO: what should we use as connection id?*/);
- if (result.second) {
- QPID_LOG(debug, "HA: Backup queue response: " << 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 queue response, already exists: " << name);
- }
+ boost::shared_ptr<Queue> queue =
+ createQueue(name, values[DURABLE].asBool(), values[AUTODELETE].asBool(), args,
+ getAltExchange(values[ALTEXCHANGE]));
+ // It is normal for the queue to already exist if we are failing over.
+ if (queue) startQueueReplicator(queue);
+ else QPID_LOG(debug, logPrefix << "Queue already replicated: " << name);
}
void BrokerReplicator::doResponseExchange(Variant::Map& 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 exchange response: " << values[NAME].asString());
- } else {
- QPID_LOG(warning, "HA: Backup exchange query, already exists: " <<
- values[QNAME].asString());
- }
+ boost::shared_ptr<Exchange> exchange = createExchange(
+ name, values[TYPE].asString(), values[DURABLE].asBool(), args,
+ getAltExchange(values[ALTEXCHANGE]));
+ QPID_LOG_IF(debug, !exchange, logPrefix << "Exchange already exists: " << name);
}
namespace {
@@ -480,16 +505,16 @@ void BrokerReplicator::doResponseBind(Variant::Map& values) {
boost::shared_ptr<Queue> queue = broker.getQueues().find(qName);
// 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 bind response: exchange=" << exchange->getName()
- << " queue=" << queue->getName()
- << " key=" << key);
}
}
@@ -500,30 +525,97 @@ const string REPLICATE_DEFAULT="replicateDefault";
// Received the ha-broker configuration object for the primary broker.
void BrokerReplicator::doResponseHaBroker(Variant::Map& values) {
try {
- ReplicateLevel mine = haBroker.getSettings().replicateDefault;
- ReplicateLevel primary = replicateLevel(values[REPLICATE_DEFAULT].asString());
- if (mine != primary) {
- std::ostringstream os;
- os << "Replicate default on backup (" << mine
- << ") does not match primary (" << primary << ")";
- haBroker.shutdown(os.str());
- }
+ QPID_LOG(trace, 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) {
- std::ostringstream os;
- os << "Received invalid replicate default from primary: " << e.what();
- haBroker.shutdown(os.str());
+ QPID_LOG(critical, logPrefix << "Invalid HA Broker response: " << e.what()
+ << ": " << values);
+ haBroker.shutdown();
+ throw;
}
}
-void BrokerReplicator::startQueueReplicator(const boost::shared_ptr<Queue>& queue) {
- if (replicateLevel(queue->getSettings()) == RL_ALL) {
- boost::shared_ptr<QueueReplicator> qr(new QueueReplicator(queue, link));
+void BrokerReplicator::startQueueReplicator(const boost::shared_ptr<Queue>& queue)
+{
+ if (replicationTest.replicateLevel(queue->getSettings()) == ALL) {
+ boost::shared_ptr<QueueReplicator> qr(
+ new QueueReplicator(haBroker, queue, link));
if (!broker.getExchanges().registerExchange(qr))
throw Exception(QPID_MSG("Duplicate queue replicator " << qr->getName()));
qr->activate();
}
}
+void BrokerReplicator::stopQueueReplicator(const std::string& 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());
+ }
+}
+
+boost::shared_ptr<Queue> BrokerReplicator::createQueue(
+ const std::string& name,
+ bool durable,
+ bool autodelete,
+ const qpid::framing::FieldTable& arguments,
+ const std::string& alternateExchange)
+{
+ std::pair<boost::shared_ptr<Queue>, bool> result =
+ broker.createQueue(
+ name,
+ durable,
+ autodelete,
+ 0, // no owner regardless of exclusivity on primary
+ string(), // Set alternate exchange below
+ arguments,
+ userId,
+ remoteHost);
+ if (result.second) {
+ if (!alternateExchange.empty()) {
+ alternates.setAlternate(
+ alternateExchange, boost::bind(&Queue::setAlternateExchange, result.first, _1));
+ }
+ return result.first;
+ }
+ else return boost::shared_ptr<Queue>();
+}
+
+boost::shared_ptr<Exchange> BrokerReplicator::createExchange(
+ const std::string& name,
+ const std::string& type,
+ bool durable,
+ const qpid::framing::FieldTable& args,
+ const std::string& alternateExchange)
+{
+ std::pair<boost::shared_ptr<Exchange>, bool> result =
+ broker.createExchange(
+ name,
+ type,
+ durable,
+ string(), // Set alternate exchange below
+ args,
+ userId,
+ remoteHost);
+ if (result.second) {
+ alternates.addExchange(result.first);
+ if (!alternateExchange.empty()) {
+ alternates.setAlternate(
+ alternateExchange, boost::bind(&Exchange::setAlternate, result.first, _1));
+ }
+ return result.first;
+ }
+ else return boost::shared_ptr<Exchange>();
+}
+
bool BrokerReplicator::bind(boost::shared_ptr<Queue>, const string&, const framing::FieldTable*) { return false; }
bool BrokerReplicator::unbind(boost::shared_ptr<Queue>, const string&, const framing::FieldTable*) { return false; }
bool BrokerReplicator::isBound(boost::shared_ptr<Queue>, const string* const, const framing::FieldTable* const) { return false; }
diff --git a/cpp/src/qpid/ha/BrokerReplicator.h b/cpp/src/qpid/ha/BrokerReplicator.h
index c9d7b9f74c..69653b876a 100644
--- a/cpp/src/qpid/ha/BrokerReplicator.h
+++ b/cpp/src/qpid/ha/BrokerReplicator.h
@@ -22,10 +22,15 @@
*
*/
-#include "ReplicateLevel.h"
+#include "types.h"
+#include "ReplicationTest.h"
+#include "AlternateExchangeSetter.h"
+#include "qpid/Address.h"
#include "qpid/broker/Exchange.h"
#include "qpid/types/Variant.h"
+#include "qpid/management/ManagementObject.h"
#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
namespace qpid {
@@ -42,6 +47,7 @@ class FieldTable;
namespace ha {
class HaBroker;
+class QueueReplicator;
/**
* Replicate configuration on a backup broker.
@@ -51,28 +57,29 @@ class HaBroker;
* exchanges and bindings to replicate the primary.
* It also creates QueueReplicators for newly replicated queues.
*
- * THREAD SAFE: Has no mutable state.
+ * THREAD UNSAFE: Only called in Link connection thread, no need for locking.
*
*/
-class BrokerReplicator : public broker::Exchange
+class BrokerReplicator : public broker::Exchange,
+ public boost::enable_shared_from_this<BrokerReplicator>
{
public:
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:
- void initializeBridge(broker::Bridge&, broker::SessionHandler&);
+ typedef boost::shared_ptr<QueueReplicator> QueueReplicatorPtr;
- ReplicateLevel replicateLevel(const std::string&);
- ReplicateLevel replicateLevel(const framing::FieldTable& args);
- ReplicateLevel replicateLevel(const types::Variant::Map& args);
+ void initializeBridge(broker::Bridge&, broker::SessionHandler&);
void doEventQueueDeclare(types::Variant::Map& values);
void doEventQueueDelete(types::Variant::Map& values);
@@ -80,17 +87,40 @@ 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>&);
+ void stopQueueReplicator(const std::string& name);
+
+ boost::shared_ptr<broker::Queue> createQueue(
+ const std::string& name,
+ bool durable,
+ bool autodelete,
+ const qpid::framing::FieldTable& arguments,
+ const std::string& alternateExchange);
+
+ boost::shared_ptr<broker::Exchange> createExchange(
+ const std::string& name,
+ const std::string& type,
+ bool durable,
+ const qpid::framing::FieldTable& args,
+ const std::string& alternateExchange);
+ std::string logPrefix;
+ std::string userId, remoteHost;
+ ReplicationTest replicationTest;
HaBroker& haBroker;
broker::Broker& broker;
boost::shared_ptr<broker::Link> link;
+ bool initialized;
+ AlternateExchangeSetter alternates;
+ qpid::Address primary;
};
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/ha/ConnectionExcluder.cpp b/cpp/src/qpid/ha/ConnectionExcluder.cpp
deleted file mode 100644
index 67ad7202d6..0000000000
--- a/cpp/src/qpid/ha/ConnectionExcluder.cpp
+++ /dev/null
@@ -1,40 +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 "ConnectionExcluder.h"
-#include "qpid/broker/Connection.h"
-#include <boost/function.hpp>
-#include <sstream>
-
-namespace qpid {
-namespace ha {
-
-ConnectionExcluder::ConnectionExcluder() {}
-
-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()));
-}
-
-const std::string ConnectionExcluder::ADMIN_TAG="qpid.ha-admin";
-
-}} // namespace qpid::ha
diff --git a/cpp/src/qpid/ha/ConnectionObserver.cpp b/cpp/src/qpid/ha/ConnectionObserver.cpp
new file mode 100644
index 0000000000..3f7a1710d9
--- /dev/null
+++ b/cpp/src/qpid/ha/ConnectionObserver.cpp
@@ -0,0 +1,94 @@
+/*
+ *
+ * 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) {
+ try {
+ 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) {
+ QPID_LOG(debug, "HA broker rejected self connection "+connection.getMgmtId());
+ connection.abort();
+ }
+
+ }
+ ObserverPtr o(getObserver());
+ if (o) o->opened(connection);
+ }
+ catch (const std::exception& e) {
+ QPID_LOG(error, logPrefix << "Open error: " << e.what());
+ throw;
+ }
+}
+
+void ConnectionObserver::closed(broker::Connection& connection) {
+ try {
+ BrokerInfo info;
+ ObserverPtr o(getObserver());
+ if (o) o->closed(connection);
+ }
+ catch (const std::exception& e) {
+ QPID_LOG(error, logPrefix << "Close error: " << e.what());
+ throw;
+ }
+}
+
+const std::string ConnectionObserver::ADMIN_TAG="qpid.ha-admin";
+const std::string ConnectionObserver::BACKUP_TAG="qpid.ha-backup";
+
+}} // namespace qpid::ha
diff --git a/cpp/src/qpid/ha/ConnectionObserver.h b/cpp/src/qpid/ha/ConnectionObserver.h
new file mode 100644
index 0000000000..5c1dabe8f8
--- /dev/null
+++ b/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/cpp/src/qpid/ha/HaBroker.cpp b/cpp/src/qpid/ha/HaBroker.cpp
index 7d82fb63bd..d126639813 100644
--- a/cpp/src/qpid/ha/HaBroker.cpp
+++ b/cpp/src/qpid/ha/HaBroker.cpp
@@ -19,22 +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 {
@@ -42,91 +51,149 @@ namespace ha {
namespace _qmf = ::qmf::org::apache::qpid::ha;
using namespace management;
using namespace std;
+using types::Variant;
+using types::Uuid;
+using sys::Mutex;
-namespace {
-
-const std::string STANDALONE="standalone";
-const std::string CATCH_UP="catch-up";
-const std::string BACKUP="backup";
-const std::string PRIMARY="primary";
-
-} // namespace
-
-
+// Called in Plugin::earlyInitialize
HaBroker::HaBroker(broker::Broker& b, const Settings& s)
- : broker(b),
+ : logPrefix("Broker: "),
+ broker(b),
+ systemId(broker.getSystem()->getSystemId().data()),
settings(s),
- mgmtObject(0)
+ observer(new ConnectionObserver(*this, systemId)),
+ mgmtObject(0),
+ status(STANDALONE),
+ membership(systemId),
+ replicationTest(s.replicateDefault.get())
{
- // Register a factory for replicating subscriptions.
- broker.getConsumerFactories().add(
- boost::shared_ptr<ReplicatingSubscription::Factory>(
- new ReplicatingSubscription::Factory()));
+ // If we are joining a cluster we must start excluding clients now,
+ // otherwise there's a window for a client to connect before we get to
+ // initialize()
+ if (settings.cluster) {
+ QPID_LOG(debug, logPrefix << "Rejecting client connections.");
+ observer->setObserver(boost::shared_ptr<broker::ConnectionObserver>(
+ new BackupConnectionExcluder));
+ broker.getConnectionObservers().add(observer);
+ }
+}
- broker.getKnownBrokers = boost::bind(&HaBroker::getKnownBrokers, this);
+// Called in Plugin::initialize
+void HaBroker::initialize() {
+ // FIXME aconway 2012-07-19: assumes there's a TCP transport with a meaningful port.
+ brokerInfo = BrokerInfo(
+ broker.getSystem()->getNodeName(), broker.getPort(broker::Broker::TCP_TRANSPORT), systemId);
+
+ // Set up the management object.
ManagementAgent* ma = broker.getManagementAgent();
- if (!ma)
+ 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_status(settings.cluster ? BACKUP : STANDALONE);
- mgmtObject->set_replicateDefault(str(settings.replicateDefault));
+ mgmtObject->set_replicateDefault(settings.replicateDefault.str());
+ mgmtObject->set_systemId(systemId);
ma->addObject(mgmtObject);
- // NOTE: lock is not needed in a constructor but we created it just to pass
- // to the set functions.
- sys::Mutex::ScopedLock l(lock);
- if (!settings.clientUrl.empty()) setClientUrl(Url(settings.clientUrl), l);
- if (!settings.brokerUrl.empty()) setBrokerUrl(Url(settings.brokerUrl), l);
+ // Register a factory for replicating subscriptions.
+ broker.getConsumerFactories().add(
+ boost::shared_ptr<ReplicatingSubscription::Factory>(
+ new ReplicatingSubscription::Factory()));
+
+ // If we are in a cluster, start as backup in joining state.
+ if (settings.cluster) {
+ status = JOINING;
+ backup.reset(new Backup(*this, settings));
+ broker.getKnownBrokers = boost::bind(&HaBroker::getKnownBrokers, this);
+ }
+
+ if (!settings.clientUrl.empty()) setClientUrl(Url(settings.clientUrl));
+ if (!settings.brokerUrl.empty()) setBrokerUrl(Url(settings.brokerUrl));
+
+
+ QPID_LOG(notice, logPrefix << "Initializing: " << brokerInfo);
- // If we are in a cluster, we start in backup mode.
- if (settings.cluster) backup.reset(new Backup(*this, s));
+ // NOTE: lock is not needed in a constructor, but create one
+ // to pass to functions that have a ScopedLock parameter.
+ Mutex::ScopedLock l(lock);
+ statusChanged(l);
}
-HaBroker::~HaBroker() {}
+HaBroker::~HaBroker() {
+ QPID_LOG(notice, logPrefix << "Shut down: " << brokerInfo);
+ broker.getConnectionObservers().remove(observer);
+}
+
+void HaBroker::recover() {
+ auto_ptr<Backup> b;
+ {
+ Mutex::ScopedLock l(lock);
+ // 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.
+ b = backup;
+ }
+ b.reset(); // Call destructor outside of lock.
+ BrokerInfo::Set backups;
+ {
+ Mutex::ScopedLock l(lock);
+ setStatus(RECOVERING, l);
+ backups = membership.otherBackups();
+ membership.reset(brokerInfo);
+ // Drop the lock, new Primary may call back on activate.
+ }
+ // Outside of lock, may call back on activate()
+ primary.reset(new Primary(*this, backups)); // Starts primary-ready check.
+}
+
+// Called back from Primary active check.
+void HaBroker::activate() { setStatus(ACTIVE); }
Manageable::status_t HaBroker::ManagementMethod (uint32_t methodId, Args& args, string&) {
- sys::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: Promoted to primary");
- mgmtObject->set_status(PRIMARY);
+ switch (getStatus()) {
+ case JOINING: recover(); break;
+ case CATCHUP:
+ QPID_LOG(error, logPrefix << "Still catching up, cannot be promoted.");
+ throw Exception("Still catching up, cannot be promoted.");
+ break;
+ case READY: recover(); 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));
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));
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);
+ settings.mechanism, settings.username, settings.password,
+ false); // no amq.failover - don't want to use client URL.
boost::shared_ptr<broker::Link> link = result.first;
link->setUrl(url);
// Create a queue replicator
- boost::shared_ptr<QueueReplicator> qr(new QueueReplicator(queue, link));
+ boost::shared_ptr<QueueReplicator> qr(
+ new QueueReplicator(*this, queue, link));
qr->activate();
broker.getExchanges().registerExchange(qr);
break;
@@ -138,43 +205,146 @@ 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(lock);
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;
if (url.empty()) throw Url::Invalid("HA client URL is empty");
- mgmtObject->set_publicBrokers(url.str());
+ 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) {
+void HaBroker::setBrokerUrl(const Url& url) {
+ Mutex::ScopedLock l(lock);
if (url.empty()) throw Url::Invalid("HA broker URL is empty");
- QPID_LOG(debug, "HA: Setting broker URL to: " << url);
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 {
+ Mutex::ScopedLock l(lock);
return knownBrokers;
}
-void HaBroker::shutdown(const std::string& message) {
- QPID_LOG(critical, "Shutting down: " << message);
+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] = {
+ { 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
+ { READY, CATCHUP }, // Timed out failing over, demoted to catch-up.
+ { RECOVERING, ACTIVE } // All expected backups are ready
+ };
+ 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(info, 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(Mutex::ScopedLock&) {
+ Variant::List brokers = membership.asList();
+ mgmtObject->set_members(brokers);
+ broker.getManagementAgent()->raiseEvent(_qmf::EventMembersUpdate(brokers));
+}
+
+void HaBroker::setMembership(const Variant::List& brokers) {
+ Mutex::ScopedLock l(lock);
+ membership.assign(brokers);
+ QPID_LOG(info, logPrefix << "Membership update: " << membership);
+ BrokerInfo info;
+ // Update my status to what the primary says it is. The primary can toggle
+ // status between READY and CATCHUP based on the state of our subscriptions.
+ if (membership.get(systemId, info) && status != info.getStatus()) {
+ setStatus(info.getStatus(), l);
+ if (backup.get()) backup->setStatus(status);
+ }
+ membershipUpdated(l);
+}
+
+void HaBroker::resetMembership(const BrokerInfo& b) {
+ Mutex::ScopedLock l(lock);
+ membership.reset(b);
+ QPID_LOG(debug, logPrefix << "Membership reset to: " << membership);
+ membershipUpdated(l);
+}
+
+void HaBroker::addBroker(const BrokerInfo& b) {
+ Mutex::ScopedLock l(lock);
+ membership.add(b);
+ QPID_LOG(debug, logPrefix << "Membership add: " << b << " now: " << membership);
+ membershipUpdated(l);
+}
+
+void HaBroker::removeBroker(const Uuid& id) {
+ Mutex::ScopedLock l(lock);
+ membership.remove(id);
+ QPID_LOG(debug, logPrefix << "Membership remove: " << id << " now: " << membership);
+ membershipUpdated(l);
+}
+
+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/cpp/src/qpid/ha/HaBroker.h b/cpp/src/qpid/ha/HaBroker.h
index 99b30fd36b..0ffc152097 100644
--- a/cpp/src/qpid/ha/HaBroker.h
+++ b/cpp/src/qpid/ha/HaBroker.h
@@ -22,31 +22,54 @@
*
*/
+#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.
*/
class HaBroker : public management::Manageable
{
public:
+ /** HaBroker is constructed during earlyInitialize */
HaBroker(broker::Broker&, const Settings&);
~HaBroker();
+ /** Called during plugin initialization */
+ void initialize();
+
// Implement Manageable.
qpid::management::ManagementObject* GetManagementObject() const { return mgmtObject; }
management::Manageable::status_t ManagementMethod (
@@ -55,26 +78,57 @@ class HaBroker : public management::Manageable
broker::Broker& getBroker() { return broker; }
const Settings& getSettings() const { return settings; }
- // Log a critical error message and shut down the broker.
- void shutdown(const std::string& message);
+ /** 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&);
+ void setBrokerUrl(const Url&);
+ void updateClientUrl(sys::Mutex::ScopedLock&);
+
+ bool isPrimary(sys::Mutex::ScopedLock&) { return !backup.get(); }
+
+ void setStatus(BrokerStatus, sys::Mutex::ScopedLock&);
+ void recover();
+ void statusChanged(sys::Mutex::ScopedLock&);
+ void setLinkProperties(sys::Mutex::ScopedLock&);
+
std::vector<Url> getKnownBrokers() const;
+ void membershipUpdated(sys::Mutex::ScopedLock&);
+
+ std::string logPrefix;
broker::Broker& broker;
+ types::Uuid systemId;
const Settings settings;
- sys::Mutex lock;
+ mutable sys::Mutex lock;
+ boost::shared_ptr<ConnectionObserver> observer; // Used by Backup and Primary
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;
+ BrokerInfo brokerInfo;
+ Membership membership;
+ ReplicationTest replicationTest;
};
}} // namespace qpid::ha
diff --git a/cpp/src/qpid/ha/HaPlugin.cpp b/cpp/src/qpid/ha/HaPlugin.cpp
index 4da3b0d7d2..f7fe553d9b 100644
--- a/cpp/src/qpid/ha/HaPlugin.cpp
+++ b/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,21 +33,21 @@ 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-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-expected-backups", optValue(settings.expectedBackups, "N"),
- "Number of backups expected to be active in the HA cluster.")
("ha-username", optValue(settings.username, "USER"),
"Username for connections between HA brokers")
("ha-password", optValue(settings.password, "PASS"),
"Password for connections between HA brokers")
("ha-mechanism", optValue(settings.mechanism, "MECH"),
"Authentication mechanism for connections between HA brokers")
+ ("ha-backup-timeout", optValue(settings.backupTimeout, "SECONDS"),
+ "Maximum time to wait for an expected backup to connect and become ready.")
;
}
};
@@ -62,14 +62,26 @@ struct HaPlugin : public Plugin {
Options* getOptions() { return &options; }
- void earlyInitialize(Plugin::Target& ) {}
+ void earlyInitialize(Plugin::Target& target) {
+ broker::Broker* broker = dynamic_cast<broker::Broker*>(&target);
+ if (broker) {
+ // Must create the HaBroker in earlyInitialize so it can set up its
+ // connection observer before clients start connecting.
+ haBroker.reset(new ha::HaBroker(*broker, settings));
+ broker->addFinalizer(boost::bind(&HaPlugin::finalize, this));
+ }
+ }
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->initialize();
+ }
+
+ void finalize() {
+ haBroker.reset();
}
};
-static HaPlugin instance; // Static initialization.
+HaPlugin instance; // Static initialization.
}} // namespace qpid::ha
diff --git a/cpp/src/qpid/ha/Membership.cpp b/cpp/src/qpid/ha/Membership.cpp
new file mode 100644
index 0000000000..cc2906dd8f
--- /dev/null
+++ b/cpp/src/qpid/ha/Membership.cpp
@@ -0,0 +1,85 @@
+/*
+ *
+ * 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;
+}
+
+std::ostream& operator<<(std::ostream& o, const Membership& members) {
+ return o << members.brokers;
+}
+
+}} // namespace qpid::ha
diff --git a/cpp/src/qpid/ha/Membership.h b/cpp/src/qpid/ha/Membership.h
new file mode 100644
index 0000000000..3bd8653a64
--- /dev/null
+++ b/cpp/src/qpid/ha/Membership.h
@@ -0,0 +1,68 @@
+#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;
+ friend std::ostream& operator<<(std::ostream&, const Membership&);
+};
+
+std::ostream& operator<<(std::ostream&, const Membership&);
+
+}} // namespace qpid::ha
+
+#endif /*!QPID_HA_MEMBERSHIP_H*/
diff --git a/cpp/src/qpid/ha/Primary.cpp b/cpp/src/qpid/ha/Primary.cpp
new file mode 100644
index 0000000000..69c94bfc7d
--- /dev/null
+++ b/cpp/src/qpid/ha/Primary.cpp
@@ -0,0 +1,252 @@
+/*
+ *
+ * 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/Connection.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Timer.h"
+#include <boost/bind.hpp>
+
+namespace qpid {
+namespace ha {
+
+using sys::Mutex;
+
+namespace {
+
+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;
+};
+
+class ExpectedBackupTimerTask : public sys::TimerTask {
+ public:
+ ExpectedBackupTimerTask(Primary& p, sys::AbsTime deadline)
+ : TimerTask(deadline, "ExpectedBackupTimerTask"), primary(p) {}
+ void fire() { primary.timeoutExpectedBackups(); }
+ 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(notice, logPrefix << "Promoted to primary. No expected backups.");
+ }
+ else {
+ // NOTE: RemoteBackups must be created before we set the ConfigurationObserver
+ // or ConnectionObserver so that there is no client activity while
+ // the QueueGuards are created.
+ QPID_LOG(notice, logPrefix << "Promoted to primary. Expected backups: " << expect);
+ for (BrokerInfo::Set::const_iterator i = expect.begin(); i != expect.end(); ++i) {
+ boost::shared_ptr<RemoteBackup> backup(
+ new RemoteBackup(*i, haBroker.getReplicationTest(), false));
+ backups[i->getSystemId()] = backup;
+ if (!backup->isReady()) expectedBackups.insert(backup);
+ backup->setInitialQueues(hb.getBroker().getQueues(), true); // Create guards
+ }
+ // Set timeout for expected brokers to connect and become ready.
+ sys::Duration timeout(int64_t(hb.getSettings().backupTimeout*sys::TIME_SEC));
+ sys::AbsTime deadline(sys::now(), timeout);
+ timerTask = new ExpectedBackupTimerTask(*this, deadline);
+ hb.getBroker().getTimer().add(timerTask);
+ }
+
+ 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() {
+ if (timerTask) timerTask->cancel();
+ haBroker.getBroker().getConfigurationObservers().remove(configurationObserver);
+}
+
+void Primary::checkReady(Mutex::ScopedLock&) {
+ if (!active && expectedBackups.empty()) {
+ active = true;
+ Mutex::ScopedUnlock u(lock); // Don't hold lock across callback
+ QPID_LOG(notice, logPrefix << "Finished waiting for backups, primary is active.");
+ haBroker.activate();
+ }
+}
+
+void Primary::checkReady(BackupMap::iterator i, Mutex::ScopedLock& l) {
+ if (i != backups.end() && i->second->reportReady()) {
+ BrokerInfo info = i->second->getBrokerInfo();
+ info.setStatus(READY);
+ haBroker.addBroker(info);
+ if (expectedBackups.erase(i->second)) {
+ QPID_LOG(info, logPrefix << "Expected backup is ready: " << info);
+ checkReady(l);
+ }
+ else
+ QPID_LOG(info, logPrefix << "New backup is ready: " << info);
+ }
+}
+
+void Primary::timeoutExpectedBackups() {
+ try {
+ sys::Mutex::ScopedLock l(lock);
+ if (active) return; // Already activated
+ // Remove records for any expectedBackups that are not yet connected
+ // Allow backups that are connected to continue becoming ready.
+ for (BackupSet::iterator i = expectedBackups.begin(); i != expectedBackups.end();)
+ {
+ boost::shared_ptr<RemoteBackup> rb = *i;
+ if (!rb->isConnected()) {
+ BrokerInfo info = rb->getBrokerInfo();
+ QPID_LOG(error, logPrefix << "Expected backup timed out: " << info);
+ expectedBackups.erase(i++);
+ backups.erase(info.getSystemId());
+ rb->cancel();
+ // Downgrade the broker to CATCHUP
+ info.setStatus(CATCHUP);
+ haBroker.addBroker(info);
+ }
+ else ++i;
+ }
+ checkReady(l);
+ }
+ catch(const std::exception& e) {
+ QPID_LOG(error, logPrefix << "Error timing out backups: " << e.what());
+ // No-where for this exception to go.
+ }
+}
+
+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) {
+ BrokerInfo info;
+ if (ha::ConnectionObserver::getBrokerInfo(connection, info)) {
+ Mutex::ScopedLock l(lock);
+ BackupMap::iterator i = backups.find(info.getSystemId());
+ if (i == backups.end()) {
+ boost::shared_ptr<RemoteBackup> backup(
+ new RemoteBackup(info, haBroker.getReplicationTest(), true));
+ {
+ // Avoid deadlock with queue registry lock.
+ Mutex::ScopedUnlock u(lock);
+ backup->setInitialQueues(haBroker.getBroker().getQueues(), false);
+ }
+ backups[info.getSystemId()] = backup;
+ QPID_LOG(debug, logPrefix << "New backup connected: " << info);
+ }
+ else {
+ QPID_LOG(debug, logPrefix << "Known backup connected: " << info);
+ i->second->setConnected(true);
+ checkReady(i, l);
+ }
+ if (info.getStatus() == JOINING) info.setStatus(CATCHUP);
+ haBroker.addBroker(info);
+ }
+ else
+ QPID_LOG(debug, logPrefix << "Accepted client connection "
+ << connection.getMgmtId());
+}
+
+void Primary::closed(broker::Connection& connection) {
+ Mutex::ScopedLock l(lock);
+ BrokerInfo info;
+ if (ha::ConnectionObserver::getBrokerInfo(connection, info)) {
+ QPID_LOG(debug, logPrefix << "Backup disconnected: " << info);
+ haBroker.removeBroker(info.getSystemId());
+ BackupMap::iterator i = backups.find(info.getSystemId());
+ if (i != backups.end()) i->second->setConnected(false);
+ }
+ // NOTE: we do not remove from the backups map here, the backups map holds
+ // all the backups we know about whether connected or not.
+ //
+ // It is possible for a backup connection to be rejected while we are a backup,
+ // but the closed is seen after 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)
+{
+ Mutex::ScopedLock l(lock);
+ 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/cpp/src/qpid/ha/Primary.h b/cpp/src/qpid/ha/Primary.h
new file mode 100644
index 0000000000..26883f4416
--- /dev/null
+++ b/cpp/src/qpid/ha/Primary.h
@@ -0,0 +1,115 @@
+#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 <boost/intrusive_ptr.hpp>
+#include <map>
+#include <string>
+
+namespace qpid {
+
+namespace broker {
+class Queue;
+class Connection;
+class ConnectionObserver;
+class ConfigurationObserver;
+}
+
+namespace sys {
+class TimerTask;
+}
+
+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&);
+
+ // Called in timer thread when the deadline for expected backups expires.
+ void timeoutExpectedBackups();
+
+ 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 expectedBackups;
+ /**
+ * 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;
+ boost::intrusive_ptr<sys::TimerTask> timerTask;
+
+ static Primary* instance;
+};
+}} // namespace qpid::ha
+
+#endif /*!QPID_HA_PRIMARY_H*/
diff --git a/cpp/src/qpid/ha/QueueGuard.cpp b/cpp/src/qpid/ha/QueueGuard.cpp
new file mode 100644
index 0000000000..a30ab1f73c
--- /dev/null
+++ b/cpp/src/qpid/ha/QueueGuard.cpp
@@ -0,0 +1,139 @@
+/*
+ *
+ * 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)
+{
+ std::ostringstream os;
+ os << "Primary guard " << queue.getName() << "@" << info.getLogId() << ": ";
+ logPrefix = os.str();
+ observer.reset(new QueueObserver(*this));
+ queue.addObserver(observer);
+ // Set range after addObserver so we know that range.back+1 is a guarded position.
+ range = QueueRange(q);
+}
+
+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.
+ // Those messages are already on the backup.
+ 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();
+}
+
+}} // namespaces qpid::ha
diff --git a/cpp/src/qpid/ha/QueueGuard.h b/cpp/src/qpid/ha/QueueGuard.h
new file mode 100644
index 0000000000..bc8f40b65f
--- /dev/null
+++ b/cpp/src/qpid/ha/QueueGuard.h
@@ -0,0 +1,118 @@
+#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 un-guarded queue range at the time the QueueGuard was created.
+ *
+ * The first position guaranteed to be protected by the guard is
+ * getRange().getBack()+1. It is possible that the guard has protected some
+ * messages before that point. Any such messages are dealt with in subscriptionStart
+ *
+ * The QueueGuard is created in 3 situations
+ * - when a backup is promoted, guards are created for expected backups.
+ * - when a new queue is created on the primary
+ * - when a new backup joins.
+ *
+ * In the last situation the queue is active while the guard is being
+ * created.
+ *
+ */
+ 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 subscription.
+ *@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;
+ QueueRange range;
+};
+}} // namespace qpid::ha
+
+#endif /*!QPID_HA_QUEUEGUARD_H*/
diff --git a/cpp/src/qpid/ha/QueueRange.h b/cpp/src/qpid/ha/QueueRange.h
new file mode 100644
index 0000000000..d734326910
--- /dev/null
+++ b/cpp/src/qpid/ha/QueueRange.h
@@ -0,0 +1,85 @@
+#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.
+ *
+ * The *back* of the queue is the position of the latest (most recently pushed)
+ * message on the queue or, if the queue is empty, the back is n-1 where n is
+ * the position that will be assigned to the next message pushed onto the queue.
+ *
+ * The *front* of the queue is the position of the oldest (next to be consumed) message
+ * on the queue or, if the queue is empty, it is the position that will be occupied
+ * by the next message pushed onto the queue.
+ *
+ * This leads to the slightly surprising conclusion that for an empty queue
+ * front = back+1
+ */
+struct QueueRange {
+ public:
+ framing::SequenceNumber front, back;
+
+ QueueRange() : front(1), back(0) { } // Empty range.
+
+ QueueRange(broker::Queue& q) {
+ if (ReplicatingSubscription::getFront(q, front))
+ back = q.getPosition();
+ else {
+ back = q.getPosition();
+ front = back+1; // empty
+ }
+ 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/cpp/src/qpid/ha/QueueReplicator.cpp b/cpp/src/qpid/ha/QueueReplicator.cpp
index 633619be13..be910a087f 100644
--- a/cpp/src/qpid/ha/QueueReplicator.cpp
+++ b/cpp/src/qpid/ha/QueueReplicator.cpp
@@ -19,6 +19,7 @@
*
*/
+#include "HaBroker.h"
#include "QueueReplicator.h"
#include "ReplicatingSubscription.h"
#include "qpid/broker/Bridge.h"
@@ -44,25 +45,39 @@ 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(HaBroker& hb,
+ boost::shared_ptr<Queue> q,
+ boost::shared_ptr<Link> l)
+ : Exchange(replicatorName(q->getName()), 0, q->getBroker()),
+ haBroker(hb),
+ logPrefix("Backup queue "+q->getName()+": "),
+ queue(q), link(l), brokerInfo(hb.getBrokerInfo())
{
- logPrefix = "HA: Backup of " + queue->getName() + ": ";
- QPID_LOG(info, logPrefix << "Created");
+ 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
@@ -77,43 +92,47 @@ void QueueReplicator::activate() {
// before initializeBridge is called.
boost::bind(&QueueReplicator::initializeBridge, shared_from_this(), _1, _2)
);
+ bridge = result.first;
}
-QueueReplicator::~QueueReplicator() {}
+QueueReplicator::~QueueReplicator() { deactivate(); }
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.
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 {
@@ -127,13 +146,11 @@ 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.
@@ -142,29 +159,33 @@ void QueueReplicator::route(Deliverable& msg)
try {
const std::string& key = msg.getMessage().getRoutingKey();
sys::Mutex::ScopedLock l(lock);
- if (key == DEQUEUE_EVENT_KEY) {
+ 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) {
+ }
+ else if (key == POSITION_EVENT_KEY) {
SequenceNumber position = decodeContent<SequenceNumber>(msg.getMessage());
QPID_LOG(trace, logPrefix << "Position moved from " << queue->getPosition()
<< " to " << position);
- if (queue->getPosition() > position) {
- throw Exception(
- QPID_MSG(logPrefix << "Invalid position update 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);
- } else {
- msg.deliverTo(queue);
- QPID_LOG(trace, logPrefix << "Enqueued message " << queue->getPosition());
}
+ // Ignore unknown event keys, may be introduced in later versions.
}
catch (const std::exception& e) {
QPID_LOG(critical, logPrefix << "Replication failed: " << e.what());
+ haBroker.shutdown();
throw;
}
}
diff --git a/cpp/src/qpid/ha/QueueReplicator.h b/cpp/src/qpid/ha/QueueReplicator.h
index bcbac988fa..f8a68ea38f 100644
--- a/cpp/src/qpid/ha/QueueReplicator.h
+++ b/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>
@@ -38,6 +40,7 @@ class Deliverable;
}
namespace ha {
+class HaBroker;
/**
* Exchange created on a backup broker to replicate a queue on the primary.
@@ -55,8 +58,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(HaBroker&,
+ 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
@@ -71,13 +79,16 @@ class QueueReplicator : public broker::Exchange,
private:
void initializeBridge(broker::Bridge& bridge, broker::SessionHandler& sessionHandler);
- void dequeue(framing::SequenceNumber, const sys::Mutex::ScopedLock&);
+ void dequeue(framing::SequenceNumber, sys::Mutex::ScopedLock&);
+ HaBroker& haBroker;
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/cpp/src/qpid/ha/RemoteBackup.cpp b/cpp/src/qpid/ha/RemoteBackup.cpp
new file mode 100644
index 0000000000..a5693fd14e
--- /dev/null
+++ b/cpp/src/qpid/ha/RemoteBackup.cpp
@@ -0,0 +1,120 @@
+/*
+ *
+ * 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;
+using boost::bind;
+
+RemoteBackup::RemoteBackup(const BrokerInfo& info, ReplicationTest rt, bool con) :
+ logPrefix("Primary remote backup "+info.getLogId()+": "),
+ brokerInfo(info), replicationTest(rt), connected(con), reportedReady(false)
+{}
+
+void RemoteBackup::setInitialQueues(broker::QueueRegistry& queues, bool createGuards)
+{
+ QPID_LOG(debug, logPrefix << "Setting initial queues" << (createGuards ? " and guards" : ""));
+ queues.eachQueue(boost::bind(&RemoteBackup::initialQueue, this, _1, createGuards));
+}
+
+RemoteBackup::~RemoteBackup() { cancel(); }
+
+void RemoteBackup::cancel() {
+ for (GuardMap::iterator i = guards.begin(); i != guards.end(); ++i)
+ i->second->cancel();
+ guards.clear();
+}
+
+bool RemoteBackup::isReady() {
+ return connected && initialQueues.empty();
+}
+
+void RemoteBackup::initialQueue(const QueuePtr& q, bool createGuard) {
+ if (replicationTest.isReplicated(ALL, *q)) {
+ initialQueues.insert(q);
+ if (createGuard) guards[q].reset(new QueueGuard(*q, brokerInfo));
+ }
+}
+
+RemoteBackup::GuardPtr RemoteBackup::guard(const QueuePtr& q) {
+ GuardMap::iterator i = guards.find(q);
+ GuardPtr guard;
+ if (i != guards.end()) {
+ guard = i->second;
+ guards.erase(i);
+ }
+ return guard;
+}
+
+namespace {
+typedef std::set<boost::shared_ptr<broker::Queue> > QS;
+struct QueueSetPrinter {
+ const QS& qs;
+ std::string prefix;
+ QueueSetPrinter(const std::string& p, const QS& q) : qs(q), prefix(p) {}
+};
+std::ostream& operator<<(std::ostream& o, const QueueSetPrinter& qp) {
+ if (!qp.qs.empty()) o << qp.prefix;
+ 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()
+ << QueueSetPrinter(", waiting for: ", initialQueues));
+ if (isReady()) QPID_LOG(debug, logPrefix << "All queues ready");
+}
+
+// Called via ConfigurationObserver::queueCreate and from initialQueue
+void RemoteBackup::queueCreate(const QueuePtr& q) {
+ if (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);
+ }
+}
+
+bool RemoteBackup::reportReady() {
+ if (!reportedReady && isReady()) {
+ reportedReady = true;
+ return true;
+ }
+ return false;
+}
+
+}} // namespace qpid::ha
diff --git a/cpp/src/qpid/ha/RemoteBackup.h b/cpp/src/qpid/ha/RemoteBackup.h
new file mode 100644
index 0000000000..8ee776e90b
--- /dev/null
+++ b/cpp/src/qpid/ha/RemoteBackup.h
@@ -0,0 +1,111 @@
+#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 Queue;
+class QueueRegistry;
+}
+
+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
+ *@param connected true if the backup is already connected.
+ */
+ RemoteBackup(const BrokerInfo& info, ReplicationTest, bool connected);
+ ~RemoteBackup();
+
+ /** Set the initial queues for all queues in the registry.
+ *@createGuards if true create guards also, if false guards will be created on demand.
+ */
+ void setInitialQueues(broker::QueueRegistry&, bool createGuards);
+
+ /** Return guard associated with a queue. Used to create ReplicatingSubscription. */
+ GuardPtr guard(const QueuePtr&);
+
+ /** Is the remote backup connected? */
+ void setConnected(bool b) { connected=b; }
+ bool isConnected() const { return connected; }
+
+ /** 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();
+
+ /**@return true if isReady() and this is the first call to reportReady */
+ bool reportReady();
+
+ /**Cancel all queue guards, called if we are timed out. */
+ void cancel();
+
+ BrokerInfo getBrokerInfo() const { return brokerInfo; }
+ private:
+ typedef std::map<QueuePtr, GuardPtr> GuardMap;
+ typedef std::set<QueuePtr> QueueSet;
+
+ /** Add queue to guard as an initial queue */
+ void initialQueue(const QueuePtr&, bool createGuard);
+
+ std::string logPrefix;
+ BrokerInfo brokerInfo;
+ ReplicationTest replicationTest;
+ GuardMap guards;
+ QueueSet initialQueues;
+ bool connected;
+ bool reportedReady;
+};
+
+}} // namespace qpid::ha
+
+#endif /*!QPID_HA_REMOTEBACKUP_H*/
diff --git a/cpp/src/qpid/ha/ReplicateLevel.cpp b/cpp/src/qpid/ha/ReplicateLevel.cpp
deleted file mode 100644
index 4981577225..0000000000
--- a/cpp/src/qpid/ha/ReplicateLevel.cpp
+++ /dev/null
@@ -1,72 +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 "ReplicateLevel.h"
-#include "qpid/Exception.h"
-#include "qpid/Msg.h"
-#include <iostream>
-#include <assert.h>
-
-namespace qpid {
-namespace ha {
-
-using namespace std;
-
-// Note replicateLevel is called during plugin-initialization which
-// happens in the static construction phase so these constants need
-// to be POD, they can't be class objects
-//
-namespace {
-const char* S_NONE="none";
-const char* S_CONFIGURATION="configuration";
-const char* S_ALL="all";
-}
-
-bool replicateLevel(const string& level, ReplicateLevel& out) {
- if (level == S_NONE) { out = RL_NONE; return true; }
- if (level == S_CONFIGURATION) { out = RL_CONFIGURATION; return true; }
- if (level == S_ALL) { out = RL_ALL; return true; }
- return false;
-}
-
-ReplicateLevel replicateLevel(const string& level) {
- ReplicateLevel rl;
- if (!replicateLevel(level, rl))
- throw Exception("Invalid value for replication level: "+level);
- return rl;
-}
-
-string str(ReplicateLevel l) {
- const char* names[] = { S_NONE, S_CONFIGURATION, S_ALL };
- if (l > RL_ALL)
- throw Exception(QPID_MSG("Invalid value for replication level: " << l));
- return names[l];
-}
-
-ostream& operator<<(ostream& o, ReplicateLevel rl) { return o << str(rl); }
-
-istream& operator>>(istream& i, ReplicateLevel& rl) {
- string str;
- i >> str;
- rl = replicateLevel(str);
- return i;
-}
-
-}} // namespace qpid::ha
diff --git a/cpp/src/qpid/ha/ReplicateLevel.h b/cpp/src/qpid/ha/ReplicateLevel.h
deleted file mode 100644
index c11e03f0ce..0000000000
--- a/cpp/src/qpid/ha/ReplicateLevel.h
+++ /dev/null
@@ -1,52 +0,0 @@
-#ifndef QPID_HA_REPLICATELEVEL_H
-#define QPID_HA_REPLICATELEVEL_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 <string>
-#include <iosfwd>
-
-namespace qpid {
-namespace ha {
-
-enum ReplicateLevel { RL_NONE, RL_CONFIGURATION, RL_ALL };
-
-/**
- * If str is a valid replicate level, set out and return true.
- */
-bool replicateLevel(const std::string& str, ReplicateLevel& out);
-
-/**
- *@return enum corresponding to string level.
- *@throw qpid::Exception if level is not a valid replication level.
- */
-ReplicateLevel replicateLevel(const std::string& level);
-
-/**@return string form of replicate level */
-std::string str(ReplicateLevel l);
-
-std::ostream& operator<<(std::ostream&, ReplicateLevel);
-std::istream& operator>>(std::istream&, ReplicateLevel&);
-
-}} // namespaces qpid::ha
-
-#endif /*!QPID_HA_REPLICATELEVEL_H*/
diff --git a/cpp/src/qpid/ha/ReplicatingSubscription.cpp b/cpp/src/qpid/ha/ReplicatingSubscription.cpp
index 91a4538bc4..c960758eaf 100644
--- a/cpp/src/qpid/ha/ReplicatingSubscription.cpp
+++ b/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,179 +160,223 @@ 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)
{
- // Separate the remote part from a "local-remote" address.
- string address = parent->getSession().getConnection().getUrl();
- size_t i = address.find('-');
- if (i != string::npos) address = address.substr(i+1);
- logPrefix = "HA: Primary ";
- stringstream ss;
- logSuffix = " (" + address + ")";
-
- // 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() << logSuffix);
-
- // 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.
-}
-
-// Message is delivered in the subscription's connection thread.
-bool ReplicatingSubscription::deliver(QueuedMessage& m) {
try {
- // 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);
- if (position != m.position)
- throw Exception(
- QPID_MSG("Expected position " << position
- << " but got " << 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.)
- if (m.position <= backupPosition)
- throw Exception(
- QPID_MSG("Expected position > " << backupPosition
- << " but got " << m.position));
-
- 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);
+ FieldTable ft;
+ if (!arguments.getTable(ReplicatingSubscription::QPID_BROKER_INFO, ft))
+ throw Exception("Replicating subscription does not have broker info: " + tag);
+ info.assign(ft);
+
+ // Set a log prefix message that identifies the remote broker.
+ ostringstream os;
+ os << "Primary " << queue->getName() << "@" << info.getLogId() << ": ";
+ logPrefix = os.str();
+
+ // NOTE: Once the guard is attached we can have concurrent
+ // calls to dequeued so we need to lock use of this->dequeues.
+ //
+ // 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, info));
+ guard->attach(*this);
+
+ QueueRange backup(arguments); // Remote backup range.
+ QueueRange primary(guard->getRange()); // Unguarded range when the guard was set.
+ backupPosition = backup.back;
+
+ // Sync backup and primary queues, don't send messages already on the backup
+
+ if (backup.front > primary.front || // Missing messages at front
+ backup.back < primary.front || // No overlap
+ primary.empty() || backup.empty()) // Empty
+ {
+ // No useful 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;
}
- backupPosition = m.position;
- QPID_LOG(trace, logPrefix << "replicating " << m << logSuffix);
+ 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;
}
- return ConsumerImpl::deliver(m);
- } catch (const std::exception& e) {
- QPID_LOG(critical, logPrefix << "error replicating " << getQueue()->getName()
- << logSuffix << ": " << e.what());
+ // NOTE: we are assuming that the messages that are on the backup are
+ // consistent with those on the primary. If the backup is a replica
+ // queue and hasn't been tampered with then that will be the case.
+
+ 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) {
+ QPID_LOG(error, logPrefix << "Creation error: " << e.what()
+ << ": arguments=" << getArguments());
throw;
}
}
-ReplicatingSubscription::~ReplicatingSubscription() {}
-
+ReplicatingSubscription::~ReplicatingSubscription() {
+ QPID_LOG(debug, logPrefix << "Detroyed replicating subscription");
+}
-// INVARIANT: delayed contains msg <=> we have outstanding startCompletion on msg
+// 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() {
+ try {
+ Mutex::ScopedLock l(lock); // Note dequeued() can be called concurrently.
-// 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 " << qm << logSuffix);
- 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);
- }
+ // Send initial dequeues and position to the backup.
+ // There must be a shared_ptr(this) when sending.
+ sendDequeueEvent(l);
+ sendPositionEvent(position, l);
+ backupPosition = position;
+ }
+ catch (const std::exception& e) {
+ QPID_LOG(error, logPrefix << "Initialization error: " << e.what()
+ << ": arguments=" << getArguments());
+ throw;
}
}
-// 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 " << qm << logSuffix);
- qm.payload->getIngressCompletion().startCompleter();
- assert(delayed.find(qm.position) == delayed.end());
- delayed[qm.position] = qm;
+// 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;
+ }
}
-
-// 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 " << v.second << logSuffix);
- v.second.payload->getIngressCompletion().finishCompleter();
+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 << "cancel backup subscription " << getName() << logSuffix);
- 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&)
{
- QPID_LOG(trace, logPrefix << "sending dequeues " << dequeues
- << " from " << getQueue()->getName() << logSuffix);
+ 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 " << qm << logSuffix);
+ 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 << logSuffix);
- 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();
@@ -276,15 +396,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);
}
@@ -292,19 +411,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/cpp/src/qpid/ha/ReplicatingSubscription.h b/cpp/src/qpid/ha/ReplicatingSubscription.h
index f9176915f6..a80141a6c2 100644
--- a/cpp/src/qpid/ha/ReplicatingSubscription.h
+++ b/cpp/src/qpid/ha/ReplicatingSubscription.h
@@ -22,10 +22,10 @@
*
*/
-#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 +42,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 +76,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 +99,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, logSuffix;
- boost::shared_ptr<broker::Queue> events;
- boost::shared_ptr<broker::Consumer> consumer;
- Delayed delayed;
+ std::string logPrefix;
+ 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/cpp/src/qpid/ha/ReplicationTest.cpp b/cpp/src/qpid/ha/ReplicationTest.cpp
new file mode 100644
index 0000000000..18e0953930
--- /dev/null
+++ b/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/cpp/src/qpid/ha/ReplicationTest.h b/cpp/src/qpid/ha/ReplicationTest.h
new file mode 100644
index 0000000000..9f6976a8e4
--- /dev/null
+++ b/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/cpp/src/qpid/ha/Settings.h b/cpp/src/qpid/ha/Settings.h
index bf70c3f3f7..37235b5c79 100644
--- a/cpp/src/qpid/ha/Settings.h
+++ b/cpp/src/qpid/ha/Settings.h
@@ -22,7 +22,7 @@
*
*/
-#include "ReplicateLevel.h"
+#include "types.h"
#include <string>
namespace qpid {
@@ -34,13 +34,15 @@ namespace ha {
class Settings
{
public:
- Settings() : cluster(false), expectedBackups(0), replicateDefault(RL_NONE) {}
+ Settings() : cluster(false), replicateDefault(NONE), backupTimeout(5)
+ {}
+
bool cluster; // True if we are a cluster member.
std::string clientUrl;
std::string brokerUrl;
- size_t expectedBackups;
- ReplicateLevel replicateDefault;
+ Enum<ReplicateLevel> replicateDefault;
std::string username, password, mechanism;
+ double backupTimeout;
private:
};
}} // namespace qpid::ha
diff --git a/cpp/src/qpid/ha/management-schema.xml b/cpp/src/qpid/ha/management-schema.xml
index 363dff61fb..3da482e732 100644
--- a/cpp/src/qpid/ha/management-schema.xml
+++ b/cpp/src/qpid/ha/management-schema.xml
@@ -25,37 +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="replicateDefault" type="sstr"
- desc="Replicate value for queues/exchanges without a 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 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/cpp/src/qpid/ha/types.cpp b/cpp/src/qpid/ha/types.cpp
new file mode 100644
index 0000000000..53e2056213
--- /dev/null
+++ b/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/cpp/src/qpid/ha/types.h b/cpp/src/qpid/ha/types.h
new file mode 100644
index 0000000000..35faf9f624
--- /dev/null
+++ b/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/cpp/src/qpid/log/Logger.cpp b/cpp/src/qpid/log/Logger.cpp
index 800881c077..7c2e807ca9 100644
--- a/cpp/src/qpid/log/Logger.cpp
+++ b/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/cpp/src/qpid/log/Options.cpp b/cpp/src/qpid/log/Options.cpp
index 1259244297..10422bbb1e 100644
--- a/cpp/src/qpid/log/Options.cpp
+++ b/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/cpp/src/qpid/log/Selector.cpp b/cpp/src/qpid/log/Selector.cpp
index a4bc580470..8757486d88 100644
--- a/cpp/src/qpid/log/Selector.cpp
+++ b/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/cpp/src/qpid/log/Statement.cpp b/cpp/src/qpid/log/Statement.cpp
index 79f7a28100..09ef458547 100644
--- a/cpp/src/qpid/log/Statement.cpp
+++ b/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", "Model", "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/cpp/src/qpid/log/posix/SinkOptions.cpp b/cpp/src/qpid/log/posix/SinkOptions.cpp
index 8459938e5c..d3db5f3cdf 100644
--- a/cpp/src/qpid/log/posix/SinkOptions.cpp
+++ b/cpp/src/qpid/log/posix/SinkOptions.cpp
@@ -22,11 +22,14 @@
#include "qpid/log/OstreamOutput.h"
#include "qpid/memory.h"
#include "qpid/Exception.h"
+
#include <iostream>
#include <map>
#include <string>
#include <syslog.h>
+#include <boost/lexical_cast.hpp>
+
using std::string;
using qpid::Exception;
@@ -90,7 +93,7 @@ public:
string name(int value) const {
ByValue::const_iterator i = byValue.find(value);
if (i == byValue.end())
- throw Exception("Not a valid syslog value: " + value);
+ throw Exception("Not a valid syslog value: " + boost::lexical_cast<string>(value));
return i->second;
}
diff --git a/cpp/src/qpid/management/Buffer.cpp b/cpp/src/qpid/management/Buffer.cpp
index 7556b2a243..0ad6e9a851 100644
--- a/cpp/src/qpid/management/Buffer.cpp
+++ b/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/cpp/src/qpid/management/ManagementAgent.cpp b/cpp/src/qpid/management/ManagementAgent.cpp
index 062a530706..7d90ed99d0 100644
--- a/cpp/src/qpid/management/ManagementAgent.cpp
+++ b/cpp/src/qpid/management/ManagementAgent.cpp
@@ -1344,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();
@@ -1662,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);
@@ -2426,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;
@@ -2468,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;
}
@@ -2480,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;
@@ -2507,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;
}
diff --git a/cpp/src/qpid/management/ManagementDirectExchange.cpp b/cpp/src/qpid/management/ManagementDirectExchange.cpp
index 312eacf462..9432a21b3a 100644
--- a/cpp/src/qpid/management/ManagementDirectExchange.cpp
+++ b/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/cpp/src/qpid/management/ManagementTopicExchange.cpp b/cpp/src/qpid/management/ManagementTopicExchange.cpp
index 587cc660df..e5b659f217 100644
--- a/cpp/src/qpid/management/ManagementTopicExchange.cpp
+++ b/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/cpp/src/qpid/messaging/PrivateImplRef.h b/cpp/src/qpid/messaging/PrivateImplRef.h
index 718faef475..47afe30919 100644
--- a/cpp/src/qpid/messaging/PrivateImplRef.h
+++ b/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/cpp/src/qpid/replication/ReplicationExchange.cpp b/cpp/src/qpid/replication/ReplicationExchange.cpp
index 66f4f14d0c..bcb7c7f293 100644
--- a/cpp/src/qpid/replication/ReplicationExchange.cpp
+++ b/cpp/src/qpid/replication/ReplicationExchange.cpp
@@ -184,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/cpp/src/qpid/sys/AsynchIOHandler.cpp b/cpp/src/qpid/sys/AsynchIOHandler.cpp
index 5233002850..8a485db72d 100644
--- a/cpp/src/qpid/sys/AsynchIOHandler.cpp
+++ b/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,11 +42,30 @@ 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),
codec(0),
+ reads(0),
readError(false),
isClient(false),
readCredit(InfiniteCredit)
@@ -54,12 +74,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);
@@ -129,10 +155,18 @@ void AsynchIOHandler::readbuff(AsynchIO& , AsynchIO::BufferBase* buff) {
}
}
+ ++reads;
size_t decoded = 0;
if (codec) { // Already initiated
try {
decoded = codec->decode(buff->bytes+buff->dataStart, buff->dataCount);
+ // When we've decoded 3 reads (probably frames) we will have authenticated and
+ // started heartbeats, if specified, in many (but not all) cases so now we will cancel
+ // the idle connection timeout - this is really hacky, and would be better implemented
+ // in the connection, but that isn't actually created until the first decode.
+ if (reads == 3) {
+ timeoutTimerTask->cancel();
+ }
}catch(const std::exception& e){
QPID_LOG(error, e.what());
readError = true;
@@ -143,6 +177,7 @@ void AsynchIOHandler::readbuff(AsynchIO& , AsynchIO::BufferBase* buff) {
framing::ProtocolInitiation protocolInit;
if (protocolInit.decode(in)) {
decoded = in.getPosition();
+
QPID_LOG(debug, "RECV [" << identifier << "]: INIT(" << protocolInit << ")");
try {
codec = factory->create(protocolInit.getVersion(), *this, identifier, SecuritySettings());
@@ -202,6 +237,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/cpp/src/qpid/sys/AsynchIOHandler.h b/cpp/src/qpid/sys/AsynchIOHandler.h
index b9867606c4..307aad5b85 100644
--- a/cpp/src/qpid/sys/AsynchIOHandler.h
+++ b/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,24 +40,28 @@ namespace sys {
class AsynchIO;
struct AsynchIOBufferBase;
class Socket;
+class Timer;
+class TimerTask;
class AsynchIOHandler : public OutputControl {
std::string identifier;
AsynchIO* aio;
ConnectionCodec::Factory* factory;
ConnectionCodec* codec;
+ uint32_t reads;
bool readError;
bool isClient;
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/cpp/src/qpid/sys/windows/MemStat.cpp b/cpp/src/qpid/sys/MemStat.cpp
index b1d25c5fc5..c71fba785c 100644
--- a/cpp/src/qpid/sys/windows/MemStat.cpp
+++ b/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/cpp/src/qpid/sys/SslPlugin.cpp b/cpp/src/qpid/sys/SslPlugin.cpp
index 48baef9042..3b50527c0a 100644
--- a/cpp/src/qpid/sys/SslPlugin.cpp
+++ b/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/cpp/src/qpid/sys/TCPIOPlugin.cpp b/cpp/src/qpid/sys/TCPIOPlugin.cpp
index bd10a5555a..551440f954 100644
--- a/cpp/src/qpid/sys/TCPIOPlugin.cpp
+++ b/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/cpp/src/qpid/sys/Timer.cpp b/cpp/src/qpid/sys/Timer.cpp
index 47752e4584..973c6bd8b7 100644
--- a/cpp/src/qpid/sys/Timer.cpp
+++ b/cpp/src/qpid/sys/Timer.cpp
@@ -35,7 +35,7 @@ TimerTask::TimerTask(Duration timeout, const std::string& n) :
sortTime(AbsTime::FarFuture()),
period(timeout),
nextFireTime(AbsTime::now(), timeout),
- cancelled(false)
+ state(WAITING)
{}
TimerTask::TimerTask(AbsTime time, const std::string& n) :
@@ -43,7 +43,7 @@ TimerTask::TimerTask(AbsTime time, const std::string& n) :
sortTime(AbsTime::FarFuture()),
period(0),
nextFireTime(time),
- cancelled(false)
+ state(WAITING)
{}
TimerTask::~TimerTask() {}
@@ -52,27 +52,48 @@ bool TimerTask::readyToFire() const {
return !(nextFireTime > AbsTime::now());
}
+bool TimerTask::prepareToFire() {
+ Monitor::ScopedLock l(stateMonitor);
+ if (state != CANCELLED) {
+ state = CALLING;
+ return true;
+ } else {
+ return false;
+ }
+}
+
void TimerTask::fireTask() {
- cancelled = true;
fire();
}
+void TimerTask::finishFiring() {
+ Monitor::ScopedLock l(stateMonitor);
+ if (state != CANCELLED) {
+ state = WAITING;
+ stateMonitor.notifyAll();
+ }
+}
+
// This can only be used to setup the next fire time. After the Timer has already fired
void TimerTask::setupNextFire() {
if (period && readyToFire()) {
nextFireTime = max(AbsTime::now(), AbsTime(nextFireTime, period));
- cancelled = false;
} else {
QPID_LOG(error, name << " couldn't setup next timer firing: " << Duration(nextFireTime, AbsTime::now()) << "[" << period << "]");
}
}
// Only allow tasks to be delayed
-void TimerTask::restart() { nextFireTime = max(nextFireTime, AbsTime(AbsTime::now(), period)); }
+void TimerTask::restart() {
+ nextFireTime = max(nextFireTime, AbsTime(AbsTime::now(), period));
+}
void TimerTask::cancel() {
- ScopedLock<Mutex> l(callbackLock);
- cancelled = true;
+ Monitor::ScopedLock l(stateMonitor);
+ while (state == CALLING) {
+ stateMonitor.wait();
+ }
+ state = CANCELLED;
}
void TimerTask::setFired() {
@@ -96,6 +117,22 @@ Timer::~Timer()
stop();
}
+class TimerTaskCallbackScope {
+ TimerTask& tt;
+public:
+ explicit TimerTaskCallbackScope(TimerTask& t) :
+ tt(t)
+ {}
+
+ operator bool() {
+ return !tt.prepareToFire();
+ }
+
+ ~TimerTaskCallbackScope() {
+ tt.finishFiring();
+ }
+};
+
// TODO AStitcher 21/08/09 The threshholds for emitting warnings are a little arbitrary
void Timer::run()
{
@@ -112,8 +149,8 @@ void Timer::run()
AbsTime start(AbsTime::now());
Duration delay(t->sortTime, start);
{
- ScopedLock<Mutex> l(t->callbackLock);
- if (t->cancelled) {
+ TimerTaskCallbackScope s(*t);
+ if (s) {
{
Monitor::ScopedUnlock u(monitor);
drop(t);
diff --git a/cpp/src/qpid/sys/Timer.h b/cpp/src/qpid/sys/Timer.h
index fccb17dbc2..5731b8d977 100644
--- a/cpp/src/qpid/sys/Timer.h
+++ b/cpp/src/qpid/sys/Timer.h
@@ -40,6 +40,7 @@ class Timer;
class TimerTask : public RefCounted {
friend class Timer;
+ friend class TimerTaskCallbackScope;
friend bool operator<(const boost::intrusive_ptr<TimerTask>&,
const boost::intrusive_ptr<TimerTask>&);
@@ -47,9 +48,11 @@ class TimerTask : public RefCounted {
AbsTime sortTime;
Duration period;
AbsTime nextFireTime;
- Mutex callbackLock;
- volatile bool cancelled;
+ qpid::sys::Monitor stateMonitor;
+ enum {WAITING, CALLING, CANCELLED} state;
+ bool prepareToFire();
+ void finishFiring();
bool readyToFire() const;
void fireTask();
diff --git a/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp b/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp
index 249b769051..29b91f3e7a 100644
--- a/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp
+++ b/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/cpp/src/qpid/sys/posix/LockFile.cpp b/cpp/src/qpid/sys/posix/LockFile.cpp
index c1f1c37b47..9fdf83f1bd 100755
--- a/cpp/src/qpid/sys/posix/LockFile.cpp
+++ b/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/cpp/src/qpid/sys/posix/MemStat.cpp b/cpp/src/qpid/sys/posix/MemStat.cpp
index 72c53e5886..2fbf119cab 100644
--- a/cpp/src/qpid/sys/posix/MemStat.cpp
+++ b/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/cpp/src/qpid/sys/posix/SocketAddress.cpp b/cpp/src/qpid/sys/posix/SocketAddress.cpp
index a7049c1851..344bd28669 100644
--- a/cpp/src/qpid/sys/posix/SocketAddress.cpp
+++ b/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/cpp/src/qpid/sys/posix/SystemInfo.cpp b/cpp/src/qpid/sys/posix/SystemInfo.cpp
index 540cc8bc91..2b1bbb97df 100755
--- a/cpp/src/qpid/sys/posix/SystemInfo.cpp
+++ b/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
@@ -33,6 +34,7 @@
#include <fstream>
#include <sstream>
#include <netdb.h>
+#include <string.h>
#ifndef HOST_NAME_MAX
# define HOST_NAME_MAX 256
@@ -59,48 +61,100 @@ 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) {
+ ::addrinfo hints;
+ ::memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC; // Allow both IPv4 and IPv6
+ if (::getaddrinfo(host.c_str(), NULL, &hints, &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/cpp/src/qpid/sys/ssl/SslHandler.cpp b/cpp/src/qpid/sys/ssl/SslHandler.cpp
index 67bf4ea893..8613059f28 100644
--- a/cpp/src/qpid/sys/ssl/SslHandler.cpp
+++ b/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/cpp/src/qpid/sys/ssl/SslHandler.h b/cpp/src/qpid/sys/ssl/SslHandler.h
index 400fa317fd..74df2b7fb0 100644
--- a/cpp/src/qpid/sys/ssl/SslHandler.h
+++ b/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/cpp/src/qpid/sys/ssl/SslIo.cpp b/cpp/src/qpid/sys/ssl/SslIo.cpp
index 2a7cf16923..789c205ead 100644
--- a/cpp/src/qpid/sys/ssl/SslIo.cpp
+++ b/cpp/src/qpid/sys/ssl/SslIo.cpp
@@ -257,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
*/
diff --git a/cpp/src/qpid/sys/ssl/SslIo.h b/cpp/src/qpid/sys/ssl/SslIo.h
index c980d73831..b795594cd9 100644
--- a/cpp/src/qpid/sys/ssl/SslIo.h
+++ b/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/cpp/src/qpid/sys/unordered_map.h b/cpp/src/qpid/sys/unordered_map.h
index 5f7f9567c5..7ae71c3daa 100644
--- a/cpp/src/qpid/sys/unordered_map.h
+++ b/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/cpp/src/qpid/sys/windows/IocpPoller.cpp b/cpp/src/qpid/sys/windows/IocpPoller.cpp
index 1805dd2cd8..c81cef87b0 100755
--- a/cpp/src/qpid/sys/windows/IocpPoller.cpp
+++ b/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/cpp/src/qpid/sys/windows/Socket.cpp b/cpp/src/qpid/sys/windows/Socket.cpp
index b085f67539..a4374260cc 100644
--- a/cpp/src/qpid/sys/windows/Socket.cpp
+++ b/cpp/src/qpid/sys/windows/Socket.cpp
@@ -266,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/cpp/src/qpid/sys/windows/SystemInfo.cpp b/cpp/src/qpid/sys/windows/SystemInfo.cpp
index 4da440bdd4..cef78dcc60 100755
--- a/cpp/src/qpid/sys/windows/SystemInfo.cpp
+++ b/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/cpp/src/qpid/types/Variant.cpp b/cpp/src/qpid/types/Variant.cpp
index 6af06ede5d..d332fffa5e 100644
--- a/cpp/src/qpid/types/Variant.cpp
+++ b/cpp/src/qpid/types/Variant.cpp
@@ -103,31 +103,27 @@ class VariantImpl
int64_t i64;
float f;
double d;
- void* v;//variable width data
+ Uuid* uuid;
+ Variant::Map* map;
+ Variant::List* list;
+ std::string* string;
} value;
std::string encoding;//optional encoding for variable length data
template<class T> T convertFromString() const
{
- std::string* s = reinterpret_cast<std::string*>(value.v);
- if (std::numeric_limits<T>::is_signed || s->find('-') != 0) {
+ const std::string& s = *value.string;
+ try {
+ T r = boost::lexical_cast<T>(s);
//lexical_cast won't fail if string is a negative number and T is unsigned
- try {
- return boost::lexical_cast<T>(*s);
- } catch(const boost::bad_lexical_cast&) {
- //don't return, throw exception below
- }
- } else {
- //T is unsigned and number starts with '-'
- try {
- //handle special case of negative zero
- if (boost::lexical_cast<int>(*s) == 0) return 0;
- //else its a non-zero negative number so throw exception at end of function
- } catch(const boost::bad_lexical_cast&) {
- //wasn't a valid int, therefore not a valid uint
+ //So check that and allow special case of negative zero
+ //else its a non-zero negative number so throw exception at end of function
+ if (std::numeric_limits<T>::is_signed || s.find('-') != 0 || r == 0) {
+ return r;
}
+ } catch(const boost::bad_lexical_cast&) {
}
- throw InvalidConversion(QPID_MSG("Cannot convert " << *s));
+ throw InvalidConversion(QPID_MSG("Cannot convert " << s));
}
};
@@ -145,24 +141,24 @@ VariantImpl::VariantImpl(int64_t i) : type(VAR_INT64) { value.i64 = i; }
VariantImpl::VariantImpl(float f) : type(VAR_FLOAT) { value.f = f; }
VariantImpl::VariantImpl(double d) : type(VAR_DOUBLE) { value.d = d; }
VariantImpl::VariantImpl(const std::string& s, const std::string& e)
- : type(VAR_STRING), encoding(e) { value.v = new std::string(s); }
-VariantImpl::VariantImpl(const Variant::Map& m) : type(VAR_MAP) { value.v = new Variant::Map(m); }
-VariantImpl::VariantImpl(const Variant::List& l) : type(VAR_LIST) { value.v = new Variant::List(l); }
-VariantImpl::VariantImpl(const Uuid& u) : type(VAR_UUID) { value.v = new Uuid(u); }
+ : type(VAR_STRING), encoding(e) { value.string = new std::string(s); }
+VariantImpl::VariantImpl(const Variant::Map& m) : type(VAR_MAP) { value.map = new Variant::Map(m); }
+VariantImpl::VariantImpl(const Variant::List& l) : type(VAR_LIST) { value.list = new Variant::List(l); }
+VariantImpl::VariantImpl(const Uuid& u) : type(VAR_UUID) { value.uuid = new Uuid(u); }
VariantImpl::~VariantImpl() {
switch (type) {
case VAR_STRING:
- delete reinterpret_cast<std::string*>(value.v);
+ delete value.string;
break;
case VAR_MAP:
- delete reinterpret_cast<Variant::Map*>(value.v);
+ delete value.map;
break;
case VAR_LIST:
- delete reinterpret_cast<Variant::List*>(value.v);
+ delete value.list;
break;
case VAR_UUID:
- delete reinterpret_cast<Uuid*>(value.v);
+ delete value.uuid;
break;
default:
break;
@@ -221,7 +217,7 @@ bool VariantImpl::asBool() const
case VAR_INT16: return value.i16;
case VAR_INT32: return value.i32;
case VAR_INT64: return value.i64;
- case VAR_STRING: return toBool(*reinterpret_cast<std::string*>(value.v));
+ case VAR_STRING: return toBool(*value.string);
default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_BOOL)));
}
}
@@ -500,8 +496,8 @@ std::string VariantImpl::asString() const
case VAR_INT64: return boost::lexical_cast<std::string>(value.i64);
case VAR_DOUBLE: return boost::lexical_cast<std::string>(value.d);
case VAR_FLOAT: return boost::lexical_cast<std::string>(value.f);
- case VAR_STRING: return *reinterpret_cast<std::string*>(value.v);
- case VAR_UUID: return reinterpret_cast<Uuid*>(value.v)->str();
+ case VAR_STRING: return *value.string;
+ case VAR_UUID: return value.uuid->str();
case VAR_LIST: return toString(asList());
case VAR_MAP: return toString(asMap());
default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_STRING)));
@@ -510,7 +506,7 @@ std::string VariantImpl::asString() const
Uuid VariantImpl::asUuid() const
{
switch(type) {
- case VAR_UUID: return *reinterpret_cast<Uuid*>(value.v);
+ case VAR_UUID: return *value.uuid;
default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_UUID)));
}
}
@@ -531,10 +527,8 @@ bool VariantImpl::isEqualTo(VariantImpl& other) const
case VAR_INT64: return value.i64 == other.value.i64;
case VAR_DOUBLE: return value.d == other.value.d;
case VAR_FLOAT: return value.f == other.value.f;
- case VAR_STRING: return *reinterpret_cast<std::string*>(value.v)
- == *reinterpret_cast<std::string*>(other.value.v);
- case VAR_UUID: return *reinterpret_cast<Uuid*>(value.v)
- == *reinterpret_cast<Uuid*>(other.value.v);
+ case VAR_STRING: return *value.string == *other.value.string;
+ case VAR_UUID: return *value.uuid == *other.value.uuid;
case VAR_LIST: return equal(asList(), other.asList());
case VAR_MAP: return equal(asMap(), other.asMap());
}
@@ -545,7 +539,7 @@ bool VariantImpl::isEqualTo(VariantImpl& other) const
const Variant::Map& VariantImpl::asMap() const
{
switch(type) {
- case VAR_MAP: return *reinterpret_cast<Variant::Map*>(value.v);
+ case VAR_MAP: return *value.map;
default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_MAP)));
}
}
@@ -553,7 +547,7 @@ const Variant::Map& VariantImpl::asMap() const
Variant::Map& VariantImpl::asMap()
{
switch(type) {
- case VAR_MAP: return *reinterpret_cast<Variant::Map*>(value.v);
+ case VAR_MAP: return *value.map;
default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_MAP)));
}
}
@@ -561,7 +555,7 @@ Variant::Map& VariantImpl::asMap()
const Variant::List& VariantImpl::asList() const
{
switch(type) {
- case VAR_LIST: return *reinterpret_cast<Variant::List*>(value.v);
+ case VAR_LIST: return *value.list;
default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_LIST)));
}
}
@@ -569,7 +563,7 @@ const Variant::List& VariantImpl::asList() const
Variant::List& VariantImpl::asList()
{
switch(type) {
- case VAR_LIST: return *reinterpret_cast<Variant::List*>(value.v);
+ case VAR_LIST: return *value.list;
default: throw InvalidConversion(QPID_MSG("Cannot convert from " << getTypeName(type) << " to " << getTypeName(VAR_LIST)));
}
}
@@ -577,7 +571,7 @@ Variant::List& VariantImpl::asList()
std::string& VariantImpl::getString()
{
switch(type) {
- case VAR_STRING: return *reinterpret_cast<std::string*>(value.v);
+ case VAR_STRING: return *value.string;
default: throw InvalidConversion(QPID_MSG("Variant is not a string; use asString() if conversion is required."));
}
}
@@ -585,7 +579,7 @@ std::string& VariantImpl::getString()
const std::string& VariantImpl::getString() const
{
switch(type) {
- case VAR_STRING: return *reinterpret_cast<std::string*>(value.v);
+ case VAR_STRING: return *value.string;
default: throw InvalidConversion(QPID_MSG("Variant is not a string; use asString() if conversion is required."));
}
}
diff --git a/cpp/src/qpid/xml/XmlExchange.cpp b/cpp/src/qpid/xml/XmlExchange.cpp
index 01770e22a6..3fb11394d0 100644
--- a/cpp/src/qpid/xml/XmlExchange.cpp
+++ b/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/cpp/src/qpid/xml/XmlExchange.h b/cpp/src/qpid/xml/XmlExchange.h
index 9ef389d9bf..1d4723f9c4 100644
--- a/cpp/src/qpid/xml/XmlExchange.h
+++ b/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/cpp/src/tests/CMakeLists.txt b/cpp/src/tests/CMakeLists.txt
index 637442e128..29dfe3634f 100644
--- a/cpp/src/tests/CMakeLists.txt
+++ b/cpp/src/tests/CMakeLists.txt
@@ -149,6 +149,7 @@ set(unit_tests_to_build
PollableCondition
Variant
ClientMessage
+ SystemInfo
${xml_tests}
CACHE STRING "Which unit tests to build"
)
diff --git a/cpp/src/tests/ExchangeTest.cpp b/cpp/src/tests/ExchangeTest.cpp
index 2fb284741a..66a16b9178 100644
--- a/cpp/src/tests/ExchangeTest.cpp
+++ b/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/cpp/src/tests/FieldTable.cpp b/cpp/src/tests/FieldTable.cpp
index c79d110ae4..8aeeb031b3 100644
--- a/cpp/src/tests/FieldTable.cpp
+++ b/cpp/src/tests/FieldTable.cpp
@@ -20,6 +20,7 @@
*/
#include <iostream>
#include <algorithm>
+#include "qpid/sys/alloca.h"
#include "qpid/framing/Array.h"
#include "qpid/framing/FieldTable.h"
#include "qpid/framing/FieldValue.h"
@@ -29,6 +30,8 @@
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"));
diff --git a/cpp/src/tests/Makefile.am b/cpp/src/tests/Makefile.am
index 66d2cdd5d5..cdc7429f3b 100644
--- a/cpp/src/tests/Makefile.am
+++ b/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
@@ -150,7 +151,7 @@ endif
# Test programs that are installed and therefore built as part of make, not make check
qpidexectest_SCRIPTS += qpid-cpp-benchmark qpid-cluster-benchmark install_env.sh
-EXTRA_DIST += qpid-cpp-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
@@ -352,7 +353,8 @@ 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
diff --git a/cpp/src/tests/MessageBuilderTest.cpp b/cpp/src/tests/MessageBuilderTest.cpp
index c3d40ed88a..9adb133d40 100644
--- a/cpp/src/tests/MessageBuilderTest.cpp
+++ b/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/cpp/src/tests/MessageTest.cpp b/cpp/src/tests/MessageTest.cpp
index 7d67c92b37..3a3ed061f9 100644
--- a/cpp/src/tests/MessageTest.cpp
+++ b/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/cpp/src/tests/MessageUtils.h b/cpp/src/tests/MessageUtils.h
index a1b140d484..991e2a2714 100644
--- a/cpp/src/tests/MessageUtils.h
+++ b/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/cpp/src/tests/MessagingSessionTests.cpp b/cpp/src/tests/MessagingSessionTests.cpp
index 968d55fd45..c8ee3aa401 100644
--- a/cpp/src/tests/MessagingSessionTests.cpp
+++ b/cpp/src/tests/MessagingSessionTests.cpp
@@ -1146,6 +1146,24 @@ QPID_AUTO_TEST_CASE(testLargeRoutingKey)
BOOST_CHECK_THROW(fix.session.createReceiver(address), qpid::messaging::MessagingException);
}
+QPID_AUTO_TEST_CASE(testAlternateExchangeInLinkDeclare)
+{
+ MessagingFixture fix;
+ Sender s = fix.session.createSender("amq.direct/key");
+ Receiver r1 = fix.session.createReceiver("amq.direct/key;{link:{x-declare:{alternate-exchange:'amq.fanout'}}}");
+ Receiver r2 = fix.session.createReceiver("amq.fanout");
+
+ for (uint i = 0; i < 10; ++i) {
+ s.send(Message((boost::format("Message_%1%") % (i+1)).str()), true);
+ }
+ r1.close();//orphans all messages in subscription queue, which should then be routed through alternate exchange
+ for (uint i = 0; i < 10; ++i) {
+ Message received;
+ BOOST_CHECK(r2.fetch(received, Duration::SECOND));
+ BOOST_CHECK_EQUAL(received.getContent(), (boost::format("Message_%1%") % (i+1)).str());
+ }
+}
+
QPID_AUTO_TEST_SUITE_END()
}} // namespace qpid::tests
diff --git a/cpp/src/tests/QueueTest.cpp b/cpp/src/tests/QueueTest.cpp
index fb429ca981..3b4f74620f 100644
--- a/cpp/src/tests/QueueTest.cpp
+++ b/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, (MessageStore*)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, (MessageStore*)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/cpp/src/tests/RangeSet.cpp b/cpp/src/tests/RangeSet.cpp
index db3a964086..285f432bf7 100644
--- a/cpp/src/tests/RangeSet.cpp
+++ b/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/cpp/src/tests/ReplicationTest.cpp b/cpp/src/tests/ReplicationTest.cpp
index 1219a6b59e..055f06579f 100644
--- a/cpp/src/tests/ReplicationTest.cpp
+++ b/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/cpp/src/tests/SystemInfo.cpp b/cpp/src/tests/SystemInfo.cpp
new file mode 100644
index 0000000000..12d8d3dba8
--- /dev/null
+++ b/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/cpp/src/tests/TestMessageStore.h b/cpp/src/tests/TestMessageStore.h
index 20e0b755b2..0b63bc9c15 100644
--- a/cpp/src/tests/TestMessageStore.h
+++ b/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/cpp/src/tests/TimerTest.cpp b/cpp/src/tests/TimerTest.cpp
index 6a0a196f4e..fc5004dcb0 100644
--- a/cpp/src/tests/TimerTest.cpp
+++ b/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/cpp/src/tests/TopicExchangeTest.cpp b/cpp/src/tests/TopicExchangeTest.cpp
index ff8931f9c9..d57951ea3f 100644
--- a/cpp/src/tests/TopicExchangeTest.cpp
+++ b/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/cpp/src/tests/TxPublishTest.cpp b/cpp/src/tests/TxPublishTest.cpp
index 152581e4ba..a636646035 100644
--- a/cpp/src/tests/TxPublishTest.cpp
+++ b/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/cpp/src/tests/Uuid.cpp b/cpp/src/tests/Uuid.cpp
index f85a297adc..aa9580e25e 100644
--- a/cpp/src/tests/Uuid.cpp
+++ b/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"
@@ -52,6 +51,11 @@ boost::array<uint8_t, 16> sample = {{0x1b, 0x4e, 0x28, 0xba, 0x2f, 0xa1, 0x11,
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/cpp/src/tests/acl.py b/cpp/src/tests/acl.py
index 720b3b4216..0e096a6f5b 100755
--- a/cpp/src/tests/acl.py
+++ b/cpp/src/tests/acl.py
@@ -285,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
@@ -297,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):
@@ -1509,6 +1537,124 @@ class ACLTests(TestBase010):
#=====================================
+ # QMF Topic Exchange tests
+ #=====================================
+
+ def test_qmf_topic_exchange_tests(self):
+ """
+ Test using QMF method hooks into ACL logic
+ """
+ aclf = self.get_acl_file()
+ 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 uPlain1@COMPANY publish exchange name=X routingkey=ab.cd.e\n')
+ aclf.write('acl allow-log uPlain2@COMPANY publish exchange name=X routingkey=.\n')
+ aclf.write('acl allow-log uStar1@COMPANY publish exchange name=X routingkey=a.*.b\n')
+ aclf.write('acl allow-log uStar2@COMPANY publish exchange name=X routingkey=*.x\n')
+ aclf.write('acl allow-log uStar3@COMPANY publish exchange name=X routingkey=x.x.*\n')
+ aclf.write('acl allow-log uHash1@COMPANY publish exchange name=X routingkey=a.#.b\n')
+ aclf.write('acl allow-log uHash2@COMPANY publish exchange name=X routingkey=a.#\n')
+ aclf.write('acl allow-log uHash3@COMPANY publish exchange name=X routingkey=#.a\n')
+ aclf.write('acl allow-log uHash4@COMPANY publish exchange name=X routingkey=a.#.b.#.c\n')
+ aclf.write('acl allow-log uMixed1@COMPANY publish exchange name=X routingkey=*.x.#.y\n')
+ aclf.write('acl allow-log uMixed2@COMPANY publish exchange name=X routingkey=a.#.b.*\n')
+ aclf.write('acl allow-log uMixed3@COMPANY publish exchange name=X routingkey=*.*.*.#\n')
+
+ aclf.write('acl allow-log all publish exchange name=X routingkey=MN.OP.Q\n')
+ aclf.write('acl allow-log all publish exchange name=X routingkey=M.*.N\n')
+ aclf.write('acl allow-log all publish exchange name=X routingkey=M.#.N\n')
+ aclf.write('acl allow-log all publish exchange name=X routingkey=*.M.#.N\n')
+
+ aclf.write('acl deny-log all all\n')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result):
+ self.fail(result)
+
+ # aclKey: "ab.cd.e"
+ self.LookupPublish("uPlain1@COMPANY", "X", "ab.cd.e", "allow-log")
+ self.LookupPublish("uPlain1@COMPANY", "X", "abx.cd.e", "deny-log")
+ self.LookupPublish("uPlain1@COMPANY", "X", "ab.cd", "deny-log")
+ self.LookupPublish("uPlain1@COMPANY", "X", "ab.cd..e.", "deny-log")
+ self.LookupPublish("uPlain1@COMPANY", "X", "ab.cd.e.", "deny-log")
+ self.LookupPublish("uPlain1@COMPANY", "X", ".ab.cd.e", "deny-log")
+ # aclKey: "."
+ self.LookupPublish("uPlain2@COMPANY", "X", ".", "allow-log")
+
+ # aclKey: "a.*.b"
+ self.LookupPublish("uStar1@COMPANY", "X", "a.xx.b", "allow-log")
+ self.LookupPublish("uStar1@COMPANY", "X", "a.b", "deny-log")
+ # aclKey: "*.x"
+ self.LookupPublish("uStar2@COMPANY", "X", "y.x", "allow-log")
+ self.LookupPublish("uStar2@COMPANY", "X", ".x", "allow-log")
+ self.LookupPublish("uStar2@COMPANY", "X", "x", "deny-log")
+ # aclKey: "x.x.*"
+ self.LookupPublish("uStar3@COMPANY", "X", "x.x.y", "allow-log")
+ self.LookupPublish("uStar3@COMPANY", "X", "x.x.", "allow-log")
+ self.LookupPublish("uStar3@COMPANY", "X", "x.x", "deny-log")
+ self.LookupPublish("uStar3@COMPANY", "X", "q.x.y", "deny-log")
+
+ # aclKey: "a.#.b"
+ self.LookupPublish("uHash1@COMPANY", "X", "a.b", "allow-log")
+ self.LookupPublish("uHash1@COMPANY", "X", "a.x.b", "allow-log")
+ self.LookupPublish("uHash1@COMPANY", "X", "a..x.y.zz.b", "allow-log")
+ self.LookupPublish("uHash1@COMPANY", "X", "a.b.", "deny-log")
+ self.LookupPublish("uHash1@COMPANY", "X", "q.x.b", "deny-log")
+
+ # aclKey: "a.#"
+ self.LookupPublish("uHash2@COMPANY", "X", "a", "allow-log")
+ self.LookupPublish("uHash2@COMPANY", "X", "a.b", "allow-log")
+ self.LookupPublish("uHash2@COMPANY", "X", "a.b.c", "allow-log")
+
+ # aclKey: "#.a"
+ self.LookupPublish("uHash3@COMPANY", "X", "a", "allow-log")
+ self.LookupPublish("uHash3@COMPANY", "X", "x.y.a", "allow-log")
+
+ # aclKey: "a.#.b.#.c"
+ self.LookupPublish("uHash4@COMPANY", "X", "a.b.c", "allow-log")
+ self.LookupPublish("uHash4@COMPANY", "X", "a.x.b.y.c", "allow-log")
+ self.LookupPublish("uHash4@COMPANY", "X", "a.x.x.b.y.y.c", "allow-log")
+
+ # aclKey: "*.x.#.y"
+ self.LookupPublish("uMixed1@COMPANY", "X", "a.x.y", "allow-log")
+ self.LookupPublish("uMixed1@COMPANY", "X", "a.x.p.qq.y", "allow-log")
+ self.LookupPublish("uMixed1@COMPANY", "X", "a.a.x.y", "deny-log")
+ self.LookupPublish("uMixed1@COMPANY", "X", "aa.x.b.c", "deny-log")
+
+ # aclKey: "a.#.b.*"
+ self.LookupPublish("uMixed2@COMPANY", "X", "a.b.x", "allow-log")
+ self.LookupPublish("uMixed2@COMPANY", "X", "a.x.x.x.b.x", "allow-log")
+
+ # aclKey: "*.*.*.#"
+ self.LookupPublish("uMixed3@COMPANY", "X", "x.y.z", "allow-log")
+ self.LookupPublish("uMixed3@COMPANY", "X", "x.y.z.a.b.c", "allow-log")
+ self.LookupPublish("uMixed3@COMPANY", "X", "x.y", "deny-log")
+ self.LookupPublish("uMixed3@COMPANY", "X", "x", "deny-log")
+
+ # Repeat the keys with wildcard user spec
+ self.LookupPublish("uPlain1@COMPANY", "X", "MN.OP.Q", "allow-log")
+ self.LookupPublish("uStar1@COMPANY" , "X", "M.xx.N", "allow-log")
+ self.LookupPublish("uHash1@COMPANY" , "X", "M.N", "allow-log")
+ self.LookupPublish("uHash1@COMPANY" , "X", "M.x.N", "allow-log")
+ self.LookupPublish("uHash1@COMPANY" , "X", "M..x.y.zz.N", "allow-log")
+ self.LookupPublish("uMixed1@COMPANY", "X", "a.M.N", "allow-log")
+ self.LookupPublish("uMixed1@COMPANY", "X", "a.M.p.qq.N", "allow-log")
+
+ self.LookupPublish("dev@QPID", "X", "MN.OP.Q", "allow-log")
+ self.LookupPublish("dev@QPID", "X", "M.xx.N", "allow-log")
+ self.LookupPublish("dev@QPID", "X", "M.N", "allow-log")
+ self.LookupPublish("dev@QPID", "X", "M.x.N", "allow-log")
+ self.LookupPublish("dev@QPID", "X", "M..x.y.zz.N", "allow-log")
+ self.LookupPublish("dev@QPID", "X", "a.M.N", "allow-log")
+ self.LookupPublish("dev@QPID", "X", "a.M.p.qq.N", "allow-log")
+
+ #=====================================
# Connection limits
#=====================================
diff --git a/cpp/src/tests/asyncstore.cmake b/cpp/src/tests/asyncstore.cmake
index 795ea55cf7..51efa88bd3 100644
--- a/cpp/src/tests/asyncstore.cmake
+++ b/cpp/src/tests/asyncstore.cmake
@@ -45,7 +45,7 @@ if (UNIX)
qpidbroker
rt
)
- add_test (Store_Perftools_Smoke_Test ${CMAKE_CURRENT_SOURCE_DIR}/storePerftools/storePerftoolsSmokeTest.sh)
+ add_test (jrnl2Perf_smoke_test ${CMAKE_CURRENT_SOURCE_DIR}/storePerftools/jrnl2Perf_smoke_test.sh)
endif (UNIX)
# Async store perf test (asyncPerf)
@@ -77,4 +77,5 @@ if (UNIX)
qpidtypes
rt
)
+ add_test (asyncStorePerf_smoke_test ${CMAKE_CURRENT_SOURCE_DIR}/storePerftools/asyncStorePerf_smoke_test.sh)
endif (UNIX)
diff --git a/cpp/src/tests/brokertest.py b/cpp/src/tests/brokertest.py
index 8255fbe9ac..aea4460e5a 100644
--- a/cpp/src/tests/brokertest.py
+++ b/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):
@@ -572,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"
@@ -647,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())
@@ -657,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/cpp/src/tests/cluster_test_logs.py b/cpp/src/tests/cluster_test_logs.py
index 003d82c619..22f2470590 100755
--- a/cpp/src/tests/cluster_test_logs.py
+++ b/cpp/src/tests/cluster_test_logs.py
@@ -66,7 +66,8 @@ def filter_log(log):
'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
- "org.apache.qpid.broker:bridge:" # ignore bridge index
+ "org.apache.qpid.broker:bridge:", # ignore bridge index
+ "closed connection"
])
# 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/cpp/src/tests/cluster_tests.py b/cpp/src/tests/cluster_tests.py
index 8952f5de7b..3c96b252df 100755
--- a/cpp/src/tests/cluster_tests.py
+++ b/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.
@@ -768,6 +780,35 @@ acl deny all all
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.
@@ -778,38 +819,6 @@ acl deny all all
cluster to newly-added members
"""
- TIMEOUT = 30
- def verify(src_broker, src, dst_broker, dst, timeout=TIMEOUT):
- """ Prove that traffic can pass from source fed broker to
- destination fed broker
- """
- 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()
- self.assertTrue(active, "Bridge failed to become active")
-
-
# 2 node cluster source, 2 node cluster destination
src_cluster = self.cluster(2, expect=EXPECT_EXIT_FAIL)
src_cluster.ready();
@@ -848,43 +857,145 @@ acl deny all all
self.assertEqual(result.status, 0, result)
# check that traffic passes
- verify(src_cluster[0], "srcQ", dst_cluster[0], "destQ")
+ 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();
- verify(src_cluster[2], "srcQ", dst_cluster[0], "destQ")
+ 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()
- verify(src_cluster[1], "srcQ", dst_cluster[0], "destQ")
+ 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()
- verify(src_cluster[2], "srcQ", dst_cluster[0], "destQ")
+ 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()
- verify(src_cluster[2], "srcQ", dst_cluster[1], "destQ")
+ 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()
- verify(src_cluster[2], "srcQ", dst_cluster[1], "destQ")
+ 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()
- verify(src_cluster[2], "srcQ", dst_cluster[2], "destQ")
+ 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/cpp/src/tests/federation.py b/cpp/src/tests/federation.py
index 7d613b98ce..dcd074eda9 100755
--- a/cpp/src/tests/federation.py
+++ b/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/cpp/src/tests/ha_tests.py b/cpp/src/tests/ha_tests.py
index 827cb7dca9..d25281eed5 100755
--- a/cpp/src/tests/ha_tests.py
+++ b/cpp/src/tests/ha_tests.py
@@ -18,59 +18,123 @@
# under the License.
#
-import os, signal, sys, time, imp, re, subprocess, glob, random, logging, shutil, math
-from qpid.messaging import Message, NotFound, ConnectionError, ReceiverError, Connection
+import os, signal, sys, time, imp, re, subprocess, glob, random, logging, shutil, math, unittest
+import traceback
+from qpid.messaging import Message, NotFound, ConnectionError, ReceiverError, Connection, Timeout, Disposition, REJECTED
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(__name__)
+class QmfAgent(object):
+ """Access to a QMF broker agent."""
+ def __init__(self, address, **kwargs):
+ self._connection = Connection.establish(
+ address, client_properties={"qpid.ha-admin":1}, **kwargs)
+ 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 Credentials(object):
+ """SASL credentials: username, password, and mechanism"""
+ def __init__(self, username, password, mechanism):
+ (self.username, self.password, self.mechanism) = (username, password, mechanism)
+
+ def __str__(self): return "Credentials%s"%(self.tuple(),)
+
+ def tuple(self): return (self.username, self.password, self.mechanism)
+
+ def add_user(self, url): return "%s/%s@%s"%(self.username, self.password, url)
+
class HaBroker(Broker):
- def __init__(self, test, args=[], broker_url=None, ha_cluster=True,
- ha_replicate="all", **kwargs):
+ """Start a broker with HA enabled
+ @param client_cred: (user, password, mechanism) for admin clients started by the HaBroker.
+ """
+ def __init__(self, test, args=[], brokers_url=None, ha_cluster=True, ha_replicate="all",
+ client_credentials=None, **kwargs):
assert BrokerTest.ha_lib, "Cannot locate HA plug-in"
args = copy(args)
args += ["--load-module", BrokerTest.ha_lib,
- "--log-enable=info+", "--log-enable=debug+:ha::",
+ "--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 broker_url: args.extend([ "--ha-brokers", broker_url ])
+ 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.
-
- def promote(self):
- assert os.system("%s/qpid-ha promote -b %s"%(self.commands, self.host_port())) == 0
-
- 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 set_broker_url(self, url):
- assert os.system(
- "%s/qpid-ha set --brokers=%s -b %s"%(self.commands, url, self.host_port())) == 0
-
- 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
+ self.qpid_ha_script=import_script(self.qpid_ha_path)
+ self._agent = None
+ self.client_credentials = client_credentials
+
+ def __str__(self): return Broker.__str__(self)
+
+ def qpid_ha(self, args):
+ cred = self.client_credentials
+ url = self.host_port()
+ if cred:
+ url =cred.add_user(url)
+ args = args + ["--sasl-mechanism", cred.mechanism]
+ self.qpid_ha_script.main_except(["", "-b", url]+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:
+ cred = self.client_credentials
+ if cred:
+ self._agent = QmfAgent(cred.add_user(self.host_port()), sasl_mechanisms=cred.mechanism)
+ else:
+ 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 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
+ self.qpid_config(["add", "queue", "--start-replica", from_broker, queue])
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
+ self.qpid_config(["add", "queue", queue, "--replicate", replication])
def connect_admin(self, **kwargs):
- return Broker.connect(self, client_properties={"qpid.ha-admin":1}, **kwargs)
+ cred = self.client_credentials
+ if cred:
+ return Broker.connect(
+ self, client_properties={"qpid.ha-admin":1},
+ username=cred.username, password=cred.password, sasl_mechanisms=cred.mechanism,
+ **kwargs)
+ else:
+ return Broker.connect(self, client_properties={"qpid.ha-admin":1}, **kwargs)
def wait_backup(self, address):
"""Wait for address to become valid on a backup broker."""
@@ -78,6 +142,14 @@ class HaBroker(Broker):
try: wait_address(bs, address)
finally: bs.connection.close()
+ 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 assert_browse_backup(self, queue, expected, **kwargs):
"""Combines wait_backup and assert_browse_retry."""
bs = self.connect_admin().session()
@@ -86,33 +158,70 @@ class HaBroker(Broker):
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
- def __init__(self, test, n, **kwargs):
+ def __init__(self, test, n, promote=True, **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
- self.url = ",".join([b.host_port() for b in self])
- for b in self: b.set_broker_url(self.url)
+ 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, args=[]):
+ """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])
+ if len(self) > 1: # No failover addresses on a 1 cluster.
+ 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 port, 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)
@@ -128,12 +237,12 @@ def wait_address(session, address):
except NotFound: return False
assert retry(check), "Timed out waiting for address %s"%(address)
-def assert_missing(session, address):
- """Assert that the address is _not_ valid"""
+def valid_address(session, address):
+ """Test if an address is valid"""
try:
session.receiver(address)
- self.fail("Expected NotFound: %s"%(address))
- except NotFound: pass
+ return True
+ except NotFound: return False
class ReplicationTests(BrokerTest):
"""Correctness tests for HA replication."""
@@ -180,7 +289,7 @@ class ReplicationTests(BrokerTest):
self.assert_browse_retry(b, prefix+"q1", ["1", "4"])
self.assert_browse_retry(b, prefix+"q2", []) # configuration only
- 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,7 +304,7 @@ class ReplicationTests(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)
@@ -233,10 +342,10 @@ class ReplicationTests(BrokerTest):
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()
@@ -276,7 +385,7 @@ class ReplicationTests(BrokerTest):
"""Verify that backups rejects connections and that fail-over works in python 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())
# Check that backup rejects normal connections
try:
backup.connect().session()
@@ -294,14 +403,15 @@ class ReplicationTests(BrokerTest):
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}")
backup.wait_backup("q")
@@ -344,6 +454,7 @@ class ReplicationTests(BrokerTest):
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)
pc = primary.connect()
ps = pc.session().sender("q;{create:always}")
@@ -393,7 +504,7 @@ class ReplicationTests(BrokerTest):
"""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())
+ 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")]:
@@ -410,7 +521,7 @@ class ReplicationTests(BrokerTest):
"""Test replication with the ring queue policy"""
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())
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)))
backup.assert_browse_backup("q", [str(i) for i in range(5,10)])
@@ -419,18 +530,20 @@ class ReplicationTests(BrokerTest):
"""Test replication with the reject queue policy"""
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())
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
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}}}}")
priorities = [8,9,5,1,2,2,3,4,9,7,8,9,9,2]
@@ -445,7 +558,7 @@ class ReplicationTests(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]
@@ -464,7 +577,7 @@ class ReplicationTests(BrokerTest):
def test_priority_ring(self):
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())
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))
@@ -475,8 +588,10 @@ class ReplicationTests(BrokerTest):
# correct result, the uncommented one is for the actualy buggy
# result. See https://issues.apache.org/jira/browse/QPID-3866
#
- # 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)
+ # expect = sorted(priorities,reverse=True)[0:5]
+ expect = [9,9,9,9,2]
+ primary.assert_browse("q", expect, transform=lambda m: m.priority)
+ backup.assert_browse_backup("q", expect, transform=lambda m: m.priority)
def test_backup_acquired(self):
"""Verify that acquired messages are backed up, for all queue types."""
@@ -509,11 +624,11 @@ class ReplicationTests(BrokerTest):
primary = HaBroker(self, name="primary")
primary.promote()
- backup1 = HaBroker(self, name="backup1", broker_url=primary.host_port())
+ 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", broker_url=primary.host_port())
+ backup2 = HaBroker(self, name="backup2", brokers_url=primary.host_port())
# Wait for backups to catch up.
for t in tests:
t.wait(self, backup1)
@@ -538,11 +653,13 @@ class ReplicationTests(BrokerTest):
self.fail("Excpected no-such-queue exception")
except NotFound: pass
- def test_invalid_default(self):
- """Verify that a queue with an invalid qpid.replicate gets default treatment"""
- cluster = HaCluster(self, 2, ha_replicate="all")
- c = cluster[0].connect().session().sender("q;{create:always, node:{x-declare:{arguments:{'qpid.replicate':XXinvalidXX}}}}")
- cluster[1].wait_backup("q")
+ 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
@@ -559,6 +676,136 @@ class ReplicationTests(BrokerTest):
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 test_auth(self):
+ """Verify that authentication does not interfere with replication."""
+ # FIXME aconway 2012-07-09: generate test sasl config portably for cmake
+ sasl_config=os.path.join(self.rootdir, "sasl_config")
+ if not os.path.exists(sasl_config):
+ print "WARNING: Skipping test, SASL test configuration %s not found."%sasl_config
+ return
+ acl=os.path.join(os.getcwd(), "policy.acl")
+ aclf=file(acl,"w")
+ # Verify that replication works with auth=yes and HA user has at least the following
+ # privileges:
+ aclf.write("""
+acl allow zag@QPID access queue
+acl allow zag@QPID create queue
+acl allow zag@QPID consume queue
+acl allow zag@QPID delete queue
+acl allow zag@QPID access exchange
+acl allow zag@QPID create exchange
+acl allow zag@QPID bind exchange
+acl allow zag@QPID publish exchange
+acl allow zag@QPID delete exchange
+acl allow zag@QPID access method
+acl allow zag@QPID create link
+acl deny all all
+ """)
+ aclf.close()
+ cluster = HaCluster(
+ self, 2,
+ args=["--auth", "yes", "--sasl-config", sasl_config,
+ "--acl-file", acl, "--load-module", os.getenv("ACL_LIB"),
+ "--ha-username=zag", "--ha-password=zag", "--ha-mechanism=PLAIN"
+ ],
+ client_credentials=Credentials("zag", "zag", "PLAIN"))
+ s0 = cluster[0].connect(username="zag", password="zag").session();
+ s0.receiver("q;{create:always}")
+ s0.receiver("ex;{create:always,node:{type:topic,x-declare:{type:'fanout'},x-bindings:[{exchange:'ex',queue:'q'}]}}")
+ cluster[1].wait_backup("q")
+ cluster[1].wait_backup("ex")
+ s1 = cluster[1].connect_admin().session(); # Uses Credentials above.
+ s1.sender("ex").send("foo");
+ self.assertEqual(s1.receiver("q").fetch().content, "foo")
+
+ def test_alternate_exchange(self):
+ """Verify that alternate-exchange on exchanges and queues is propagated
+ to new members of a cluster. """
+ cluster = HaCluster(self, 2)
+ s = cluster[0].connect().session()
+ # altex exchange: acts as alternate exchange
+ s.sender("altex;{create:always,node:{type:topic,x-declare:{type:'fanout'}}}")
+ # altq queue bound to altex, collect re-routed messages.
+ s.sender("altq;{create:always,node:{x-bindings:[{exchange:'altex',queue:altq}]}}")
+ # 0ex exchange with alternate-exchange altex and no queues bound
+ s.sender("0ex;{create:always,node:{type:topic, x-declare:{type:'direct', alternate-exchange:'altex'}}}")
+ # create queue q with alternate-exchange altex
+ s.sender("q;{create:always,node:{type:queue, x-declare:{alternate-exchange:'altex'}}}")
+ # create a bunch of exchanges to ensure we don't clean up prematurely if the
+ # response comes in multiple fragments.
+ for i in xrange(200): s.sender("00ex%s;{create:always,node:{type:topic}}"%i)
+
+ def verify(broker):
+ s = broker.connect().session()
+ # Verify unmatched message goes to ex's alternate.
+ s.sender("0ex").send("foo")
+ altq = s.receiver("altq")
+ self.assertEqual("foo", altq.fetch(timeout=0).content)
+ s.acknowledge()
+ # Verify rejected message goes to q's alternate.
+ s.sender("q").send("bar")
+ msg = s.receiver("q").fetch(timeout=0)
+ self.assertEqual("bar", msg.content)
+ s.acknowledge(msg, Disposition(REJECTED)) # Reject the message
+ self.assertEqual("bar", altq.fetch(timeout=0).content)
+ s.acknowledge()
+
+ # Sanity check: alternate exchanges on original broker
+ verify(cluster[0])
+ # Check backup that was connected during setup.
+ cluster[1].wait_backup("0ex")
+ cluster[1].wait_backup("q")
+ cluster.bounce(0)
+ verify(cluster[1])
+ # Check a newly started backup.
+ cluster.start()
+ cluster[2].wait_backup("0ex")
+ cluster[2].wait_backup("q")
+ cluster.bounce(1)
+ verify(cluster[2])
+
def fairshare(msgs, limit, levels):
"""
Generator to return prioritised messages in expected order for a given fairshare limit
@@ -601,49 +848,135 @@ 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()
- # Wait for sender & receiver to get up and running
- assert retry(lambda: receiver.received > 100)
+ 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()
+
+ def wait_passed(r, n):
+ """Wait for receiver r to pass n"""
+ def check():
+ r.check() # Verify no exceptions
+ return r.received > n
+ assert retry(check), "Stalled %s at %s"%(r.queue, n)
+
+ for r in receivers: wait_passed(r, 0)
+
# 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()
+ checkpoint = [ r.received for r in receivers ]
+ # Don't kill primary till it is active and the next
+ # backup is ready, otherwise we can lose messages.
+ brokers[i%3].wait_status("active")
+ brokers[(i+1)%3].wait_status("ready")
+ brokers.bounce(i%3)
+ i += 1
+ map(wait_passed, receivers, checkpoint) # Wait for all receivers
+ 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."""
+
+ # We don't want backups to time out for this test, set long timeout.
+ cluster = HaCluster(self, 4, args=["--ha-backup-timeout=100000"]);
+ # 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 assertSyncTimeout(s):
+ try:
+ s.sync(timeout=.01)
+ 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)
+ assertSyncTimeout(s1)
+ self.assertEqual(s1.unsettled(), 100)
+ assertSyncTimeout(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)
+ assertSyncTimeout(s1)
+ self.assertEqual(s1.unsettled(), 100)
+ assertSyncTimeout(s2)
+ self.assertEqual(s2.unsettled(), 100)
+ self.assertEqual(cluster[3].ha_status(), "recovering")
+ cluster.restart(2)
+
+ # Verify everything is up to date and active
+ 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()
+
+ def test_expected_backup_timeout(self):
+ """Verify that we time-out expected backups and release held queues
+ after a configured interval. Verify backup is demoted to catch-up,
+ but can still rejoin.
+ """
+ cluster = HaCluster(self, 3, args=["--ha-backup-timeout=0.5"]);
+ cluster[0].wait_status("active") # Primary ready
+ for b in cluster[1:4]: b.wait_status("ready") # Backups ready
+ for i in [0,1]: cluster.kill(i, False)
+ cluster[2].promote() # New primary, backups will be 1 and 2
+ cluster[2].wait_status("recovering")
+ # Should not go active till the expected backup connects or times out.
+ self.assertEqual(cluster[2].ha_status(), "recovering")
+ # Messages should be held expected backup times out
+ s = cluster[2].connect().session().sender("q;{create:always}")
+ for i in xrange(100): s.send(str(i), sync=False)
+ # Verify message held initially.
+ try: s.sync(timeout=.01); self.fail("Expected Timeout exception")
+ except Timeout: pass
+ s.sync(timeout=1) # And released after the timeout.
+ self.assertEqual(cluster[2].ha_status(), "active")
if __name__ == "__main__":
shutil.rmtree("brokertest.tmp", True)
diff --git a/cpp/src/tests/ipv6_test b/cpp/src/tests/ipv6_test
index 6becfd8c96..9d1cb2acdd 100755
--- a/cpp/src/tests/ipv6_test
+++ b/cpp/src/tests/ipv6_test
@@ -19,6 +19,19 @@
# under the License.
#
+# Check whether we have any globally configured IPv6 addresses
+# - if not then we can't run the tests because ipv6 lookups won't
+# work within the qpid code. This is a deliberate feature to avoid
+# getting addresses that can't be routed by the machine.
+
+if ip -f inet6 -o addr | cut -f 9 -s -d' ' | grep global > /dev/null ; then
+ echo "IPv6 addresses configured continuing"
+else
+ echo "No global IPv6 addresses configured - skipping test"
+ exit 0
+fi
+
+
# Run a simple test over IPv6
source ./test_env.sh
diff --git a/cpp/src/tests/logging.cpp b/cpp/src/tests/logging.cpp
index 5d5bb1feef..a29714c002 100644
--- a/cpp/src/tests/logging.cpp
+++ b/cpp/src/tests/logging.cpp
@@ -258,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;
}
@@ -347,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");
@@ -375,11 +375,11 @@ QPID_AUTO_TEST_CASE(testQuoteNonPrintable) {
char s[] = "null\0tab\tspace newline\nret\r\x80\x99\xff";
string str(s, sizeof(s));
- QPID_LOG(critical, str);
+ QPID_LOG_CAT(critical, test, str);
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/cpp/src/tests/ping_broker b/cpp/src/tests/ping_broker
new file mode 100755
index 0000000000..6c391027a3
--- /dev/null
+++ b/cpp/src/tests/ping_broker
@@ -0,0 +1,127 @@
+#!/usr/bin/env python
+
+#
+# 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.
+#
+
+import os
+from optparse import OptionParser, OptionGroup
+import sys
+import locale
+import socket
+import re
+from qpid.messaging import Connection
+
+home = os.environ.get("QPID_TOOLS_HOME", os.path.normpath("/usr/share/qpid-tools"))
+sys.path.append(os.path.join(home, "python"))
+
+from qpidtoollibs import BrokerAgent
+from qpidtoollibs import Display, Header, Sorter, YN, Commas, TimeLong
+
+
+class Config:
+ def __init__(self):
+ self._host = "localhost"
+ self._connTimeout = 10
+
+config = Config()
+conn_options = {}
+
+def OptionsAndArguments(argv):
+ """ Set global variables for options, return arguments """
+
+ global config
+ global conn_options
+
+ usage = "%prog [options]"
+
+ parser = OptionParser(usage=usage)
+
+ parser.add_option("-b", "--broker", action="store", type="string", default="localhost", metavar="<url>",
+ help="URL of the broker to query")
+ parser.add_option("-t", "--timeout", action="store", type="int", default=10, metavar="<secs>",
+ help="Maximum time to wait for broker connection (in seconds)")
+ parser.add_option("--sasl-mechanism", action="store", type="string", metavar="<mech>",
+ help="SASL mechanism for authentication (e.g. EXTERNAL, ANONYMOUS, PLAIN, CRAM-MD, DIGEST-MD5, GSSAPI). SASL automatically picks the most secure available mechanism - use this option to override.")
+ parser.add_option("--ssl-certificate", action="store", type="string", metavar="<cert>", help="Client SSL certificate (PEM Format)")
+ parser.add_option("--ssl-key", action="store", type="string", metavar="<key>", help="Client SSL private key (PEM Format)")
+ parser.add_option("--ha-admin", action="store_true", help="Allow connection to a HA backup broker.")
+
+ opts, args = parser.parse_args(args=argv)
+
+ config._host = opts.broker
+ config._connTimeout = opts.timeout
+
+ if opts.sasl_mechanism:
+ conn_options['sasl_mechanisms'] = opts.sasl_mechanism
+ if opts.ssl_certificate:
+ conn_options['ssl_certfile'] = opts.ssl_certificate
+ if opts.ssl_key:
+ conn_options['ssl_key'] = opts.ssl_key
+ if opts.ha_admin:
+ conn_options['client_properties'] = {'qpid.ha-admin' : 1}
+ return args
+
+class BrokerManager:
+ def __init__(self):
+ self.brokerName = None
+ self.connection = None
+ self.broker = None
+ self.cluster = None
+
+ def SetBroker(self, brokerUrl):
+ self.url = brokerUrl
+ self.connection = Connection.establish(self.url, **conn_options)
+ self.broker = BrokerAgent(self.connection)
+
+ def Disconnect(self):
+ """ Release any allocated brokers. Ignore any failures as the tool is
+ shutting down.
+ """
+ try:
+ connection.close()
+ except:
+ pass
+
+ def Ping(self, args):
+ for sequence in range(10):
+ result = self.broker.echo(sequence, "ECHO BODY")
+ if result['sequence'] != sequence:
+ raise Exception("Invalid Sequence")
+
+
+def main(argv=None):
+
+ args = OptionsAndArguments(argv)
+ bm = BrokerManager()
+
+ try:
+ bm.SetBroker(config._host)
+ bm.Ping(args)
+ bm.Disconnect()
+ return 0
+ except KeyboardInterrupt:
+ print
+ except Exception,e:
+ print "Failed: %s - %s" % (e.__class__.__name__, e)
+
+ bm.Disconnect() # try to deallocate brokers
+ return 1
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/cpp/src/tests/qpid-latency-test.cpp b/cpp/src/tests/qpid-latency-test.cpp
index 20eb4568f3..2343cb1d77 100644
--- a/cpp/src/tests/qpid-latency-test.cpp
+++ b/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/cpp/src/tests/qpid-receive.cpp b/cpp/src/tests/qpid-receive.cpp
index 6deeb566dc..7a02b871db 100644
--- a/cpp/src/tests/qpid-receive.cpp
+++ b/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/cpp/src/tests/run_acl_tests b/cpp/src/tests/run_acl_tests
index 3a8c03eda6..25241ad75e 100755
--- a/cpp/src/tests/run_acl_tests
+++ b/cpp/src/tests/run_acl_tests
@@ -30,9 +30,9 @@ 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 --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 --acl-max-connect-per-ip 2 --log-to-file locali.log > qpiddi.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 --acl-max-connect-per-user 2 --log-to-file localu.log > qpiddu.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`
}
diff --git a/cpp/src/tests/run_federation_tests b/cpp/src/tests/run_federation_tests
index 7735b559cf..c2ee550226 100755
--- a/cpp/src/tests/run_federation_tests
+++ b/cpp/src/tests/run_federation_tests
@@ -36,10 +36,10 @@ 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() {
rm -f fed_local.log fed_remote.log fed_b1.log fed_b2.log
- LOCAL_PORT=$($QPIDD_CMD fed_local.log)
- REMOTE_PORT=$($QPIDD_CMD fed_remote.log)
- REMOTE_B1=$($QPIDD_CMD fed_b1.log)
- REMOTE_B2=$($QPIDD_CMD 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/cpp/src/tests/run_ha_tests b/cpp/src/tests/run_ha_tests
new file mode 100755
index 0000000000..1a469646c9
--- /dev/null
+++ b/cpp/src/tests/run_ha_tests
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+#
+# 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.
+#
+
+
+# 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; }
+
+srcdir=`dirname $0`
+$srcdir/ha_tests.py
+
diff --git a/cpp/src/tests/sasl_test_setup.sh b/cpp/src/tests/sasl_test_setup.sh
index 3e69c0f02b..3947986517 100755
--- a/cpp/src/tests/sasl_test_setup.sh
+++ b/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/cpp/src/tests/ssl_test b/cpp/src/tests/ssl_test
index 91ff8eec1e..19a316a483 100755
--- a/cpp/src/tests/ssl_test
+++ b/cpp/src/tests/ssl_test
@@ -148,6 +148,11 @@ URL=$TEST_HOSTNAME:$PORT
MSG=`./qpid-receive -b $URL --connection-options '{transport:ssl,heartbeat:2}' -a "foo;{create:always}" --messages 1`
test "$MSG" = "hello again" || { echo "receive failed '$MSG' != 'hello again'"; exit 1; }
+## Test using the Python client
+echo "Testing Non-Authenticating with Python Client..."
+URL=amqps://$TEST_HOSTNAME:$PORT
+if `$top_srcdir/src/tests/ping_broker -b $URL`; then echo " Passed"; else { echo " Failed"; exit 1; }; fi
+
#### Client Authentication tests
start_authenticating_broker
diff --git a/cpp/src/tests/storePerftools/storePerftoolsSmokeTest.sh b/cpp/src/tests/storePerftools/asyncStorePerf_smoke_test.sh
index 592e1e60a0..9e8880e128 100755
--- a/cpp/src/tests/storePerftools/storePerftoolsSmokeTest.sh
+++ b/cpp/src/tests/storePerftools/asyncStorePerf_smoke_test.sh
@@ -9,11 +9,16 @@ run_test() {
fi
}
-NUM_MSGS=1000
+NUM_MSGS=10000
TEST_PROG="./asyncStorePerf"
+# Default (no args)
run_test "${TEST_PROG}"
+
+# Help
run_test "${TEST_PROG} --help"
+
+# Limited combinations of major params
for q in 1 2; do
for p in 1 2; do
for c in 1 2; do
@@ -27,25 +32,3 @@ for q in 1 2; do
done
done
done
-
-
-NUM_MSGS=1000
-TEST_PROG="./jrnl2Perf"
-
-
-run_test "${TEST_PROG}"
-
-# This test returns 1, don't use run_test until this is fixed.
-cmd="${TEST_PROG} --help"
-echo $cmd
-$cmd
-
-for q in 1 2; do
- for p in 1 2; do
- for c in 1; do # BUG - this will fail for c > 1
- run_test "./jrnl2Perf --num_queues $q --num_msgs ${NUM_MSGS} --num_enq_threads_per_queue $p --num_deq_threads_per_queue $c"
- done
- done
-done
-
-#exit 0
diff --git a/cpp/src/tests/storePerftools/jrnl2Perf_smoke_test.sh b/cpp/src/tests/storePerftools/jrnl2Perf_smoke_test.sh
new file mode 100755
index 0000000000..23fe0fea9b
--- /dev/null
+++ b/cpp/src/tests/storePerftools/jrnl2Perf_smoke_test.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+run_test() {
+ local cmd=$1
+ echo $cmd
+ $cmd
+ if (( $? != 0 )); then
+ exit 1
+ fi
+}
+
+NUM_MSGS=10000
+TEST_PROG="./jrnl2Perf"
+
+# Default (no args)
+run_test "${TEST_PROG}"
+
+# Help
+# This test returns 1, don't use run_test until this is fixed.
+cmd="${TEST_PROG} --help"
+echo $cmd
+$cmd
+
+# Limited combinations of major params
+for q in 1 2; do
+ for p in 1 2; do
+ for c in 1; do # BUG - this will fail for c > 1
+ run_test "./jrnl2Perf --num_queues $q --num_msgs ${NUM_MSGS} --num_enq_threads_per_queue $p --num_deq_threads_per_queue $c"
+ done
+ done
+done
+
+#exit 0
diff --git a/cpp/src/tests/txjob.cpp b/cpp/src/tests/txjob.cpp
index a7a905c1b7..29394c3415 100644
--- a/cpp/src/tests/txjob.cpp
+++ b/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/cpp/src/tests/txshift.cpp b/cpp/src/tests/txshift.cpp
index 882d3716d8..bf85bee986 100644
--- a/cpp/src/tests/txshift.cpp
+++ b/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/cpp/xml/cluster.xml b/cpp/xml/cluster.xml
index f9b8caf185..09434ea37b 100644
--- a/cpp/xml/cluster.xml
+++ b/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. -->