summaryrefslogtreecommitdiff
path: root/cpp
diff options
context:
space:
mode:
authorStephen D. Huston <shuston@apache.org>2011-10-21 14:42:12 +0000
committerStephen D. Huston <shuston@apache.org>2011-10-21 14:42:12 +0000
commitf83677056891e436bf5ba99e79240df2a44528cd (patch)
tree625bfd644b948e89105630759cf6decb0435354d /cpp
parentebfd9ff053b04ab379acfc0fefedee5a31b6d8a5 (diff)
downloadqpid-python-QPID-2519.tar.gz
Merged out from trunkQPID-2519
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/QPID-2519@1187375 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'cpp')
-rw-r--r--cpp/CMakeLists.txt1
-rw-r--r--cpp/INSTALL20
-rw-r--r--cpp/INSTALL-WINDOWS11
-rw-r--r--cpp/Makefile.am4
-rw-r--r--cpp/bindings/qmf/python/Makefile.am4
-rw-r--r--cpp/bindings/qmf/ruby/Makefile.am4
-rw-r--r--cpp/bindings/qmf/tests/test_base.rb3
-rw-r--r--cpp/bindings/qmf2/examples/cpp/Makefile.am5
-rw-r--r--cpp/bindings/qmf2/examples/cpp/event_driven_list_agents.cpp107
-rw-r--r--cpp/bindings/qmf2/python/Makefile.am4
-rw-r--r--cpp/bindings/qmf2/qmf2.i1
-rw-r--r--cpp/bindings/qmf2/ruby/Makefile.am4
-rw-r--r--cpp/bindings/qpid/CMakeLists.txt41
-rw-r--r--cpp/bindings/qpid/Makefile.am2
-rw-r--r--cpp/bindings/qpid/dotnet/configure-windows.ps1108
-rw-r--r--cpp/bindings/qpid/dotnet/examples/csharp.direct.receiver/Properties/AssemblyInfo.cs16
-rw-r--r--cpp/bindings/qpid/dotnet/examples/csharp.direct.sender/Properties/AssemblyInfo.cs16
-rw-r--r--cpp/bindings/qpid/dotnet/examples/csharp.map.callback.receiver/Properties/AssemblyInfo.cs12
-rw-r--r--cpp/bindings/qpid/dotnet/examples/csharp.map.callback.sender/Properties/AssemblyInfo.cs2
-rw-r--r--cpp/bindings/qpid/dotnet/examples/csharp.map.receiver/Properties/AssemblyInfo.cs16
-rw-r--r--cpp/bindings/qpid/dotnet/examples/csharp.map.sender/Properties/AssemblyInfo.cs16
-rw-r--r--cpp/bindings/qpid/dotnet/examples/visualbasic.example.client/MyProject/AssemblyInfo.vb30
-rw-r--r--cpp/bindings/qpid/dotnet/org.apache.qpid.messaging.sessionreceiver.sln6
-rw-r--r--cpp/bindings/qpid/dotnet/src/Address.cpp24
-rw-r--r--cpp/bindings/qpid/dotnet/src/Address.h1
-rw-r--r--cpp/bindings/qpid/dotnet/src/Connection.cpp24
-rw-r--r--cpp/bindings/qpid/dotnet/src/Connection.h1
-rw-r--r--cpp/bindings/qpid/dotnet/src/Duration.h12
-rw-r--r--cpp/bindings/qpid/dotnet/src/FailoverUpdates.h1
-rw-r--r--cpp/bindings/qpid/dotnet/src/Message.cpp26
-rw-r--r--cpp/bindings/qpid/dotnet/src/Message.h1
-rw-r--r--cpp/bindings/qpid/dotnet/src/Receiver.cpp25
-rw-r--r--cpp/bindings/qpid/dotnet/src/Receiver.h1
-rw-r--r--cpp/bindings/qpid/dotnet/src/Sender.cpp25
-rw-r--r--cpp/bindings/qpid/dotnet/src/Sender.h1
-rw-r--r--cpp/bindings/qpid/dotnet/src/Session.cpp51
-rw-r--r--cpp/bindings/qpid/dotnet/src/Session.h3
-rw-r--r--cpp/bindings/qpid/dotnet/test/messaging.test/Properties/AssemblyInfo.cs16
-rw-r--r--cpp/bindings/qpid/examples/perl/client.pl8
-rw-r--r--cpp/bindings/qpid/examples/perl/drain.pl12
-rw-r--r--cpp/bindings/qpid/examples/perl/hello_world.pl10
-rw-r--r--cpp/bindings/qpid/examples/perl/hello_xml.pl6
-rw-r--r--cpp/bindings/qpid/examples/perl/map_receiver.pl8
-rw-r--r--cpp/bindings/qpid/examples/perl/map_sender.pl8
-rw-r--r--cpp/bindings/qpid/examples/perl/server.pl6
-rw-r--r--cpp/bindings/qpid/examples/perl/spout.pl10
-rw-r--r--cpp/bindings/qpid/perl/CMakeLists.txt38
-rw-r--r--cpp/bindings/qpid/perl/Makefile.am22
-rw-r--r--cpp/bindings/qpid/perl/perl.i2
-rw-r--r--cpp/bindings/qpid/python/CMakeLists.txt45
-rw-r--r--cpp/bindings/qpid/python/Makefile.am6
-rw-r--r--cpp/bindings/qpid/python/python.i338
-rw-r--r--cpp/bindings/qpid/qpid.i3
-rw-r--r--cpp/bindings/qpid/ruby/.gitignore2
-rw-r--r--cpp/bindings/qpid/ruby/CMakeLists.txt63
-rw-r--r--cpp/bindings/qpid/ruby/LICENSE234
-rw-r--r--cpp/bindings/qpid/ruby/Makefile.am6
-rw-r--r--cpp/bindings/qpid/ruby/README.rdoc45
-rw-r--r--cpp/bindings/qpid/ruby/Rakefile130
-rw-r--r--cpp/bindings/qpid/ruby/TODO7
-rw-r--r--cpp/bindings/qpid/ruby/examples/client.rb50
-rw-r--r--cpp/bindings/qpid/ruby/examples/drain.rb111
-rw-r--r--cpp/bindings/qpid/ruby/examples/hello_world.rb49
-rw-r--r--cpp/bindings/qpid/ruby/examples/map_receiver.rb63
-rw-r--r--cpp/bindings/qpid/ruby/examples/map_sender.rb52
-rw-r--r--cpp/bindings/qpid/ruby/examples/server.rb51
-rw-r--r--cpp/bindings/qpid/ruby/examples/spout.rb126
-rw-r--r--cpp/bindings/qpid/ruby/ext/cqpid/extconf.rb73
-rw-r--r--cpp/bindings/qpid/ruby/lib/qpid.rb29
-rw-r--r--cpp/bindings/qpid/ruby/lib/qpid/address.rb125
-rw-r--r--cpp/bindings/qpid/ruby/lib/qpid/connection.rb134
-rw-r--r--cpp/bindings/qpid/ruby/lib/qpid/duration.rb63
-rw-r--r--cpp/bindings/qpid/ruby/lib/qpid/encoding.rb56
-rw-r--r--cpp/bindings/qpid/ruby/lib/qpid/errors.rb30
-rw-r--r--cpp/bindings/qpid/ruby/lib/qpid/message.rb157
-rw-r--r--cpp/bindings/qpid/ruby/lib/qpid/receiver.rb102
-rw-r--r--cpp/bindings/qpid/ruby/lib/qpid/sender.rb82
-rw-r--r--cpp/bindings/qpid/ruby/lib/qpid/session.rb186
-rw-r--r--cpp/bindings/qpid/ruby/lib/qpid/version.rb31
-rw-r--r--cpp/bindings/qpid/ruby/test/lib/setup.rb29
-rw-r--r--cpp/bindings/qpid/ruby/test/test_address.rb39
-rw-r--r--cpp/bindings/qpid/ruby/test/test_connection.rb257
-rw-r--r--cpp/bindings/qpid/ruby/test/test_encoding.rb146
-rw-r--r--cpp/bindings/qpid/ruby/test/test_message.rb353
-rw-r--r--cpp/bindings/qpid/ruby/test/test_receiver.rb238
-rw-r--r--cpp/bindings/qpid/ruby/test/test_sender.rb183
-rw-r--r--cpp/bindings/qpid/ruby/test/test_session.rb445
-rw-r--r--cpp/bindings/qpid/ruby/test/ts_bindings.rb30
-rw-r--r--cpp/bindings/swig_python_typemaps.i56
-rw-r--r--cpp/bindings/swig_ruby_typemaps.i2
-rw-r--r--cpp/bld-winsdk.ps13
-rw-r--r--cpp/configure.ac21
-rw-r--r--cpp/design_docs/hot-standby-design.txt239
-rw-r--r--cpp/design_docs/new-cluster-design.txt302
-rw-r--r--cpp/design_docs/new-cluster-plan.txt563
-rw-r--r--cpp/docs/api/developer.doxygen.in2
-rw-r--r--cpp/docs/api/doxygen_mainpage.h41
-rw-r--r--cpp/docs/api/footer.html2
-rw-r--r--cpp/docs/api/user.doxygen.in2
-rw-r--r--cpp/docs/man/Makefile.am23
-rw-r--r--cpp/docs/man/qpidd.1247
-rw-r--r--cpp/docs/man/qpidd.x2
-rw-r--r--cpp/etc/Makefile.am23
-rw-r--r--cpp/etc/qpidd.conf2
-rw-r--r--cpp/etc/sasl2/qpidd.conf5
-rw-r--r--cpp/examples/CMakeLists.txt13
-rw-r--r--cpp/examples/Makefile.am16
-rw-r--r--cpp/examples/examples.sln24
-rw-r--r--cpp/examples/messaging/drain.cpp4
-rw-r--r--cpp/examples/messaging/server.cpp4
-rw-r--r--cpp/examples/messaging/spout.cpp2
-rw-r--r--cpp/examples/old_api/CMakeLists.txt25
-rw-r--r--cpp/examples/old_api/Makefile.am48
-rw-r--r--cpp/examples/old_api/README.verify (renamed from cpp/examples/README.verify)0
-rw-r--r--cpp/examples/old_api/direct/CMakeLists.txt (renamed from cpp/examples/direct/CMakeLists.txt)0
-rw-r--r--cpp/examples/old_api/direct/Makefile.am (renamed from cpp/examples/direct/Makefile.am)2
-rw-r--r--cpp/examples/old_api/direct/declare_queues.cpp (renamed from cpp/examples/direct/declare_queues.cpp)0
-rw-r--r--cpp/examples/old_api/direct/direct_declare_queues.vcproj (renamed from cpp/examples/direct/direct_declare_queues.vcproj)0
-rw-r--r--cpp/examples/old_api/direct/direct_direct_producer.vcproj (renamed from cpp/examples/direct/direct_direct_producer.vcproj)0
-rw-r--r--cpp/examples/old_api/direct/direct_listener.vcproj (renamed from cpp/examples/direct/direct_listener.vcproj)0
-rw-r--r--cpp/examples/old_api/direct/direct_producer.cpp (renamed from cpp/examples/direct/direct_producer.cpp)0
-rw-r--r--cpp/examples/old_api/direct/listener.cpp (renamed from cpp/examples/direct/listener.cpp)0
-rw-r--r--cpp/examples/old_api/direct/verify (renamed from cpp/examples/direct/verify)0
-rw-r--r--cpp/examples/old_api/direct/verify.in (renamed from cpp/examples/direct/verify.in)0
-rw-r--r--cpp/examples/old_api/failover/CMakeLists.txt (renamed from cpp/examples/failover/CMakeLists.txt)0
-rw-r--r--cpp/examples/old_api/failover/Makefile.am (renamed from cpp/examples/failover/Makefile.am)2
-rw-r--r--cpp/examples/old_api/failover/declare_queues.cpp (renamed from cpp/examples/failover/declare_queues.cpp)0
-rw-r--r--cpp/examples/old_api/failover/failover_declare_queues.vcproj (renamed from cpp/examples/failover/failover_declare_queues.vcproj)0
-rw-r--r--cpp/examples/old_api/failover/failover_replaying_sender.vcproj (renamed from cpp/examples/failover/failover_replaying_sender.vcproj)0
-rw-r--r--cpp/examples/old_api/failover/failover_resuming_receiver.vcproj (renamed from cpp/examples/failover/failover_resuming_receiver.vcproj)0
-rw-r--r--cpp/examples/old_api/failover/replaying_sender.cpp (renamed from cpp/examples/failover/replaying_sender.cpp)0
-rw-r--r--cpp/examples/old_api/failover/resuming_receiver.cpp (renamed from cpp/examples/failover/resuming_receiver.cpp)0
-rw-r--r--cpp/examples/old_api/fanout/CMakeLists.txt (renamed from cpp/examples/fanout/CMakeLists.txt)0
-rw-r--r--cpp/examples/old_api/fanout/Makefile.am (renamed from cpp/examples/fanout/Makefile.am)2
-rw-r--r--cpp/examples/old_api/fanout/fanout_fanout_producer.vcproj (renamed from cpp/examples/fanout/fanout_fanout_producer.vcproj)0
-rw-r--r--cpp/examples/old_api/fanout/fanout_listener.vcproj (renamed from cpp/examples/fanout/fanout_listener.vcproj)0
-rw-r--r--cpp/examples/old_api/fanout/fanout_producer.cpp (renamed from cpp/examples/fanout/fanout_producer.cpp)0
-rw-r--r--cpp/examples/old_api/fanout/listener.cpp (renamed from cpp/examples/fanout/listener.cpp)0
-rw-r--r--cpp/examples/old_api/fanout/verify (renamed from cpp/examples/fanout/verify)0
-rw-r--r--cpp/examples/old_api/fanout/verify.in (renamed from cpp/examples/fanout/verify.in)0
-rw-r--r--cpp/examples/old_api/old-examples.sln (renamed from cpp/examples/old-examples.sln)24
-rw-r--r--cpp/examples/old_api/pub-sub/CMakeLists.txt (renamed from cpp/examples/pub-sub/CMakeLists.txt)0
-rw-r--r--cpp/examples/old_api/pub-sub/Makefile.am (renamed from cpp/examples/pub-sub/Makefile.am)2
-rw-r--r--cpp/examples/old_api/pub-sub/pub-sub_topic_listener.vcproj (renamed from cpp/examples/pub-sub/pub-sub_topic_listener.vcproj)0
-rw-r--r--cpp/examples/old_api/pub-sub/pub-sub_topic_publisher.vcproj (renamed from cpp/examples/pub-sub/pub-sub_topic_publisher.vcproj)0
-rw-r--r--cpp/examples/old_api/pub-sub/topic_listener.cpp (renamed from cpp/examples/pub-sub/topic_listener.cpp)0
-rw-r--r--cpp/examples/old_api/pub-sub/topic_publisher.cpp (renamed from cpp/examples/pub-sub/topic_publisher.cpp)0
-rw-r--r--cpp/examples/old_api/pub-sub/verify (renamed from cpp/examples/pub-sub/verify)0
-rw-r--r--cpp/examples/old_api/pub-sub/verify.in (renamed from cpp/examples/pub-sub/verify.in)0
-rw-r--r--cpp/examples/old_api/request-response/CMakeLists.txt (renamed from cpp/examples/request-response/CMakeLists.txt)0
-rw-r--r--cpp/examples/old_api/request-response/Makefile.am (renamed from cpp/examples/request-response/Makefile.am)2
-rw-r--r--cpp/examples/old_api/request-response/client.cpp (renamed from cpp/examples/request-response/client.cpp)0
-rw-r--r--cpp/examples/old_api/request-response/request-response_client.vcproj (renamed from cpp/examples/request-response/request-response_client.vcproj)0
-rw-r--r--cpp/examples/old_api/request-response/request-response_server.vcproj (renamed from cpp/examples/request-response/request-response_server.vcproj)0
-rw-r--r--cpp/examples/old_api/request-response/server.cpp (renamed from cpp/examples/request-response/server.cpp)0
-rw-r--r--cpp/examples/old_api/request-response/verify (renamed from cpp/examples/request-response/verify)0
-rw-r--r--cpp/examples/old_api/request-response/verify.in (renamed from cpp/examples/request-response/verify.in)0
-rw-r--r--cpp/examples/old_api/tradedemo/CMakeLists.txt (renamed from cpp/examples/tradedemo/CMakeLists.txt)0
-rw-r--r--cpp/examples/old_api/tradedemo/Makefile.am (renamed from cpp/examples/tradedemo/Makefile.am)2
-rw-r--r--cpp/examples/old_api/tradedemo/declare_queues.cpp (renamed from cpp/examples/tradedemo/declare_queues.cpp)0
-rw-r--r--cpp/examples/old_api/tradedemo/topic_listener.cpp (renamed from cpp/examples/tradedemo/topic_listener.cpp)0
-rw-r--r--cpp/examples/old_api/tradedemo/topic_publisher.cpp (renamed from cpp/examples/tradedemo/topic_publisher.cpp)0
-rw-r--r--cpp/examples/old_api/tradedemo/tradedemo_declare_queues.vcproj (renamed from cpp/examples/tradedemo/tradedemo_declare_queues.vcproj)0
-rw-r--r--cpp/examples/old_api/tradedemo/tradedemo_topic_listener.vcproj (renamed from cpp/examples/tradedemo/tradedemo_topic_listener.vcproj)0
-rw-r--r--cpp/examples/old_api/tradedemo/tradedemo_topic_publisher.vcproj (renamed from cpp/examples/tradedemo/tradedemo_topic_publisher.vcproj)0
-rwxr-xr-xcpp/examples/old_api/verify (renamed from cpp/examples/verify)0
-rwxr-xr-xcpp/examples/old_api/verify_all (renamed from cpp/examples/verify_all)2
-rw-r--r--cpp/examples/old_api/xml-exchange/CMakeLists.txt (renamed from cpp/examples/xml-exchange/CMakeLists.txt)0
-rw-r--r--cpp/examples/old_api/xml-exchange/Makefile.am (renamed from cpp/examples/xml-exchange/Makefile.am)2
-rw-r--r--cpp/examples/old_api/xml-exchange/README.txt (renamed from cpp/examples/xml-exchange/README.txt)0
-rw-r--r--cpp/examples/old_api/xml-exchange/declare_queues.cpp (renamed from cpp/examples/xml-exchange/declare_queues.cpp)0
-rw-r--r--cpp/examples/old_api/xml-exchange/listener.cpp (renamed from cpp/examples/xml-exchange/listener.cpp)0
-rw-r--r--cpp/examples/old_api/xml-exchange/xml_producer.cpp (renamed from cpp/examples/xml-exchange/xml_producer.cpp)0
-rw-r--r--cpp/examples/qmf-console/ping.cpp4
-rw-r--r--cpp/examples/qmf-console/printevents.cpp4
-rw-r--r--cpp/include/qmf/Agent.h2
-rw-r--r--cpp/include/qmf/AgentEvent.h2
-rw-r--r--cpp/include/qmf/AgentSession.h48
-rw-r--r--cpp/include/qmf/ConsoleEvent.h2
-rw-r--r--cpp/include/qmf/ConsoleSession.h55
-rw-r--r--cpp/include/qmf/Data.h2
-rw-r--r--cpp/include/qmf/DataAddr.h5
-rw-r--r--cpp/include/qmf/Handle.h10
-rw-r--r--cpp/include/qmf/ImportExport.h16
-rw-r--r--cpp/include/qmf/Query.h4
-rw-r--r--cpp/include/qmf/Schema.h2
-rw-r--r--cpp/include/qmf/SchemaId.h2
-rw-r--r--cpp/include/qmf/SchemaMethod.h2
-rw-r--r--cpp/include/qmf/SchemaProperty.h2
-rw-r--r--cpp/include/qmf/Subscription.h4
-rw-r--r--cpp/include/qmf/engine/QmfEngineImportExport.h9
-rw-r--r--cpp/include/qmf/exceptions.h8
-rw-r--r--cpp/include/qmf/posix/EventNotifier.h63
-rwxr-xr-xcpp/include/qpid/Address.h2
-rw-r--r--cpp/include/qpid/CommonImportExport.h14
-rw-r--r--cpp/include/qpid/Exception.h12
-rw-r--r--cpp/include/qpid/ImportExport.h71
-rw-r--r--cpp/include/qpid/Msg.h1
-rw-r--r--cpp/include/qpid/Options.h88
-rw-r--r--cpp/include/qpid/Url.h2
-rw-r--r--cpp/include/qpid/agent/ManagementAgent.h4
-rw-r--r--cpp/include/qpid/agent/QmfAgentImportExport.h16
-rw-r--r--cpp/include/qpid/amqp_0_10/Codecs.h20
-rw-r--r--cpp/include/qpid/client/ClientImportExport.h14
-rw-r--r--cpp/include/qpid/client/Completion.h2
-rw-r--r--cpp/include/qpid/client/Connection.h10
-rw-r--r--cpp/include/qpid/client/ConnectionSettings.h2
-rw-r--r--cpp/include/qpid/client/FailoverListener.h2
-rw-r--r--cpp/include/qpid/client/FailoverManager.h2
-rw-r--r--cpp/include/qpid/client/Future.h2
-rw-r--r--cpp/include/qpid/client/FutureResult.h2
-rw-r--r--cpp/include/qpid/client/Handle.h10
-rw-r--r--cpp/include/qpid/client/LocalQueue.h2
-rw-r--r--cpp/include/qpid/client/Message.h2
-rw-r--r--cpp/include/qpid/client/MessageListener.h2
-rw-r--r--cpp/include/qpid/client/MessageReplayTracker.h2
-rw-r--r--cpp/include/qpid/client/QueueOptions.h2
-rw-r--r--cpp/include/qpid/client/SessionBase_0_10.h2
-rw-r--r--cpp/include/qpid/client/Subscription.h8
-rw-r--r--cpp/include/qpid/client/SubscriptionManager.h2
-rw-r--r--cpp/include/qpid/console/Agent.h14
-rw-r--r--cpp/include/qpid/console/Broker.h16
-rw-r--r--cpp/include/qpid/console/ClassKey.h30
-rw-r--r--cpp/include/qpid/console/ConsoleImportExport.h14
-rw-r--r--cpp/include/qpid/console/ObjectId.h8
-rw-r--r--cpp/include/qpid/framing/Array.h26
-rw-r--r--cpp/include/qpid/framing/Buffer.h16
-rw-r--r--cpp/include/qpid/framing/FieldTable.h6
-rw-r--r--cpp/include/qpid/framing/FieldValue.h21
-rw-r--r--cpp/include/qpid/framing/List.h25
-rw-r--r--cpp/include/qpid/framing/ProtocolVersion.h12
-rw-r--r--cpp/include/qpid/framing/SequenceNumber.h2
-rw-r--r--cpp/include/qpid/framing/SequenceSet.h2
-rw-r--r--cpp/include/qpid/framing/StructHelper.h2
-rw-r--r--cpp/include/qpid/framing/Uuid.h10
-rw-r--r--cpp/include/qpid/log/Logger.h6
-rw-r--r--cpp/include/qpid/log/Options.h2
-rw-r--r--cpp/include/qpid/management/ManagementObject.h18
-rw-r--r--cpp/include/qpid/messaging/Address.h2
-rw-r--r--cpp/include/qpid/messaging/Connection.h30
-rw-r--r--cpp/include/qpid/messaging/Duration.h8
-rw-r--r--cpp/include/qpid/messaging/FailoverUpdates.h2
-rw-r--r--cpp/include/qpid/messaging/Handle.h10
-rw-r--r--cpp/include/qpid/messaging/ImportExport.h14
-rw-r--r--cpp/include/qpid/messaging/Message.h101
-rw-r--r--cpp/include/qpid/messaging/Receiver.h2
-rw-r--r--cpp/include/qpid/messaging/Sender.h2
-rw-r--r--cpp/include/qpid/messaging/Session.h51
-rw-r--r--cpp/include/qpid/messaging/exceptions.h98
-rw-r--r--cpp/include/qpid/sys/ExceptionHolder.h13
-rwxr-xr-xcpp/include/qpid/sys/IntegerTypes.h2
-rw-r--r--cpp/include/qpid/sys/Runnable.h2
-rw-r--r--cpp/include/qpid/sys/Thread.h6
-rw-r--r--cpp/include/qpid/sys/Time.h5
-rwxr-xr-xcpp/include/qpid/sys/windows/IntegerTypes.h6
-rw-r--r--cpp/include/qpid/types/Exception.h2
-rw-r--r--cpp/include/qpid/types/ImportExport.h14
-rw-r--r--cpp/include/qpid/types/Uuid.h2
-rw-r--r--cpp/include/qpid/types/Variant.h6
-rw-r--r--cpp/managementgen/Makefile.am12
-rwxr-xr-xcpp/managementgen/qmfgen/schema.py4
-rwxr-xr-xcpp/rubygen/0-10/specification.rb2
-rwxr-xr-xcpp/rubygen/MethodBodyDefaultVisitor.rb2
-rwxr-xr-xcpp/rubygen/amqpgen.rb6
-rwxr-xr-xcpp/rubygen/cppgen.rb3
-rwxr-xr-xcpp/rubygen/framing.0-10/MethodBodyDefaultVisitor.rb2
-rwxr-xr-xcpp/rubygen/framing.0-10/OperationsInvoker.rb2
-rwxr-xr-xcpp/rubygen/framing.0-10/Proxy.rb4
-rwxr-xr-xcpp/rubygen/framing.0-10/Session.rb4
-rwxr-xr-xcpp/rubygen/framing.0-10/structs.rb2
-rw-r--r--cpp/src/CMakeLists.txt126
-rw-r--r--cpp/src/CMakeWinVersions.cmake12
-rw-r--r--cpp/src/Makefile.am55
-rw-r--r--cpp/src/acl.mk4
-rw-r--r--cpp/src/cluster.mk6
-rw-r--r--cpp/src/posix/QpiddBroker.cpp12
-rw-r--r--cpp/src/qmf.mk9
-rw-r--r--cpp/src/qmf/Agent.cpp29
-rw-r--r--cpp/src/qmf/AgentImpl.h1
-rw-r--r--cpp/src/qmf/AgentSession.cpp229
-rw-r--r--cpp/src/qmf/AgentSessionImpl.h175
-rw-r--r--cpp/src/qmf/ConsoleSession.cpp125
-rw-r--r--cpp/src/qmf/ConsoleSessionImpl.h22
-rw-r--r--cpp/src/qmf/DataAddr.cpp6
-rw-r--r--cpp/src/qmf/DataAddrImpl.h4
-rw-r--r--cpp/src/qmf/EventNotifierImpl.cpp56
-rw-r--r--cpp/src/qmf/EventNotifierImpl.h (renamed from cpp/src/tests/qrsh_utils/qrsh_example_command.cpp)58
-rw-r--r--cpp/src/qmf/PosixEventNotifier.cpp65
-rw-r--r--cpp/src/qmf/PosixEventNotifierImpl.cpp112
-rw-r--r--cpp/src/qmf/PosixEventNotifierImpl.h61
-rw-r--r--cpp/src/qmf/PrivateImplRef.h2
-rw-r--r--cpp/src/qmf/engine/ResilientConnection.cpp6
-rw-r--r--cpp/src/qmf/engine/SchemaImpl.cpp11
-rw-r--r--cpp/src/qmf/engine/SchemaImpl.h7
-rw-r--r--cpp/src/qpid/Address.cpp8
-rw-r--r--cpp/src/qpid/BufferRef.h70
-rw-r--r--cpp/src/qpid/DisableExceptionLogging.h (renamed from cpp/src/qpid/client/amqp0_10/SimpleUrlParser.h)27
-rw-r--r--cpp/src/qpid/Exception.cpp16
-rw-r--r--cpp/src/qpid/Modules.cpp3
-rw-r--r--cpp/src/qpid/Options.cpp269
-rw-r--r--cpp/src/qpid/RefCounted.h6
-rw-r--r--cpp/src/qpid/RefCountedBuffer.cpp29
-rw-r--r--cpp/src/qpid/RefCountedBuffer.h61
-rw-r--r--cpp/src/qpid/Sasl.h4
-rw-r--r--cpp/src/qpid/SaslFactory.cpp28
-rw-r--r--cpp/src/qpid/Url.cpp23
-rw-r--r--cpp/src/qpid/acl/Acl.cpp5
-rw-r--r--cpp/src/qpid/acl/AclPlugin.cpp2
-rw-r--r--cpp/src/qpid/agent/ManagementAgentImpl.cpp361
-rw-r--r--cpp/src/qpid/agent/ManagementAgentImpl.h4
-rw-r--r--cpp/src/qpid/amqp_0_10/Codecs.cpp6
-rw-r--r--cpp/src/qpid/amqp_0_10/SessionHandler.cpp8
-rw-r--r--cpp/src/qpid/amqp_0_10/SessionHandler.h6
-rw-r--r--cpp/src/qpid/broker/AsyncCompletion.h201
-rw-r--r--cpp/src/qpid/broker/Bridge.cpp6
-rw-r--r--cpp/src/qpid/broker/Bridge.h2
-rw-r--r--cpp/src/qpid/broker/Broker.cpp566
-rw-r--r--cpp/src/qpid/broker/Broker.h107
-rw-r--r--cpp/src/qpid/broker/BrokerImportExport.h23
-rw-r--r--cpp/src/qpid/broker/Connection.cpp57
-rw-r--r--cpp/src/qpid/broker/Connection.h9
-rw-r--r--cpp/src/qpid/broker/ConnectionHandler.cpp78
-rw-r--r--cpp/src/qpid/broker/ConnectionHandler.h6
-rw-r--r--cpp/src/qpid/broker/ConnectionState.h9
-rw-r--r--cpp/src/qpid/broker/Consumer.h21
-rw-r--r--cpp/src/qpid/broker/Daemon.cpp5
-rw-r--r--cpp/src/qpid/broker/DeliverableMessage.h2
-rw-r--r--cpp/src/qpid/broker/DeliveryRecord.cpp28
-rw-r--r--cpp/src/qpid/broker/DeliveryRecord.h4
-rw-r--r--cpp/src/qpid/broker/DirectExchange.cpp14
-rw-r--r--cpp/src/qpid/broker/DtxAck.cpp4
-rw-r--r--cpp/src/qpid/broker/DtxAck.h35
-rw-r--r--cpp/src/qpid/broker/DtxBuffer.cpp22
-rw-r--r--cpp/src/qpid/broker/DtxBuffer.h53
-rw-r--r--cpp/src/qpid/broker/DtxManager.cpp40
-rw-r--r--cpp/src/qpid/broker/DtxManager.h26
-rw-r--r--cpp/src/qpid/broker/DtxTimeout.cpp2
-rw-r--r--cpp/src/qpid/broker/DtxTimeout.h10
-rw-r--r--cpp/src/qpid/broker/DtxWorkRecord.cpp40
-rw-r--r--cpp/src/qpid/broker/DtxWorkRecord.h14
-rw-r--r--cpp/src/qpid/broker/Exchange.cpp83
-rw-r--r--cpp/src/qpid/broker/Exchange.h48
-rw-r--r--cpp/src/qpid/broker/ExchangeRegistry.cpp11
-rw-r--r--cpp/src/qpid/broker/ExpiryPolicy.cpp10
-rw-r--r--cpp/src/qpid/broker/ExpiryPolicy.h14
-rw-r--r--cpp/src/qpid/broker/Fairshare.cpp76
-rw-r--r--cpp/src/qpid/broker/Fairshare.h6
-rw-r--r--cpp/src/qpid/broker/FanOutExchange.cpp11
-rw-r--r--cpp/src/qpid/broker/FifoDistributor.cpp58
-rw-r--r--cpp/src/qpid/broker/FifoDistributor.h (renamed from cpp/src/qpid/broker/IncompleteMessageList.h)50
-rw-r--r--cpp/src/qpid/broker/HeadersExchange.cpp20
-rw-r--r--cpp/src/qpid/broker/IncompleteMessageList.cpp85
-rw-r--r--cpp/src/qpid/broker/LegacyLVQ.cpp6
-rw-r--r--cpp/src/qpid/broker/Link.cpp65
-rw-r--r--cpp/src/qpid/broker/Link.h10
-rw-r--r--cpp/src/qpid/broker/LinkRegistry.cpp6
-rw-r--r--cpp/src/qpid/broker/Message.cpp172
-rw-r--r--cpp/src/qpid/broker/Message.h78
-rw-r--r--cpp/src/qpid/broker/MessageBuilder.h2
-rw-r--r--cpp/src/qpid/broker/MessageDistributor.h76
-rw-r--r--cpp/src/qpid/broker/MessageGroupManager.cpp411
-rw-r--r--cpp/src/qpid/broker/MessageGroupManager.h107
-rw-r--r--cpp/src/qpid/broker/Messages.h4
-rw-r--r--cpp/src/qpid/broker/NullMessageStore.cpp4
-rw-r--r--cpp/src/qpid/broker/NullMessageStore.h4
-rw-r--r--cpp/src/qpid/broker/PersistableMessage.cpp26
-rw-r--r--cpp/src/qpid/broker/PersistableMessage.h29
-rw-r--r--cpp/src/qpid/broker/Queue.cpp727
-rw-r--r--cpp/src/qpid/broker/Queue.h131
-rw-r--r--cpp/src/qpid/broker/QueueCleaner.cpp18
-rw-r--r--cpp/src/qpid/broker/QueueCleaner.h14
-rw-r--r--cpp/src/qpid/broker/QueueEvents.cpp4
-rw-r--r--cpp/src/qpid/broker/QueueFlowLimit.cpp410
-rw-r--r--cpp/src/qpid/broker/QueueFlowLimit.h132
-rw-r--r--cpp/src/qpid/broker/QueueListeners.cpp36
-rw-r--r--cpp/src/qpid/broker/QueueListeners.h4
-rw-r--r--cpp/src/qpid/broker/QueueObserver.h42
-rw-r--r--cpp/src/qpid/broker/QueuePolicy.cpp23
-rw-r--r--cpp/src/qpid/broker/QueuePolicy.h3
-rw-r--r--cpp/src/qpid/broker/QueueRegistry.cpp20
-rw-r--r--cpp/src/qpid/broker/QueueRegistry.h7
-rw-r--r--cpp/src/qpid/broker/RateTracker.cpp51
-rw-r--r--cpp/src/qpid/broker/RateTracker.h57
-rw-r--r--cpp/src/qpid/broker/RecoveredDequeue.cpp1
-rw-r--r--cpp/src/qpid/broker/RecoveredEnqueue.cpp1
-rw-r--r--cpp/src/qpid/broker/RecoveryManagerImpl.cpp3
-rw-r--r--cpp/src/qpid/broker/SaslAuthenticator.cpp66
-rw-r--r--cpp/src/qpid/broker/SaslAuthenticator.h2
-rw-r--r--cpp/src/qpid/broker/SemanticState.cpp171
-rw-r--r--cpp/src/qpid/broker/SemanticState.h45
-rw-r--r--cpp/src/qpid/broker/SessionAdapter.cpp292
-rw-r--r--cpp/src/qpid/broker/SessionAdapter.h1
-rw-r--r--cpp/src/qpid/broker/SessionContext.h1
-rw-r--r--cpp/src/qpid/broker/SessionHandler.cpp5
-rw-r--r--cpp/src/qpid/broker/SessionState.cpp245
-rw-r--r--cpp/src/qpid/broker/SessionState.h119
-rw-r--r--cpp/src/qpid/broker/StatefulQueueObserver.h63
-rw-r--r--cpp/src/qpid/broker/ThresholdAlerts.cpp68
-rw-r--r--cpp/src/qpid/broker/ThresholdAlerts.h7
-rw-r--r--cpp/src/qpid/broker/TopicExchange.cpp84
-rw-r--r--cpp/src/qpid/broker/TopicExchange.h29
-rw-r--r--cpp/src/qpid/broker/TxBuffer.cpp2
-rw-r--r--cpp/src/qpid/broker/TxPublish.cpp9
-rw-r--r--cpp/src/qpid/broker/TxPublish.h91
-rw-r--r--cpp/src/qpid/broker/windows/BrokerDefaults.cpp6
-rw-r--r--cpp/src/qpid/broker/windows/SaslAuthenticator.cpp32
-rw-r--r--cpp/src/qpid/broker/windows/SslProtocolFactory.cpp53
-rw-r--r--cpp/src/qpid/client/ConnectionHandler.cpp64
-rw-r--r--cpp/src/qpid/client/ConnectionImpl.cpp12
-rw-r--r--cpp/src/qpid/client/Connector.h2
-rw-r--r--cpp/src/qpid/client/RdmaConnector.cpp6
-rw-r--r--cpp/src/qpid/client/SessionImpl.cpp1
-rw-r--r--cpp/src/qpid/client/SslConnector.cpp6
-rw-r--r--cpp/src/qpid/client/TCPConnector.cpp6
-rw-r--r--cpp/src/qpid/client/TCPConnector.h2
-rw-r--r--cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp42
-rw-r--r--cpp/src/qpid/client/amqp0_10/AcceptTracker.h5
-rw-r--r--cpp/src/qpid/client/amqp0_10/AddressResolution.cpp60
-rw-r--r--cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp179
-rw-r--r--cpp/src/qpid/client/amqp0_10/ConnectionImpl.h2
-rw-r--r--cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp10
-rw-r--r--cpp/src/qpid/client/amqp0_10/IncomingMessages.h2
-rw-r--r--cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp4
-rw-r--r--cpp/src/qpid/client/amqp0_10/SenderImpl.cpp3
-rw-r--r--cpp/src/qpid/client/amqp0_10/SessionImpl.cpp56
-rw-r--r--cpp/src/qpid/client/amqp0_10/SessionImpl.h11
-rw-r--r--cpp/src/qpid/client/amqp0_10/SimpleUrlParser.cpp79
-rw-r--r--cpp/src/qpid/client/windows/SaslFactory.cpp16
-rw-r--r--cpp/src/qpid/client/windows/SslConnector.cpp4
-rw-r--r--cpp/src/qpid/cluster/Cluster.cpp169
-rw-r--r--cpp/src/qpid/cluster/Cluster.h56
-rw-r--r--cpp/src/qpid/cluster/ClusterMap.cpp5
-rw-r--r--cpp/src/qpid/cluster/ClusterPlugin.cpp1
-rw-r--r--cpp/src/qpid/cluster/ClusterSettings.h3
-rw-r--r--cpp/src/qpid/cluster/ClusterTimer.cpp4
-rw-r--r--cpp/src/qpid/cluster/Connection.cpp235
-rw-r--r--cpp/src/qpid/cluster/Connection.h55
-rw-r--r--cpp/src/qpid/cluster/Decoder.h2
-rw-r--r--cpp/src/qpid/cluster/ErrorCheck.h2
-rw-r--r--cpp/src/qpid/cluster/Event.cpp5
-rw-r--r--cpp/src/qpid/cluster/Event.h28
-rw-r--r--cpp/src/qpid/cluster/EventFrame.h6
-rw-r--r--cpp/src/qpid/cluster/ExpiryPolicy.cpp95
-rw-r--r--cpp/src/qpid/cluster/ExpiryPolicy.h42
-rw-r--r--cpp/src/qpid/cluster/FailoverExchange.cpp26
-rw-r--r--cpp/src/qpid/cluster/FailoverExchange.h10
-rw-r--r--cpp/src/qpid/cluster/Multicaster.cpp3
-rw-r--r--cpp/src/qpid/cluster/OutputInterceptor.cpp39
-rw-r--r--cpp/src/qpid/cluster/OutputInterceptor.h10
-rw-r--r--cpp/src/qpid/cluster/SecureConnectionFactory.cpp8
-rw-r--r--cpp/src/qpid/cluster/UpdateClient.cpp223
-rw-r--r--cpp/src/qpid/cluster/UpdateClient.h32
-rw-r--r--cpp/src/qpid/cluster/UpdateDataExchange.cpp10
-rw-r--r--cpp/src/qpid/cluster/UpdateDataExchange.h2
-rw-r--r--cpp/src/qpid/cluster/UpdateExchange.cpp27
-rw-r--r--cpp/src/qpid/cluster/UpdateReceiver.h14
-rw-r--r--cpp/src/qpid/cluster/types.h1
-rw-r--r--cpp/src/qpid/console/SessionManager.cpp3
-rw-r--r--cpp/src/qpid/framing/AMQBody.h2
-rw-r--r--cpp/src/qpid/framing/AMQContentBody.h12
-rw-r--r--cpp/src/qpid/framing/AMQFrame.cpp5
-rw-r--r--cpp/src/qpid/framing/AMQFrame.h7
-rw-r--r--cpp/src/qpid/framing/AMQHeaderBody.h14
-rw-r--r--cpp/src/qpid/framing/AMQHeartbeatBody.h2
-rw-r--r--cpp/src/qpid/framing/FieldTable.cpp6
-rw-r--r--cpp/src/qpid/framing/List.cpp6
-rw-r--r--cpp/src/qpid/framing/MethodBodyFactory.h1
-rw-r--r--cpp/src/qpid/framing/SendContent.h2
-rw-r--r--cpp/src/qpid/framing/TransferContent.h2
-rw-r--r--cpp/src/qpid/framing/Uuid.cpp4
-rw-r--r--cpp/src/qpid/log/Logger.cpp21
-rw-r--r--cpp/src/qpid/log/Options.cpp4
-rw-r--r--cpp/src/qpid/log/Statement.cpp5
-rw-r--r--cpp/src/qpid/log/posix/SinkOptions.cpp2
-rw-r--r--cpp/src/qpid/log/windows/SinkOptions.cpp4
-rw-r--r--cpp/src/qpid/log/windows/SinkOptions.h2
-rw-r--r--cpp/src/qpid/management/ManagementAgent.cpp228
-rw-r--r--cpp/src/qpid/management/ManagementAgent.h13
-rw-r--r--cpp/src/qpid/messaging/AddressParser.cpp5
-rw-r--r--cpp/src/qpid/messaging/Duration.cpp10
-rw-r--r--cpp/src/qpid/messaging/Message.cpp7
-rw-r--r--cpp/src/qpid/messaging/Session.cpp3
-rw-r--r--cpp/src/qpid/messaging/SessionImpl.h2
-rw-r--r--cpp/src/qpid/replication/ReplicatingEventListener.cpp7
-rw-r--r--cpp/src/qpid/replication/ReplicationExchange.cpp9
-rw-r--r--cpp/src/qpid/store/StorageProvider.h2
-rw-r--r--cpp/src/qpid/sys/AggregateOutput.h2
-rw-r--r--cpp/src/qpid/sys/AsynchIO.h4
-rw-r--r--cpp/src/qpid/sys/AsynchIOHandler.h2
-rw-r--r--cpp/src/qpid/sys/AtomicValue.h7
-rw-r--r--cpp/src/qpid/sys/AtomicValue_gcc.h11
-rw-r--r--cpp/src/qpid/sys/ClusterSafe.cpp12
-rw-r--r--cpp/src/qpid/sys/ClusterSafe.h21
-rw-r--r--cpp/src/qpid/sys/CopyOnWriteArray.h6
-rw-r--r--cpp/src/qpid/sys/PollableQueue.h21
-rw-r--r--cpp/src/qpid/sys/Poller.h2
-rw-r--r--cpp/src/qpid/sys/ProtocolFactory.h3
-rw-r--r--cpp/src/qpid/sys/RdmaIOPlugin.cpp24
-rw-r--r--cpp/src/qpid/sys/Socket.h34
-rw-r--r--cpp/src/qpid/sys/SocketAddress.h10
-rw-r--r--cpp/src/qpid/sys/SslPlugin.cpp154
-rw-r--r--cpp/src/qpid/sys/StateMonitor.h14
-rw-r--r--cpp/src/qpid/sys/TCPIOPlugin.cpp105
-rw-r--r--cpp/src/qpid/sys/Timer.cpp22
-rw-r--r--cpp/src/qpid/sys/Timer.h8
-rw-r--r--cpp/src/qpid/sys/TimerWarnings.cpp16
-rw-r--r--cpp/src/qpid/sys/alloca.h25
-rw-r--r--cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp3
-rw-r--r--cpp/src/qpid/sys/epoll/EpollPoller.cpp7
-rw-r--r--cpp/src/qpid/sys/posix/AsynchIO.cpp36
-rwxr-xr-xcpp/src/qpid/sys/posix/LockFile.cpp3
-rw-r--r--cpp/src/qpid/sys/posix/Socket.cpp171
-rw-r--r--cpp/src/qpid/sys/posix/SocketAddress.cpp80
-rw-r--r--cpp/src/qpid/sys/posix/Thread.cpp3
-rw-r--r--cpp/src/qpid/sys/posix/Time.cpp7
-rw-r--r--cpp/src/qpid/sys/rdma/RdmaIO.cpp14
-rw-r--r--cpp/src/qpid/sys/rdma/rdma_wrap.cpp15
-rw-r--r--cpp/src/qpid/sys/rdma/rdma_wrap.h10
-rw-r--r--cpp/src/qpid/sys/ssl/SslHandler.h2
-rw-r--r--cpp/src/qpid/sys/ssl/SslIo.cpp22
-rw-r--r--cpp/src/qpid/sys/ssl/SslIo.h18
-rw-r--r--cpp/src/qpid/sys/ssl/SslSocket.cpp163
-rw-r--r--cpp/src/qpid/sys/ssl/SslSocket.h46
-rw-r--r--cpp/src/qpid/sys/windows/AsynchIO.cpp71
-rwxr-xr-xcpp/src/qpid/sys/windows/AsynchIoResult.h6
-rwxr-xr-xcpp/src/qpid/sys/windows/IocpPoller.cpp6
-rw-r--r--cpp/src/qpid/sys/windows/Shlib.cpp3
-rw-r--r--[-rwxr-xr-x]cpp/src/qpid/sys/windows/Socket.cpp188
-rw-r--r--cpp/src/qpid/sys/windows/SocketAddress.cpp120
-rw-r--r--cpp/src/qpid/sys/windows/SslAsynchIO.h3
-rwxr-xr-xcpp/src/qpid/sys/windows/StrError.cpp7
-rwxr-xr-xcpp/src/qpid/sys/windows/Thread.cpp285
-rw-r--r--cpp/src/qpid/sys/windows/Time.cpp36
-rw-r--r--[-rwxr-xr-x]cpp/src/qpid/sys/windows/mingw32_compat.h (renamed from cpp/src/tests/qrsh_utils/6_get)22
-rw-r--r--cpp/src/qpid/sys/windows/uuid.cpp6
-rw-r--r--cpp/src/qpid/types/Uuid.cpp19
-rw-r--r--cpp/src/qpid/types/Variant.cpp35
-rw-r--r--cpp/src/replication.mk6
-rw-r--r--cpp/src/ssl.mk6
-rw-r--r--cpp/src/tests/.valgrind.supp74
-rw-r--r--cpp/src/tests/Address.cpp11
-rw-r--r--cpp/src/tests/BrokerFixture.h32
-rw-r--r--cpp/src/tests/BrokerMgmtAgent.cpp3
-rw-r--r--cpp/src/tests/BrokerOptions.cpp79
-rw-r--r--cpp/src/tests/CMakeLists.txt18
-rw-r--r--cpp/src/tests/ClientSessionTest.cpp33
-rw-r--r--cpp/src/tests/ExchangeTest.cpp2
-rw-r--r--cpp/src/tests/ForkedBroker.cpp3
-rw-r--r--cpp/src/tests/IncompleteMessageList.cpp134
-rw-r--r--cpp/src/tests/Makefile.am47
-rw-r--r--cpp/src/tests/MessageReplayTracker.cpp4
-rw-r--r--cpp/src/tests/MessagingFixture.h117
-rw-r--r--cpp/src/tests/MessagingSessionTests.cpp228
-rw-r--r--cpp/src/tests/Qmf2.cpp104
-rw-r--r--cpp/src/tests/QueueEvents.cpp4
-rw-r--r--cpp/src/tests/QueueFlowLimitTest.cpp463
-rw-r--r--cpp/src/tests/QueuePolicyTest.cpp21
-rw-r--r--cpp/src/tests/QueueTest.cpp444
-rw-r--r--cpp/src/tests/ReplicationTest.cpp2
-rw-r--r--cpp/src/tests/SessionState.cpp8
-rw-r--r--cpp/src/tests/SocketProxy.h181
-rw-r--r--cpp/src/tests/TimerTest.cpp4
-rw-r--r--cpp/src/tests/TxPublishTest.cpp7
-rw-r--r--cpp/src/tests/Url.cpp26
-rw-r--r--cpp/src/tests/Variant.cpp58
-rw-r--r--cpp/src/tests/XmlClientSessionTest.cpp2
-rwxr-xr-xcpp/src/tests/acl.py219
-rwxr-xr-xcpp/src/tests/allhosts4
-rw-r--r--cpp/src/tests/brokertest.py312
-rwxr-xr-xcpp/src/tests/cli_tests.py24
-rw-r--r--cpp/src/tests/cluster_python_tests_failing.txt28
-rwxr-xr-xcpp/src/tests/cluster_test_logs.py16
-rwxr-xr-xcpp/src/tests/cluster_tests.py1033
-rw-r--r--cpp/src/tests/exception_test.cpp14
-rwxr-xr-xcpp/src/tests/federated_topic_test27
-rwxr-xr-xcpp/src/tests/federation.py398
-rwxr-xr-xcpp/src/tests/federation_sys.py1900
-rwxr-xr-xcpp/src/tests/ipv6_test150
-rw-r--r--cpp/src/tests/msg_group_test.cpp618
-rwxr-xr-xcpp/src/tests/python_tests2
-rwxr-xr-xcpp/src/tests/qpid-cluster-benchmark47
-rwxr-xr-xcpp/src/tests/qpid-cpp-benchmark71
-rwxr-xr-xcpp/src/tests/qpid-ctrl5
-rw-r--r--cpp/src/tests/qpid-perftest.cpp22
-rw-r--r--cpp/src/tests/qpid-receive.cpp9
-rw-r--r--cpp/src/tests/qpid-send.cpp104
-rw-r--r--cpp/src/tests/qrsh.cpp169
-rw-r--r--cpp/src/tests/qrsh_run.cpp321
-rw-r--r--cpp/src/tests/qrsh_server.cpp1068
-rwxr-xr-xcpp/src/tests/qrsh_utils/10_all30
-rwxr-xr-xcpp/src/tests/qrsh_utils/1_remote_run26
-rwxr-xr-xcpp/src/tests/qrsh_utils/2_forever26
-rwxr-xr-xcpp/src/tests/qrsh_utils/3_kill_it27
-rwxr-xr-xcpp/src/tests/qrsh_utils/4_wait_for_it26
-rwxr-xr-xcpp/src/tests/qrsh_utils/5_exited64
-rwxr-xr-xcpp/src/tests/qrsh_utils/7_get_output44
-rwxr-xr-xcpp/src/tests/qrsh_utils/8_any43
-rwxr-xr-xcpp/src/tests/qrsh_utils/9_alias38
-rw-r--r--cpp/src/tests/qrsh_utils/qrsh_forever.cpp50
-rw-r--r--cpp/src/tests/qrsh_utils/qsh_doc.txt309
-rw-r--r--cpp/src/tests/queue_flow_limit_tests.py371
-rwxr-xr-xcpp/src/tests/replication_test2
-rwxr-xr-xcpp/src/tests/run_acl_tests2
-rwxr-xr-xcpp/src/tests/run_cli_tests5
-rwxr-xr-xcpp/src/tests/run_federation_sys_tests97
-rwxr-xr-xcpp/src/tests/run_federation_tests4
-rwxr-xr-xcpp/src/tests/run_header_test2
-rw-r--r--cpp/src/tests/run_long_federation_sys_tests24
-rwxr-xr-xcpp/src/tests/run_msg_group_tests66
-rwxr-xr-xcpp/src/tests/run_msg_group_tests_soak60
-rwxr-xr-xcpp/src/tests/run_queue_flow_limit_tests57
-rw-r--r--cpp/src/tests/run_store_tests.ps12
-rwxr-xr-xcpp/src/tests/run_test2
-rw-r--r--cpp/src/tests/sasl.mk16
-rwxr-xr-xcpp/src/tests/sasl_fed2
-rwxr-xr-xcpp/src/tests/sasl_fed_ex306
-rwxr-xr-xcpp/src/tests/sasl_fed_ex_dynamic27
-rwxr-xr-xcpp/src/tests/sasl_fed_ex_dynamic_cluster28
-rwxr-xr-xcpp/src/tests/sasl_fed_ex_link27
-rwxr-xr-xcpp/src/tests/sasl_fed_ex_link_cluster28
-rwxr-xr-xcpp/src/tests/sasl_fed_ex_queue27
-rwxr-xr-xcpp/src/tests/sasl_fed_ex_queue_cluster28
-rwxr-xr-xcpp/src/tests/sasl_fed_ex_route27
-rwxr-xr-xcpp/src/tests/sasl_fed_ex_route_cluster28
-rwxr-xr-xcpp/src/tests/sasl_no_dir218
-rwxr-xr-xcpp/src/tests/sasl_test_setup.sh1
-rw-r--r--cpp/src/tests/sender.cpp2
-rwxr-xr-xcpp/src/tests/ssl_test36
-rw-r--r--cpp/src/tests/windows/DisableWin32ErrorWindows.cpp4
-rw-r--r--cpp/src/windows/QpiddBroker.cpp4
-rw-r--r--cpp/src/windows/resources/template-resource.rc2
-rw-r--r--cpp/src/xml.mk2
-rw-r--r--cpp/xml/cluster.xml74
633 files changed, 22321 insertions, 8520 deletions
diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt
index 5c37ecfc97..17411e90a4 100644
--- a/cpp/CMakeLists.txt
+++ b/cpp/CMakeLists.txt
@@ -80,6 +80,7 @@ set(CPACK_PACKAGE_INSTALL_DIRECTORY "qpidc-${qpidc_version}")
add_subdirectory(managementgen)
add_subdirectory(etc)
add_subdirectory(src)
+add_subdirectory(bindings/qpid)
add_subdirectory(docs/api)
# add_subdirectory(docs/man)
add_subdirectory(examples)
diff --git a/cpp/INSTALL b/cpp/INSTALL
index 6483d7de4e..dbd41c7cc1 100644
--- a/cpp/INSTALL
+++ b/cpp/INSTALL
@@ -47,9 +47,9 @@ Redhat Linux 3, will almost certainly require some packages to be upgraded.
====================
The following libraries and header files must be installed to build
a source distribution:
- * boost <http://www.boost.org> (1.35)(*)
- * e2fsprogs <http://e2fsprogs.sourceforge.net/> (1.39)
- * pkgconfig <http://pkgconfig.freedesktop.org/wiki/> (0.21)
+ * boost <http://www.boost.org> (1.35)(*)
+ * libuuid <http://kernel.org/~kzak/util-linux/> (2.19)
+ * pkgconfig <http://pkgconfig.freedesktop.org/wiki/> (0.21)
(*) earlier versions of boost e.g. 1.33 also work and there is a patch
to get 1.32 working in the svn tree though that is only recommended as
@@ -96,12 +96,12 @@ the following must also be installed:
* python-devel
* swig <http://www.swig.org> (1.3.35)
-UUID problems:
-In some later Linux releases (such as Fedora 12), the uuid/uuid.h file has been
-moved from e2fsprogs-devel into libuuid-devel. If you are using a newer Linux
-release and run into a problem during configure in which uuid.h cannot be found,
-look for and install the libuuid-devel package.
-
+UUID problems:
+In some earlier Linux releases (such as Fedora 11), the uuid/uuid.h
+file is located in the e2fsprogs-devel package instead of
+libuuid-devel. If you are using an older Linux release and run into a
+problem during configure in which uuid.h cannot be found, install the
+e2fsprogs-devel package.
2.2. How to Install
===================
@@ -112,7 +112,7 @@ look for and install the libuuid-devel package.
On linux most packages can be installed using your distribution's
package management tool. For example on Fedora:
- # yum install boost-devel e2fsprogs-devel pkgconfig gcc-c++ make autoconf automake ruby libtool help2man doxygen graphviz
+ # yum install boost-devel libuuid-devel pkgconfig gcc-c++ make autoconf automake ruby libtool help2man doxygen graphviz
The optional clustering packages changed name in Fedora 10. On Fedora 9 or earlier:
# yum install openais-devel cman-devel
diff --git a/cpp/INSTALL-WINDOWS b/cpp/INSTALL-WINDOWS
index 964d644a7a..75fa69079e 100644
--- a/cpp/INSTALL-WINDOWS
+++ b/cpp/INSTALL-WINDOWS
@@ -64,7 +64,7 @@ manually installed to non-standard locations. For example:
It is also necessary to set BOOST_ROOT to refer to the base of your Boost
installation. The Visual Studio projects refer to it. For example:
- # set BOOST_ROOT=C:\Program Files\boost\boost_1_35_0
+ # set BOOST_ROOT="C:\Program Files\boost\boost_1_35_0"
3. Building from a Source Distribution
@@ -75,7 +75,7 @@ Visual Studio solution file which is generated by CMake.
From a command prompt:
# cd qpid\cpp
- # cmake -i
+ # cmake -i -G "Visual Studio 9 2008" .
Output from CMake includes .h files in the include directory, .vcproj
files for executables and dlls, and the qpid-cpp.sln solution file.
@@ -120,7 +120,12 @@ files that are part of the build.
Configure again. Repeat until all the Cache Values are gray.
- Click the OK button to generate the project/make files.
-Now follow instruction for building from a source distribution in step (3).
+Open the qpid-cpp.sln solution located in the build directory, select Debug
+or Release, and build. You can build both Release and Debug from the same
+project.
+
+If you build all the projects you can then "Build" the RUN_TESTS project.
+This will run the test suite against the Qpid version just built.
5. Tests
diff --git a/cpp/Makefile.am b/cpp/Makefile.am
index 01b8507454..9f4b8e2082 100644
--- a/cpp/Makefile.am
+++ b/cpp/Makefile.am
@@ -33,3 +33,7 @@ SUBDIRS = managementgen etc src docs/api docs/man examples bindings/qmf bindings
# Update libtool, if needed.
libtool: $(LIBTOOL_DEPS)
$(SHELL) ./config.status --recheck
+
+check-long: all
+ $(MAKE) -C src/tests check-long
+ \ No newline at end of file
diff --git a/cpp/bindings/qmf/python/Makefile.am b/cpp/bindings/qmf/python/Makefile.am
index 421590f189..8abad32959 100644
--- a/cpp/bindings/qmf/python/Makefile.am
+++ b/cpp/bindings/qmf/python/Makefile.am
@@ -30,11 +30,13 @@ BUILT_SOURCES = $(generated_file_list)
SWIG_FLAGS = -w362,401
$(generated_file_list): $(srcdir)/python.i $(srcdir)/../qmfengine.i
- swig -c++ -python $(SWIG_FLAGS) $(INCLUDES) $(QPID_CXXFLAGS) -I$(top_srcdir)/src/qmf -I/usr/include -o qmfengine.cpp $(srcdir)/python.i
+ $(SWIG) -c++ -python $(SWIG_FLAGS) $(INCLUDES) $(QPID_CXXFLAGS) -I$(top_srcdir)/src/qmf -I/usr/include -o qmfengine.cpp $(srcdir)/python.i
pylibdir = $(PYTHON_LIB)
lib_LTLIBRARIES = _qmfengine.la
+qenginedir = $(pyexecdir)
+qengine_PYTHON = qmfengine.py qmf.py
#_qmfengine_la_LDFLAGS = -avoid-version -module -shrext "$(PYTHON_SO)"
#_qmfengine_la_LDFLAGS = -avoid-version -module -shrext ".so"
diff --git a/cpp/bindings/qmf/ruby/Makefile.am b/cpp/bindings/qmf/ruby/Makefile.am
index cfb3a33870..de8c4d10d5 100644
--- a/cpp/bindings/qmf/ruby/Makefile.am
+++ b/cpp/bindings/qmf/ruby/Makefile.am
@@ -35,9 +35,9 @@ qmfengine.cpp: $(srcdir)/ruby.i $(srcdir)/../qmfengine.i
rubylibarchdir = $(RUBY_LIB_ARCH)
rubylibarch_LTLIBRARIES = qmfengine.la
-qmfengine_la_LDFLAGS = -avoid-version -module -shrext ".$(RUBY_DLEXT)"
+qmfengine_la_LDFLAGS = -avoid-version -module -shared -shrext ".$(RUBY_DLEXT)"
qmfengine_la_LIBADD = $(RUBY_LIBS) -L$(top_builddir)/src/.libs -lqpidclient $(top_builddir)/src/libqmfengine.la
-qmfengine_la_CXXFLAGS = $(INCLUDES) -I$(RUBY_INC) -I$(RUBY_INC_ARCH)
+qmfengine_la_CXXFLAGS = $(INCLUDES) -I$(RUBY_INC) -I$(RUBY_INC_ARCH) -fno-strict-aliasing
nodist_qmfengine_la_SOURCES = qmfengine.cpp
CLEANFILES = qmfengine.cpp
diff --git a/cpp/bindings/qmf/tests/test_base.rb b/cpp/bindings/qmf/tests/test_base.rb
index 3e4337a9c0..7d4609097c 100644
--- a/cpp/bindings/qmf/tests/test_base.rb
+++ b/cpp/bindings/qmf/tests/test_base.rb
@@ -24,6 +24,7 @@ require 'socket'
class ConsoleTestBase < Qmf::ConsoleHandler
def initialize
+ sleep(2)
@settings = Qmf::ConnectionSettings.new
@settings.host = ARGV[0] if ARGV.size > 0
@settings.port = ARGV[1].to_i if ARGV.size > 1
@@ -67,7 +68,7 @@ class ConsoleTestBase < Qmf::ConsoleHandler
def assert(condition, in_text=nil)
text = " (#{in_text})" if in_text
- raise "Assertion failed: #{left} != #{right}#{text}" unless condition
+ raise "Assertion failed: #{condition} #{text}" unless condition
end
def fail(text)
diff --git a/cpp/bindings/qmf2/examples/cpp/Makefile.am b/cpp/bindings/qmf2/examples/cpp/Makefile.am
index 84207d43c4..062fbd0a85 100644
--- a/cpp/bindings/qmf2/examples/cpp/Makefile.am
+++ b/cpp/bindings/qmf2/examples/cpp/Makefile.am
@@ -21,7 +21,7 @@ INCLUDE = -I$(top_srcdir)/include
AM_CPPFLAGS = $(INCLUDE)
-noinst_PROGRAMS=agent list_agents print_events
+noinst_PROGRAMS=agent event_driven_list_agents list_agents print_events
agent_SOURCES=agent.cpp
agent_LDADD=$(top_builddir)/src/libqmf2.la
@@ -29,5 +29,8 @@ agent_LDADD=$(top_builddir)/src/libqmf2.la
list_agents_SOURCES=list_agents.cpp
list_agents_LDADD=$(top_builddir)/src/libqmf2.la
+event_driven_list_agents_SOURCES=event_driven_list_agents.cpp
+event_driven_list_agents_LDADD=$(top_builddir)/src/libqmf2.la
+
print_events_SOURCES=print_events.cpp
print_events_LDADD=$(top_builddir)/src/libqmf2.la
diff --git a/cpp/bindings/qmf2/examples/cpp/event_driven_list_agents.cpp b/cpp/bindings/qmf2/examples/cpp/event_driven_list_agents.cpp
new file mode 100644
index 0000000000..c288aa6bdd
--- /dev/null
+++ b/cpp/bindings/qmf2/examples/cpp/event_driven_list_agents.cpp
@@ -0,0 +1,107 @@
+/*
+ * 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 <sys/select.h>
+#include <time.h>
+
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Duration.h>
+#include <qmf/Agent.h>
+#include <qmf/ConsoleEvent.h>
+#include <qmf/ConsoleSession.h>
+#include <qpid/types/Variant.h>
+#include "qmf/posix/EventNotifier.h"
+
+#include <string>
+#include <iostream>
+
+using namespace std;
+using namespace qmf;
+using qpid::types::Variant;
+using qpid::messaging::Duration;
+
+int main(int argc, char** argv)
+{
+ string url("localhost");
+ string connectionOptions;
+ string sessionOptions;
+
+ if (argc > 1)
+ url = argv[1];
+ if (argc > 2)
+ connectionOptions = argv[2];
+ if (argc > 3)
+ sessionOptions = argv[3];
+
+ qpid::messaging::Connection connection(url, connectionOptions);
+ connection.open();
+
+ ConsoleSession session(connection, sessionOptions);
+ session.open();
+ session.setAgentFilter("");
+
+ posix::EventNotifier notifier(session);
+
+ int fd(notifier.getHandle());
+ time_t lastUpdate;
+ bool ftl = false;
+
+ time(&lastUpdate);
+
+ while (true) {
+ fd_set rfds;
+ struct timeval tv;
+ int nfds, retval;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ nfds = fd + 1;
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+
+ retval = select(nfds, &rfds, NULL, NULL, &tv);
+
+ if (retval > 0 && FD_ISSET(fd, &rfds)) {
+ ConsoleEvent event;
+ while (session.nextEvent(event, Duration::IMMEDIATE)) {
+ string eventType = "";
+ switch(event.getType()) {
+ case CONSOLE_AGENT_ADD: eventType = "Added"; break;
+ case CONSOLE_AGENT_DEL: eventType = "Deleted"; break;
+ case CONSOLE_AGENT_RESTART: eventType = "Restarted"; break;
+ case CONSOLE_AGENT_SCHEMA_UPDATE: eventType = "Schema Updated"; break;
+ case CONSOLE_AGENT_SCHEMA_RESPONSE: eventType = "Schema Response"; break;
+ case CONSOLE_EVENT: eventType = "Event"; break;
+ case CONSOLE_QUERY_RESPONSE: eventType = "Query Response"; break;
+ case CONSOLE_METHOD_RESPONSE: eventType = "Method Response"; break;
+ case CONSOLE_EXCEPTION: eventType = "Exception"; break;
+ case CONSOLE_SUBSCRIBE_ADD: eventType = "Subscription Added"; break;
+ case CONSOLE_SUBSCRIBE_UPDATE: eventType = "Subscription Updated"; break;
+ case CONSOLE_SUBSCRIBE_DEL: eventType = "Subscription Deleted" ; break;
+ case CONSOLE_THREAD_FAILED: eventType = "Thread Failure"; break;
+ default: eventType = "[UNDEFINED]";
+ }
+ cout << "Agent " << eventType << ": " << event.getAgent().getName() << endl;
+ }
+ } else {
+ cout << "No message received within waiting period." << endl;
+ }
+ }
+}
+
diff --git a/cpp/bindings/qmf2/python/Makefile.am b/cpp/bindings/qmf2/python/Makefile.am
index 7adc62eddb..3dc04e832f 100644
--- a/cpp/bindings/qmf2/python/Makefile.am
+++ b/cpp/bindings/qmf2/python/Makefile.am
@@ -30,12 +30,12 @@ BUILT_SOURCES = $(generated_file_list)
SWIG_FLAGS = -w362,401
$(generated_file_list): $(srcdir)/python.i $(srcdir)/../qmf2.i $(srcdir)/../../swig_python_typemaps.i
- swig -c++ -python $(SWIG_FLAGS) $(INCLUDES) $(QPID_CXXFLAGS) -I/usr/include -o cqmf2.cpp $(srcdir)/python.i
+ $(SWIG) -c++ -python $(SWIG_FLAGS) $(INCLUDES) $(QPID_CXXFLAGS) -I/usr/include -o cqmf2.cpp $(srcdir)/python.i
pylibdir = $(PYTHON_LIB)
lib_LTLIBRARIES = _cqmf2.la
-cqpiddir = $(pythondir)
+cqpiddir = $(pyexecdir)
cqpid_PYTHON = qmf2.py cqmf2.py
_cqmf2_la_LDFLAGS = -avoid-version -module -shared
diff --git a/cpp/bindings/qmf2/qmf2.i b/cpp/bindings/qmf2/qmf2.i
index a09a95168f..0f573fe3e6 100644
--- a/cpp/bindings/qmf2/qmf2.i
+++ b/cpp/bindings/qmf2/qmf2.i
@@ -37,6 +37,7 @@
%}
+%include <qpid/ImportExport.h>
%include <qpid/messaging/ImportExport.h>
%include <qpid/messaging/Duration.h>
diff --git a/cpp/bindings/qmf2/ruby/Makefile.am b/cpp/bindings/qmf2/ruby/Makefile.am
index ae840f87c6..97bbc6f385 100644
--- a/cpp/bindings/qmf2/ruby/Makefile.am
+++ b/cpp/bindings/qmf2/ruby/Makefile.am
@@ -34,9 +34,9 @@ rubylibarchdir = $(RUBY_LIB_ARCH)
rubylibarch_LTLIBRARIES = cqmf2.la
dist_rubylib_DATA = qmf2.rb
-cqmf2_la_LDFLAGS = -avoid-version -module -shrext ".$(RUBY_DLEXT)"
+cqmf2_la_LDFLAGS = -avoid-version -module -shared -shrext ".$(RUBY_DLEXT)"
cqmf2_la_LIBADD = $(RUBY_LIBS) -L$(top_builddir)/src/.libs -lqmf2 $(top_builddir)/src/libqmf2.la
-cqmf2_la_CXXFLAGS = $(INCLUDES) -I$(RUBY_INC) -I$(RUBY_INC_ARCH)
+cqmf2_la_CXXFLAGS = $(INCLUDES) -I$(RUBY_INC) -I$(RUBY_INC_ARCH) -fno-strict-aliasing
nodist_cqmf2_la_SOURCES = cqmf2.cpp
CLEANFILES = cqmf2.cpp
diff --git a/cpp/bindings/qpid/CMakeLists.txt b/cpp/bindings/qpid/CMakeLists.txt
new file mode 100644
index 0000000000..7c9f76f991
--- /dev/null
+++ b/cpp/bindings/qpid/CMakeLists.txt
@@ -0,0 +1,41 @@
+#
+# 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(FindSWIG)
+include(UseSWIG)
+include(FindRuby)
+include(FindPythonLibs)
+include(FindPerlLibs)
+
+if (SWIG_FOUND)
+ set(CMAKE_SWIG_FLAGS "-w361,362,401,467,503")
+
+ if (PYTHONLIBS_FOUND)
+ add_subdirectory(python)
+ endif (PYTHONLIBS_FOUND)
+
+ if (RUBY_FOUND)
+ add_subdirectory(ruby)
+ endif (RUBY_FOUND)
+
+ if (PERLLIBS_FOUND)
+ add_subdirectory(perl)
+ endif (PERLLIBS_FOUND)
+endif (SWIG_FOUND)
diff --git a/cpp/bindings/qpid/Makefile.am b/cpp/bindings/qpid/Makefile.am
index ca9eda0c73..31bce5d1d5 100644
--- a/cpp/bindings/qpid/Makefile.am
+++ b/cpp/bindings/qpid/Makefile.am
@@ -21,7 +21,7 @@ SUBDIRS = dotnet
if HAVE_SWIG
-EXTRA_DIST = qpid.i
+EXTRA_DIST = CMakeLists.txt qpid.i
if HAVE_RUBY_DEVEL
SUBDIRS += ruby
diff --git a/cpp/bindings/qpid/dotnet/configure-windows.ps1 b/cpp/bindings/qpid/dotnet/configure-windows.ps1
index 34395911b9..23fc742e07 100644
--- a/cpp/bindings/qpid/dotnet/configure-windows.ps1
+++ b/cpp/bindings/qpid/dotnet/configure-windows.ps1
@@ -24,29 +24,32 @@
# This script configures a qpid\cpp developer build environment under Windows
# to enable working with cpp\bindings\qpid\dotnet binding source code.
#
+# * Supports multiple versions of Visual Studio (VS2008, VS2010) as CMake
+# generator.
+#
# * Supports 32-bit and/or 64-bit development platforms.
#
# * User chooses in-source or out-of-source build directories.
#
# - 'In-source' builds happen when CMake is run from directory qpid\cpp.
-# Hundreds of CMake-generated output files are placed in qpid\cpp\src.
+# Hundreds of CMake-generated output files are placed in qpid\cpp\src.
# These files go right on top of files that are part of the source tree
# in qpid\cpp\src.
-# In-source builds support only one platform.
+# In-source builds support only one platform.
# Choose only a 32-bit or a 64-bit platform but not both.
#
# - Out-of-source builds happen when the user chooses another directory
# under qpid in which to run CMake. Out-of-source builds are required
-# in order to build both x86 and x64 targets using the same source tree.
+# in order to build both x86 and x64 targets using the same source tree.
# For each build platform (32-bit x86 or Win32, or 64-bit x64) the user
-# specifies a build directory and a specific version of Boost.
+# specifies a build directory and a specific version of Boost.
# Many platform/Boost-version directories may reside side by side.
#
# * User chooses to run CMake or not.
#
# - When a new build directory is created then the user is given the
-# option of running CMake in that directory. Running CMake is a
-# necessary step as CMake creates important source, solution, and
+# option of running CMake in that directory. Running CMake is a
+# necessary step as CMake creates important source, solution, and
# project files.
#
# - If a directory "looks like" is has already had CMake run in it
@@ -63,7 +66,7 @@
# 3. CMake 2.8 (or later) must be installed. The cmake\bin directory
# must be in the user's path.
# 4. Boost library specifications may or may not be in the user's path.
-# The script author recommeds not to have Boost in the path and only
+# The script author recommeds not to have Boost in the path and only
# allow the Boost path to be specified by generated command procedures.
# 5. Visual Studio build environment must be installed.
#
@@ -90,7 +93,7 @@
# In this example the build dirs are new. The script will prompt
# asking if CMake is to run in the build directories. User chooses Yes.
#
-# Now this script runs CMake twice, once each with the 32-bit and 64-bit
+# Now this script runs CMake twice, once each with the 32-bit and 64-bit
# generators.
# * This step creates qpid-cpp.sln and related project files.
# C:\svn\qpid\build32\qpid-cpp.sln
@@ -107,7 +110,7 @@
# C:\svn\qpid\build64\setenv-messaging-x64-64bit.bat
#
# Next the user compiles solution qpid\build32\qpid-cpp.sln.
-#
+#
# Using the generated scripts:
#
# Case 1. Run an executable in 32-bit mode.
@@ -142,6 +145,11 @@ $global:txtPath = '$env:PATH'
$global:txtQR = '$env:QPID_BUILD_ROOT'
$global:txtWH = 'Write-Host'
+#############################
+# Visual Studio version selection dialog items and choice
+#
+[array]$global:VsVersionCmakeChoiceList = "Visual Studio 10", "Visual Studio 9 2008"
+$global:cmakeGenerator = ''
#############################
# Select-Folder
@@ -168,7 +176,7 @@ function AskYesOrNo ($Question="No question?", $Title="No Title?")
[Windows.Forms.MessageBoxIcon]::Question)
$result = $dlg -eq [Windows.Forms.DialogResult]::Yes
-
+
$result
}
@@ -188,7 +196,7 @@ function SanityCheckBoostPath ($path=0)
$toTest = ('include', 'lib')
foreach ($pattern in $toTest) {
- $target = Join-Path $path $pattern
+ $target = Join-Path $path $pattern
if (!(Test-Path -path $target)) {
$result = $false
}
@@ -196,7 +204,7 @@ function SanityCheckBoostPath ($path=0)
} else {
$result = $false
}
-
+
if (! $result) {
Write-Host "The path ""$displayPath"" does not appear to be a Boost root path."
}
@@ -219,7 +227,7 @@ function SanityCheckBuildPath ($path=0)
$toTest = ('CMakeFiles', 'docs', 'etc', 'examples', 'include',
'managementgen', 'src')
foreach ($pattern in $toTest) {
- $target = Join-Path $path $pattern
+ $target = Join-Path $path $pattern
if (!(Test-Path -path $target)) {
$result = $false
}
@@ -313,7 +321,7 @@ function WriteDotnetBindingEnvSetupBat
$out = @("@ECHO OFF
REM
-REM Call this command procedure from a command prompt to set up a $vsPlatform ($nBits-bit)
+REM Call this command procedure from a command prompt to set up a $vsPlatform ($nBits-bit)
REM $slnName environment
REM
REM > call $outfileName
@@ -329,6 +337,56 @@ ECHO Environment set for $slnName $vsPlatform $nBits-bit development.
$out | Out-File "$buildRoot\$outfileName" -encoding ASCII
}
+#############################
+# Return the SelectedItem from the dropdown list and close the form.
+#
+function Return-DropDown {
+ if ($DropDown.SelectedItem -ne $null) {
+ $global:cmakeGenerator = $DropDown.SelectedItem.ToString()
+ $Form.Close()
+ Write-Host "Selected generator: $global:cmakeGenerator"
+ }
+}
+
+#############################
+# Create the CMake generator form and launch it
+#
+function SelectCMakeGenerator {
+
+ $Form = New-Object System.Windows.Forms.Form
+
+ $Form.width = 350
+ $Form.height = 150
+ $Form.Text = ”Select CMake Generator”
+
+ $DropDown = new-object System.Windows.Forms.ComboBox
+ $DropDown.Location = new-object System.Drawing.Size(120,10)
+ $DropDown.Size = new-object System.Drawing.Size(150,30)
+
+ ForEach ($Item in $global:VsVersionCmakeChoiceList) {
+ $DropDown.Items.Add($Item)
+ }
+ $DropDown.SelectedIndex = 0
+
+ $Form.Controls.Add($DropDown)
+
+ $DropDownLabel = new-object System.Windows.Forms.Label
+ $DropDownLabel.Location = new-object System.Drawing.Size(10,10)
+ $DropDownLabel.size = new-object System.Drawing.Size(100,20)
+ $DropDownLabel.Text = "CMake generators"
+ $Form.Controls.Add($DropDownLabel)
+
+ $Button = new-object System.Windows.Forms.Button
+ $Button.Location = new-object System.Drawing.Size(120,50)
+ $Button.Size = new-object System.Drawing.Size(120,20)
+ $Button.Text = "Select a generator"
+ $Button.Add_Click({Return-DropDown})
+ $form.Controls.Add($Button)
+
+ $Form.Add_Shown({$Form.Activate()})
+ $Form.ShowDialog()
+}
+
#############################
# Main
@@ -341,6 +399,12 @@ ECHO Environment set for $slnName $vsPlatform $nBits-bit development.
[string] $cppDir = Resolve-Path (Join-Path $curDir "..\..\..")
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
+[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") | Out-Null
+
+#############################
+# User dialog to select a version of Visual Studio as CMake generator
+#
+SelectCMakeGenerator
#############################
# User dialog to get optional 32-bit boost and build paths
@@ -412,7 +476,7 @@ if ($make32) {
$env:BOOST_ROOT = "$boost32"
cd "$build32"
Write-Host "Running 32-bit CMake in $build32 ..."
- CMake -G "Visual Studio 9 2008" "-DCMAKE_INSTALL_PREFIX=install_x86" $cppDir
+ CMake -G "$global:cmakeGenerator" "-DCMAKE_INSTALL_PREFIX=install_x86" $cppDir
} else {
Write-Host "Skipped 32-bit CMake."
}
@@ -424,7 +488,7 @@ if ($make64) {
$env:BOOST_ROOT = "$boost64"
cd "$build64"
Write-Host "Running 64-bit CMake in $build64"
- CMake -G "Visual Studio 9 2008 Win64" "-DCMAKE_INSTALL_PREFIX=install_x64" $cppDir
+ CMake -G "$global:cmakeGenerator Win64" "-DCMAKE_INSTALL_PREFIX=install_x64" $cppDir
} else {
Write-Host "Skipped 64-bit CMake."
}
@@ -437,7 +501,7 @@ if ($make64) {
if ($defined32) {
Write-Host "Writing 32-bit scripts..."
-
+
###########
# Powershell script to launch org.apache.qpid.messaging.sln
#
@@ -448,8 +512,8 @@ if ($defined32) {
-vsPlatform "x86" `
-nBits "32" `
-outfileName "start-devenv-messaging-x86-32bit.ps1"
-
-
+
+
###########
# Batch script (that you doubleclick) to launch powershell script
# that launches org.apache.qpid.messaging.sln.
@@ -482,7 +546,7 @@ if ($defined32) {
if ($defined64) {
Write-Host "Writing 64-bit scripts..."
-
+
###########
# Powershell script to launch org.apache.qpid.messaging.sln
#
@@ -493,8 +557,8 @@ if ($defined64) {
-vsPlatform "x64" `
-nBits "64" `
-outfileName "start-devenv-messaging-x64-64bit.ps1"
-
-
+
+
###########
# Batch script (that you doubleclick) to launch powershell script
# that launches org.apache.qpid.messaging.sln.
diff --git a/cpp/bindings/qpid/dotnet/examples/csharp.direct.receiver/Properties/AssemblyInfo.cs b/cpp/bindings/qpid/dotnet/examples/csharp.direct.receiver/Properties/AssemblyInfo.cs
index abe35cf053..6976be5d02 100644
--- a/cpp/bindings/qpid/dotnet/examples/csharp.direct.receiver/Properties/AssemblyInfo.cs
+++ b/cpp/bindings/qpid/dotnet/examples/csharp.direct.receiver/Properties/AssemblyInfo.cs
@@ -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,7 +23,7 @@ using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-// General Information about an assembly is controlled through the following
+// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("csharp.direct.receiver")]
@@ -31,12 +31,12 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("csharp.direct.receiver")]
-[assembly: AssemblyCopyright("Copyright © 2010")]
+[assembly: AssemblyCopyright("Copyright 2011")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
@@ -46,11 +46,11 @@ using System.Runtime.InteropServices;
// Version information for an assembly consists of the following four values:
//
// Major Version
-// Minor Version
+// Minor Version
// Build Number
// Revision
//
-// You can specify all the values or you can default the Build and Revision Numbers
+// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
diff --git a/cpp/bindings/qpid/dotnet/examples/csharp.direct.sender/Properties/AssemblyInfo.cs b/cpp/bindings/qpid/dotnet/examples/csharp.direct.sender/Properties/AssemblyInfo.cs
index 18502a0666..12368def8e 100644
--- a/cpp/bindings/qpid/dotnet/examples/csharp.direct.sender/Properties/AssemblyInfo.cs
+++ b/cpp/bindings/qpid/dotnet/examples/csharp.direct.sender/Properties/AssemblyInfo.cs
@@ -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,7 +23,7 @@ using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-// General Information about an assembly is controlled through the following
+// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("csharp.direct.sender")]
@@ -31,12 +31,12 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("csharp.direct.sender")]
-[assembly: AssemblyCopyright("Copyright © 2010")]
+[assembly: AssemblyCopyright("Copyright 2011")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
@@ -46,11 +46,11 @@ using System.Runtime.InteropServices;
// Version information for an assembly consists of the following four values:
//
// Major Version
-// Minor Version
+// Minor Version
// Build Number
// Revision
//
-// You can specify all the values or you can default the Build and Revision Numbers
+// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
diff --git a/cpp/bindings/qpid/dotnet/examples/csharp.map.callback.receiver/Properties/AssemblyInfo.cs b/cpp/bindings/qpid/dotnet/examples/csharp.map.callback.receiver/Properties/AssemblyInfo.cs
index a87f92ccdf..459130ec6c 100644
--- a/cpp/bindings/qpid/dotnet/examples/csharp.map.callback.receiver/Properties/AssemblyInfo.cs
+++ b/cpp/bindings/qpid/dotnet/examples/csharp.map.callback.receiver/Properties/AssemblyInfo.cs
@@ -20,7 +20,7 @@ using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-// General Information about an assembly is controlled through the following
+// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("csharp.map.callback.receiver")]
@@ -28,12 +28,12 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("csharp.map.callback.receiver")]
-[assembly: AssemblyCopyright("Copyright © 2010")]
+[assembly: AssemblyCopyright("Copyright 2011")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
@@ -43,11 +43,11 @@ using System.Runtime.InteropServices;
// Version information for an assembly consists of the following four values:
//
// Major Version
-// Minor Version
+// Minor Version
// Build Number
// Revision
//
-// You can specify all the values or you can default the Build and Revision Numbers
+// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
diff --git a/cpp/bindings/qpid/dotnet/examples/csharp.map.callback.sender/Properties/AssemblyInfo.cs b/cpp/bindings/qpid/dotnet/examples/csharp.map.callback.sender/Properties/AssemblyInfo.cs
index e633f76673..2be4011f19 100644
--- a/cpp/bindings/qpid/dotnet/examples/csharp.map.callback.sender/Properties/AssemblyInfo.cs
+++ b/cpp/bindings/qpid/dotnet/examples/csharp.map.callback.sender/Properties/AssemblyInfo.cs
@@ -28,7 +28,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("csharp.map.callback.sender")]
-[assembly: AssemblyCopyright("Copyright © 2010")]
+[assembly: AssemblyCopyright("Copyright 2010")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
diff --git a/cpp/bindings/qpid/dotnet/examples/csharp.map.receiver/Properties/AssemblyInfo.cs b/cpp/bindings/qpid/dotnet/examples/csharp.map.receiver/Properties/AssemblyInfo.cs
index 694d6b9ce1..f11ce8c220 100644
--- a/cpp/bindings/qpid/dotnet/examples/csharp.map.receiver/Properties/AssemblyInfo.cs
+++ b/cpp/bindings/qpid/dotnet/examples/csharp.map.receiver/Properties/AssemblyInfo.cs
@@ -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,7 +23,7 @@ using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-// General Information about an assembly is controlled through the following
+// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("csharp.map.receiver")]
@@ -31,12 +31,12 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("csharp.map.receiver")]
-[assembly: AssemblyCopyright("Copyright © 2010")]
+[assembly: AssemblyCopyright("Copyright 2011")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
@@ -46,11 +46,11 @@ using System.Runtime.InteropServices;
// Version information for an assembly consists of the following four values:
//
// Major Version
-// Minor Version
+// Minor Version
// Build Number
// Revision
//
-// You can specify all the values or you can default the Build and Revision Numbers
+// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
diff --git a/cpp/bindings/qpid/dotnet/examples/csharp.map.sender/Properties/AssemblyInfo.cs b/cpp/bindings/qpid/dotnet/examples/csharp.map.sender/Properties/AssemblyInfo.cs
index ea29ac2417..ee09057f18 100644
--- a/cpp/bindings/qpid/dotnet/examples/csharp.map.sender/Properties/AssemblyInfo.cs
+++ b/cpp/bindings/qpid/dotnet/examples/csharp.map.sender/Properties/AssemblyInfo.cs
@@ -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,7 +23,7 @@ using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-// General Information about an assembly is controlled through the following
+// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("csharp.map.sender")]
@@ -31,12 +31,12 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("csharp.map.sender")]
-[assembly: AssemblyCopyright("Copyright © 2010")]
+[assembly: AssemblyCopyright("Copyright 2011")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
@@ -46,11 +46,11 @@ using System.Runtime.InteropServices;
// Version information for an assembly consists of the following four values:
//
// Major Version
-// Minor Version
+// Minor Version
// Build Number
// Revision
//
-// You can specify all the values or you can default the Build and Revision Numbers
+// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
diff --git a/cpp/bindings/qpid/dotnet/examples/visualbasic.example.client/MyProject/AssemblyInfo.vb b/cpp/bindings/qpid/dotnet/examples/visualbasic.example.client/MyProject/AssemblyInfo.vb
index d0727fe9fa..469d6ed5cf 100644
--- a/cpp/bindings/qpid/dotnet/examples/visualbasic.example.client/MyProject/AssemblyInfo.vb
+++ b/cpp/bindings/qpid/dotnet/examples/visualbasic.example.client/MyProject/AssemblyInfo.vb
@@ -6,9 +6,9 @@
' to you under the Apache License, Version 2.0 (the
' "License"); you may not use this file except in compliance
' with the License. You may obtain a copy of the License at
-'
+'
' http://www.apache.org/licenses/LICENSE-2.0
-'
+'
' Unless required by applicable law or agreed to in writing,
' software distributed under the License is distributed on an
' "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -21,34 +21,34 @@ Imports System
Imports System.Reflection
Imports System.Runtime.InteropServices
-' General Information about an assembly is controlled through the following
+' General Information about an assembly is controlled through the following
' set of attributes. Change these attribute values to modify the information
' associated with an assembly.
' Review the values of the assembly attributes
-<Assembly: AssemblyTitle("visualbasic.example.client")>
-<Assembly: AssemblyDescription("")>
-<Assembly: AssemblyCompany("Microsoft")>
-<Assembly: AssemblyProduct("visualbasic.example.client")>
-<Assembly: AssemblyCopyright("Copyright © Microsoft 2010")>
-<Assembly: AssemblyTrademark("")>
+<Assembly: AssemblyTitle("visualbasic.example.client")>
+<Assembly: AssemblyDescription("")>
+<Assembly: AssemblyCompany("")>
+<Assembly: AssemblyProduct("visualbasic.example.client")>
+<Assembly: AssemblyCopyright("Copyright 2011")>
+<Assembly: AssemblyTrademark("")>
<Assembly: ComVisible(False)>
'The following GUID is for the ID of the typelib if this project is exposed to COM
-<Assembly: Guid("ec9df8cf-c1d4-4938-9e72-93fb81d55700")>
+<Assembly: Guid("ec9df8cf-c1d4-4938-9e72-93fb81d55700")>
' Version information for an assembly consists of the following four values:
'
' Major Version
-' Minor Version
+' Minor Version
' Build Number
' Revision
'
-' You can specify all the values or you can default the Build and Revision Numbers
+' You can specify all the values or you can default the Build and Revision Numbers
' by using the '*' as shown below:
-' <Assembly: AssemblyVersion("1.0.*")>
+' <Assembly: AssemblyVersion("1.0.*")>
-<Assembly: AssemblyVersion("1.0.0.0")>
-<Assembly: AssemblyFileVersion("1.0.0.0")>
+<Assembly: AssemblyVersion("1.0.0.0")>
+<Assembly: AssemblyFileVersion("1.0.0.0")>
diff --git a/cpp/bindings/qpid/dotnet/org.apache.qpid.messaging.sessionreceiver.sln b/cpp/bindings/qpid/dotnet/org.apache.qpid.messaging.sessionreceiver.sln
index 90e98a4bbe..edf8af4808 100644
--- a/cpp/bindings/qpid/dotnet/org.apache.qpid.messaging.sessionreceiver.sln
+++ b/cpp/bindings/qpid/dotnet/org.apache.qpid.messaging.sessionreceiver.sln
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
-#
+#
# 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
@@ -8,9 +8,9 @@ Microsoft Visual Studio Solution File, Format Version 10.00
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
-#
+#
# http://www.apache.org/licenses/LICENSE-2.0
-#
+#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
diff --git a/cpp/bindings/qpid/dotnet/src/Address.cpp b/cpp/bindings/qpid/dotnet/src/Address.cpp
index b688d973ed..79a8021d9a 100644
--- a/cpp/bindings/qpid/dotnet/src/Address.cpp
+++ b/cpp/bindings/qpid/dotnet/src/Address.cpp
@@ -141,7 +141,7 @@ namespace Messaging {
}
}
- // copy constructor
+ // Copy constructor look-alike (C#)
Address::Address(const Address ^ address)
{
System::Exception ^ newException = nullptr;
@@ -163,6 +163,28 @@ namespace Messaging {
}
}
+ // Copy constructor implicitly dereferenced (C++)
+ Address::Address(const Address % address)
+ {
+ System::Exception ^ newException = nullptr;
+
+ try
+ {
+ addressp = new ::qpid::messaging::Address(
+ *(const_cast<Address %>(address).NativeAddress));
+ }
+ catch (const ::qpid::types::Exception & error)
+ {
+ String ^ errmsg = gcnew String(error.what());
+ newException = gcnew QpidException(errmsg);
+ }
+
+ if (newException != nullptr)
+ {
+ throw newException;
+ }
+ }
+
// unmanaged clone
Address::Address(const ::qpid::messaging::Address & addrp)
{
diff --git a/cpp/bindings/qpid/dotnet/src/Address.h b/cpp/bindings/qpid/dotnet/src/Address.h
index e5a00d8f11..8bbc207d4e 100644
--- a/cpp/bindings/qpid/dotnet/src/Address.h
+++ b/cpp/bindings/qpid/dotnet/src/Address.h
@@ -64,6 +64,7 @@ namespace Messaging {
// copy constructor
Address(const Address ^ address);
+ Address(const Address % address);
// unmanaged clone
Address(const ::qpid::messaging::Address & addrp);
diff --git a/cpp/bindings/qpid/dotnet/src/Connection.cpp b/cpp/bindings/qpid/dotnet/src/Connection.cpp
index 69ace7db52..12c0e29f74 100644
--- a/cpp/bindings/qpid/dotnet/src/Connection.cpp
+++ b/cpp/bindings/qpid/dotnet/src/Connection.cpp
@@ -114,7 +114,7 @@ namespace Messaging {
}
- // Copy constructor
+ // Copy constructor look-alike (C#)
Connection::Connection(const Connection ^ connection)
{
System::Exception ^ newException = nullptr;
@@ -136,6 +136,28 @@ namespace Messaging {
}
}
+ // Copy constructor implicitly dereferenced (C++)
+ Connection::Connection(const Connection % connection)
+ {
+ System::Exception ^ newException = nullptr;
+
+ try
+ {
+ connectionp = new ::qpid::messaging::Connection(
+ *(const_cast<Connection %>(connection).NativeConnection));
+ }
+ catch (const ::qpid::types::Exception & error)
+ {
+ String ^ errmsg = gcnew String(error.what());
+ newException = gcnew QpidException(errmsg);
+ }
+
+ if (newException != nullptr)
+ {
+ throw newException;
+ }
+ }
+
// Destructor
Connection::~Connection()
diff --git a/cpp/bindings/qpid/dotnet/src/Connection.h b/cpp/bindings/qpid/dotnet/src/Connection.h
index f9b62d4a08..0788f5d225 100644
--- a/cpp/bindings/qpid/dotnet/src/Connection.h
+++ b/cpp/bindings/qpid/dotnet/src/Connection.h
@@ -56,6 +56,7 @@ namespace Messaging {
// copy constructor
Connection(const Connection ^ connection);
+ Connection(const Connection % connection);
// unmanaged clone
// not defined
diff --git a/cpp/bindings/qpid/dotnet/src/Duration.h b/cpp/bindings/qpid/dotnet/src/Duration.h
index 213c338a59..d4239fae88 100644
--- a/cpp/bindings/qpid/dotnet/src/Duration.h
+++ b/cpp/bindings/qpid/dotnet/src/Duration.h
@@ -81,7 +81,17 @@ namespace Messaging {
Duration ^ result = gcnew Duration(multiplier * dur->Milliseconds);
return result;
}
- };
+
+ static bool operator == (Duration ^ a, Duration ^ b)
+ {
+ return a->Milliseconds == b->Milliseconds;
+ }
+
+ static bool operator != (Duration ^ a, Duration ^ b)
+ {
+ return a->Milliseconds != b->Milliseconds;
+ }
+};
public ref class DurationConstants sealed
{
diff --git a/cpp/bindings/qpid/dotnet/src/FailoverUpdates.h b/cpp/bindings/qpid/dotnet/src/FailoverUpdates.h
index 1dd92b8688..d82e276fc8 100644
--- a/cpp/bindings/qpid/dotnet/src/FailoverUpdates.h
+++ b/cpp/bindings/qpid/dotnet/src/FailoverUpdates.h
@@ -54,6 +54,7 @@ namespace Messaging {
// copy constructor
FailoverUpdates(const FailoverUpdates ^ failoverUpdates) {}
+ FailoverUpdates(const FailoverUpdates % failoverUpdates) {}
// assignment operator
FailoverUpdates % operator=(const FailoverUpdates % rhs)
diff --git a/cpp/bindings/qpid/dotnet/src/Message.cpp b/cpp/bindings/qpid/dotnet/src/Message.cpp
index fe7825134d..e5dbf845b3 100644
--- a/cpp/bindings/qpid/dotnet/src/Message.cpp
+++ b/cpp/bindings/qpid/dotnet/src/Message.cpp
@@ -235,7 +235,7 @@ namespace Messaging {
}
}
- // Copy constructor
+ // Copy constructor look-alike (C#)
Message::Message(const Message ^ message)
{
System::Exception ^ newException = nullptr;
@@ -257,7 +257,29 @@ namespace Messaging {
}
}
- // Property
+ // Copy constructor implicitly dereferenced (C++)
+ Message::Message(const Message % message)
+ {
+ System::Exception ^ newException = nullptr;
+
+ try
+ {
+ messagep = new ::qpid::messaging::Message(
+ *(const_cast<Message %>(message).NativeMessage));
+ }
+ catch (const ::qpid::types::Exception & error)
+ {
+ String ^ errmsg = gcnew String(error.what());
+ newException = gcnew QpidException(errmsg);
+ }
+
+ if (newException != nullptr)
+ {
+ throw newException;
+ }
+ }
+
+ // Property
void Message::SetProperty(System::String ^ name, System::Object ^ value)
{
System::Exception ^ newException = nullptr;
diff --git a/cpp/bindings/qpid/dotnet/src/Message.h b/cpp/bindings/qpid/dotnet/src/Message.h
index b92cc4200b..ac7f285fe5 100644
--- a/cpp/bindings/qpid/dotnet/src/Message.h
+++ b/cpp/bindings/qpid/dotnet/src/Message.h
@@ -71,6 +71,7 @@ namespace Messaging {
// Copy constructor
Message(const Message ^ message);
+ Message(const Message % message);
// unmanaged clone
Message(const ::qpid::messaging::Message & msgp);
diff --git a/cpp/bindings/qpid/dotnet/src/Receiver.cpp b/cpp/bindings/qpid/dotnet/src/Receiver.cpp
index 3c0d79b393..8aa77effbd 100644
--- a/cpp/bindings/qpid/dotnet/src/Receiver.cpp
+++ b/cpp/bindings/qpid/dotnet/src/Receiver.cpp
@@ -89,7 +89,7 @@ namespace Messaging {
}
- // Copy constructor
+ // Copy constructor look-alike (C#)
Receiver::Receiver(const Receiver ^ receiver) :
parentSession(receiver->parentSession)
{
@@ -112,6 +112,29 @@ namespace Messaging {
}
}
+ // Copy constructor implicitly dereferenced (C++)
+ Receiver::Receiver(const Receiver % receiver) :
+ parentSession(receiver.parentSession)
+ {
+ System::Exception ^ newException = nullptr;
+
+ try
+ {
+ receiverp = new ::qpid::messaging::Receiver(
+ *(const_cast<Receiver %>(receiver).NativeReceiver));
+ }
+ catch (const ::qpid::types::Exception & error)
+ {
+ String ^ errmsg = gcnew String(error.what());
+ newException = gcnew QpidException(errmsg);
+ }
+
+ if (newException != nullptr)
+ {
+ throw newException;
+ }
+ }
+
//
// Get(message)
diff --git a/cpp/bindings/qpid/dotnet/src/Receiver.h b/cpp/bindings/qpid/dotnet/src/Receiver.h
index e9912a61dd..8ddcc9ac01 100644
--- a/cpp/bindings/qpid/dotnet/src/Receiver.h
+++ b/cpp/bindings/qpid/dotnet/src/Receiver.h
@@ -65,6 +65,7 @@ namespace Messaging {
// copy constructor
Receiver(const Receiver ^ receiver);
+ Receiver(const Receiver % receiver);
// unmanaged clone
// undefined
diff --git a/cpp/bindings/qpid/dotnet/src/Sender.cpp b/cpp/bindings/qpid/dotnet/src/Sender.cpp
index 584075ef5f..3225f1a6e1 100644
--- a/cpp/bindings/qpid/dotnet/src/Sender.cpp
+++ b/cpp/bindings/qpid/dotnet/src/Sender.cpp
@@ -84,7 +84,7 @@ namespace Messaging {
}
- // Copy constructor
+ // Copy constructor look-alike (C#)
Sender::Sender(const Sender ^ sender)
: parentSession(sender->parentSession)
{
@@ -107,6 +107,29 @@ namespace Messaging {
}
}
+ // Copy constructor implicitly dereferenced (C++)
+ Sender::Sender(const Sender % sender)
+ : parentSession(sender.parentSession)
+ {
+ System::Exception ^ newException = nullptr;
+
+ try
+ {
+ senderp = new ::qpid::messaging::Sender(
+ *(const_cast<Sender %>(sender).NativeSender));
+ }
+ catch (const ::qpid::types::Exception & error)
+ {
+ String ^ errmsg = gcnew String(error.what());
+ newException = gcnew QpidException(errmsg);
+ }
+
+ if (newException != nullptr)
+ {
+ throw newException;
+ }
+ }
+
//
// Send(msg)
diff --git a/cpp/bindings/qpid/dotnet/src/Sender.h b/cpp/bindings/qpid/dotnet/src/Sender.h
index 0e90a9f4a4..4054e87316 100644
--- a/cpp/bindings/qpid/dotnet/src/Sender.h
+++ b/cpp/bindings/qpid/dotnet/src/Sender.h
@@ -62,6 +62,7 @@ namespace Messaging {
// copy constructor
Sender(const Sender ^ sender);
+ Sender(const Sender % sender);
~Sender();
!Sender();
diff --git a/cpp/bindings/qpid/dotnet/src/Session.cpp b/cpp/bindings/qpid/dotnet/src/Session.cpp
index 880331c588..0e918769a3 100644
--- a/cpp/bindings/qpid/dotnet/src/Session.cpp
+++ b/cpp/bindings/qpid/dotnet/src/Session.cpp
@@ -89,7 +89,7 @@ namespace Messaging {
}
- // Copy constructor
+ // Copy constructor look-alike (C#)
Session::Session(const Session ^ session)
: parentConnectionp(session->parentConnectionp)
{
@@ -113,6 +113,30 @@ namespace Messaging {
}
}
+ // Copy constructor implicitly dereferenced (C++)
+ Session::Session(const Session % session)
+ : parentConnectionp(session.parentConnectionp)
+ {
+ System::Exception ^ newException = nullptr;
+
+ try
+ {
+ sessionp = new ::qpid::messaging::Session(
+ *(const_cast<Session %>(session).NativeSession));
+
+ }
+ catch (const ::qpid::types::Exception & error)
+ {
+ String ^ errmsg = gcnew String(error.what());
+ newException = gcnew QpidException(errmsg);
+ }
+
+ if (newException != nullptr)
+ {
+ throw newException;
+ }
+ }
+
void Session::Close()
{
@@ -224,6 +248,31 @@ namespace Messaging {
}
}
+ void Session::AcknowledgeUpTo(Message ^ message)
+ {
+ AcknowledgeUpTo(message, false);
+ }
+
+ void Session::AcknowledgeUpTo(Message ^ message, bool sync)
+ {
+ System::Exception ^ newException = nullptr;
+
+ try
+ {
+ sessionp->acknowledgeUpTo(*(message->NativeMessage), sync);
+ }
+ catch (const ::qpid::types::Exception & error)
+ {
+ String ^ errmsg = gcnew String(error.what());
+ newException = gcnew QpidException(errmsg);
+ }
+
+ if (newException != nullptr)
+ {
+ throw newException;
+ }
+ }
+
void Session::Reject(Message ^ message)
{
System::Exception ^ newException = nullptr;
diff --git a/cpp/bindings/qpid/dotnet/src/Session.h b/cpp/bindings/qpid/dotnet/src/Session.h
index 7eaad8a0a5..4b98a37f18 100644
--- a/cpp/bindings/qpid/dotnet/src/Session.h
+++ b/cpp/bindings/qpid/dotnet/src/Session.h
@@ -69,6 +69,7 @@ namespace Messaging {
// copy constructor
Session(const Session ^ session);
+ Session(const Session % session);
~Session();
!Session();
@@ -103,6 +104,8 @@ namespace Messaging {
void Acknowledge(bool sync);
void Acknowledge(Message ^ message);
void Acknowledge(Message ^ message, bool sync);
+ void AcknowledgeUpTo(Message ^ message);
+ void AcknowledgeUpTo(Message ^ message, bool sync);
void Reject(Message ^);
void Release(Message ^);
void Sync();
diff --git a/cpp/bindings/qpid/dotnet/test/messaging.test/Properties/AssemblyInfo.cs b/cpp/bindings/qpid/dotnet/test/messaging.test/Properties/AssemblyInfo.cs
index cf50e88200..81a89ce393 100644
--- a/cpp/bindings/qpid/dotnet/test/messaging.test/Properties/AssemblyInfo.cs
+++ b/cpp/bindings/qpid/dotnet/test/messaging.test/Properties/AssemblyInfo.cs
@@ -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,7 +23,7 @@ using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-// General Information about an assembly is controlled through the following
+// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("messaging.test")]
@@ -31,12 +31,12 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("messaging.test")]
-[assembly: AssemblyCopyright("Copyright © 2010")]
+[assembly: AssemblyCopyright("Copyright 2011")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-// Setting ComVisible to false makes the types in this assembly not visible
-// to COM components. If you need to access a type in this assembly from
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
@@ -46,11 +46,11 @@ using System.Runtime.InteropServices;
// Version information for an assembly consists of the following four values:
//
// Major Version
-// Minor Version
+// Minor Version
// Build Number
// Revision
//
-// You can specify all the values or you can default the Build and Revision Numbers
+// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
diff --git a/cpp/bindings/qpid/examples/perl/client.pl b/cpp/bindings/qpid/examples/perl/client.pl
index 93eec88e07..19d9d3f14f 100644
--- a/cpp/bindings/qpid/examples/perl/client.pl
+++ b/cpp/bindings/qpid/examples/perl/client.pl
@@ -20,13 +20,13 @@
use strict;
use warnings;
-use cqpid;
+use cqpid_perl;
my $url = ( @ARGV == 1 ) ? $ARGV[0] : "amqp:tcp:127.0.0.1:5672";
my $connectionOptions = ( @ARGV > 1 ) ? $ARGV[1] : "";
-my $connection = new cqpid::Connection($url, $connectionOptions);
+my $connection = new cqpid_perl::Connection($url, $connectionOptions);
eval {
$connection->open();
@@ -35,7 +35,7 @@ my $session = $connection->createSession();
my $sender = $session->createSender("service_queue");
#create temp queue & receiver...
-my $responseQueue = new cqpid::Address("#response-queue; {create:always, delete:always}");
+my $responseQueue = new cqpid_perl::Address("#response-queue; {create:always, delete:always}");
my $receiver = $session->createReceiver($responseQueue);
#Now send some messages...
@@ -47,7 +47,7 @@ my @s = (
"And the mome raths outgrabe."
);
-my $request = new cqpid::Message();
+my $request = new cqpid_perl::Message();
$request->setReplyTo($responseQueue);
for (my $i=0; $i<4; $i++) {
$request->setContent($s[$i]);
diff --git a/cpp/bindings/qpid/examples/perl/drain.pl b/cpp/bindings/qpid/examples/perl/drain.pl
index 8010b7c95b..60ac0c50ed 100644
--- a/cpp/bindings/qpid/examples/perl/drain.pl
+++ b/cpp/bindings/qpid/examples/perl/drain.pl
@@ -20,7 +20,7 @@
use strict;
use warnings;
-use cqpid;
+use cqpid_perl;
use Getopt::Long;
my $url = "127.0.0.1";
@@ -47,11 +47,11 @@ if ($#ARGV ge 0) {
}
sub getTimeout {
- return ($forever) ? $cqpid::Duration::FOREVER : new cqpid::Duration($timeout*1000);
+ return ($forever) ? $cqpid_perl::Duration::FOREVER : new cqpid_perl::Duration($timeout*1000);
}
-my $connection = new cqpid::Connection($url, $connectionOptions);
+my $connection = new cqpid_perl::Connection($url, $connectionOptions);
eval {
$connection->open();
@@ -59,13 +59,13 @@ eval {
my $receiver = $session->createReceiver($address);
my $timeout = getTimeout();
- my $message = new cqpid::Message();
+ my $message = new cqpid_perl::Message();
my $i = 0;
while($receiver->fetch($message, $timeout)) {
print "Message(properties=" . $message->getProperties() . ",content='";
if ($message->getContentType() eq "amqp/map") {
- my $content = cqpid::decodeMap($message);
+ my $content = cqpid_perl::decodeMap($message);
map{ print "\n$_ => $content->{$_}"; } keys %{$content};
}
else {
@@ -77,7 +77,7 @@ eval {
if ($replyto->getName()) {
print "Replying to " . $message->getReplyTo()->str() . "...\n";
my $sender = $session->createSender($replyto);
- my $response = new cqpid::Message("received by the server.");
+ my $response = new cqpid_perl::Message("received by the server.");
$sender->send($response);
}
$session->acknowledge();
diff --git a/cpp/bindings/qpid/examples/perl/hello_world.pl b/cpp/bindings/qpid/examples/perl/hello_world.pl
index cf2f05f8b7..a96b98a002 100644
--- a/cpp/bindings/qpid/examples/perl/hello_world.pl
+++ b/cpp/bindings/qpid/examples/perl/hello_world.pl
@@ -21,13 +21,13 @@ use strict;
use warnings;
use Data::Dumper;
-use cqpid;
+use cqpid_perl;
my $broker = ( @ARGV > 0 ) ? $ARGV[0] : "localhost:5672";
my $address = ( @ARGV > 1 ) ? $ARGV[0] : "amq.topic";
my $connectionOptions = ( @ARGV > 2 ) ? $ARGV[1] : "";
-my $connection = new cqpid::Connection($broker, $connectionOptions);
+my $connection = new cqpid_perl::Connection($broker, $connectionOptions);
eval {
$connection->open();
@@ -36,12 +36,12 @@ eval {
my $receiver = $session->createReceiver($address);
my $sender = $session->createSender($address);
- $sender->send(new cqpid::Message("Hello world!"));
+ $sender->send(new cqpid_perl::Message("Hello world!"));
- #my $duration = new cqpid::Duration(1000);
+ #my $duration = new cqpid_perl::Duration(1000);
#print ">>>" . $duration->getMilliseconds() . "\n";
- my $message = $receiver->fetch($cqpid::Duration::SECOND);
+ my $message = $receiver->fetch($cqpid_perl::Duration::SECOND);
#$message->setDurable(1);
#print "Durable: " . $message->getDurable() . "\n";
diff --git a/cpp/bindings/qpid/examples/perl/hello_xml.pl b/cpp/bindings/qpid/examples/perl/hello_xml.pl
index c48a5225c2..cebf2ceee6 100644
--- a/cpp/bindings/qpid/examples/perl/hello_xml.pl
+++ b/cpp/bindings/qpid/examples/perl/hello_xml.pl
@@ -20,7 +20,7 @@
use strict;
use warnings;
-use cqpid;
+use cqpid_perl;
my $broker = ( @ARGV > 0 ) ? $ARGV[0] : "localhost:5672";
my $connectionOptions = ( @ARGV > 1 ) ? $ARGV[1] : "";
@@ -44,7 +44,7 @@ x-bindings: [{ exchange: xml-exchange, key: weather, arguments: { xquery:" $quer
END
-my $connection = new cqpid::Connection($broker, $connectionOptions);
+my $connection = new cqpid_perl::Connection($broker, $connectionOptions);
eval {
$connection->open();
@@ -52,7 +52,7 @@ eval {
my $receiver = $session->createReceiver($address);
- my $message = new cqpid::Message();
+ my $message = new cqpid_perl::Message();
my $content = <<END;
<weather>
diff --git a/cpp/bindings/qpid/examples/perl/map_receiver.pl b/cpp/bindings/qpid/examples/perl/map_receiver.pl
index e3e8a201dd..2e2611e38f 100644
--- a/cpp/bindings/qpid/examples/perl/map_receiver.pl
+++ b/cpp/bindings/qpid/examples/perl/map_receiver.pl
@@ -21,21 +21,21 @@ use strict;
use warnings;
use Data::Dumper;
-use cqpid;
+use cqpid_perl;
my $url = ( @ARGV > 0 ) ? $ARGV[0] : "amqp:tcp:127.0.0.1:5672";
my $address = ( @ARGV > 1 ) ? $ARGV[0] : "message_queue; {create: always}";
my $connectionOptions = ( @ARGV > 2 ) ? $ARGV[1] : "";
-my $connection = new cqpid::Connection($url, $connectionOptions);
+my $connection = new cqpid_perl::Connection($url, $connectionOptions);
eval {
$connection->open();
my $session = $connection->createSession();
my $receiver = $session->createReceiver($address);
- my $content = cqpid::decodeMap($receiver->fetch());
- #my $content = cqpid::decodeList($receiver->fetch());
+ my $content = cqpid_perl::decodeMap($receiver->fetch());
+ #my $content = cqpid_perl::decodeList($receiver->fetch());
print Dumper($content);
diff --git a/cpp/bindings/qpid/examples/perl/map_sender.pl b/cpp/bindings/qpid/examples/perl/map_sender.pl
index 095acce0ab..4107cd48b9 100644
--- a/cpp/bindings/qpid/examples/perl/map_sender.pl
+++ b/cpp/bindings/qpid/examples/perl/map_sender.pl
@@ -21,13 +21,13 @@ use strict;
use warnings;
use Data::Dumper;
-use cqpid;
+use cqpid_perl;
my $url = ( @ARGV > 0 ) ? $ARGV[0] : "amqp:tcp:127.0.0.1:5672";
my $address = ( @ARGV > 1 ) ? $ARGV[1] : "message_queue; {create: always}";
my $connectionOptions = ( @ARGV > 2 ) ? $ARGV[2] : "";
-my $connection = new cqpid::Connection($url, $connectionOptions);
+my $connection = new cqpid_perl::Connection($url, $connectionOptions);
eval {
$connection->open();
@@ -35,13 +35,13 @@ eval {
my $session = $connection->createSession();
my $sender = $session->createSender($address);
- my $message = new cqpid::Message();
+ my $message = new cqpid_perl::Message();
my $content = { id => 987654321,
name => "Widget",
percent => sprintf("%.2f", 0.99),
colours => [ qw (red green white) ],
};
- cqpid::encode($content, $message);
+ cqpid_perl::encode($content, $message);
$sender->send($message, 1);
$connection->close();
diff --git a/cpp/bindings/qpid/examples/perl/server.pl b/cpp/bindings/qpid/examples/perl/server.pl
index 0c64f15c66..b14da565b9 100644
--- a/cpp/bindings/qpid/examples/perl/server.pl
+++ b/cpp/bindings/qpid/examples/perl/server.pl
@@ -20,13 +20,13 @@
use strict;
use warnings;
-use cqpid;
+use cqpid_perl;
my $url = ( @ARGV == 1 ) ? $ARGV[0] : "amqp:tcp:127.0.0.1:5672";
my $connectionOptions = ( @ARGV > 1 ) ? $ARGV[1] : "";
-my $connection = new cqpid::Connection($url, $connectionOptions);
+my $connection = new cqpid_perl::Connection($url, $connectionOptions);
eval {
$connection->open();
@@ -41,7 +41,7 @@ eval {
my $sender = $session->createSender($address);
my $s = $request->getContent();
$s = uc($s);
- my $response = new cqpid::Message($s);
+ my $response = new cqpid_perl::Message($s);
$sender->send($response);
print "Processed request: " . $request->getContent() . " -> " . $response->getContent() . "\n";
$session->acknowledge();
diff --git a/cpp/bindings/qpid/examples/perl/spout.pl b/cpp/bindings/qpid/examples/perl/spout.pl
index 50773a4fe2..7365e732bf 100644
--- a/cpp/bindings/qpid/examples/perl/spout.pl
+++ b/cpp/bindings/qpid/examples/perl/spout.pl
@@ -20,7 +20,7 @@
use strict;
use warnings;
-use cqpid;
+use cqpid_perl;
use Getopt::Long;
use Time::Local;
@@ -77,19 +77,19 @@ sub setProperties {
}
}
-my $connection = new cqpid::Connection($url, $connectionOptions);
+my $connection = new cqpid_perl::Connection($url, $connectionOptions);
eval {
$connection->open();
my $session = $connection->createSession();
my $sender = $session->createSender($address);
- my $message = new cqpid::Message();
+ my $message = new cqpid_perl::Message();
setProperties($message) if (@properties);
if (@entries) {
my $content = {};
setEntries($content);
- cqpid::encode($content, $message);
+ cqpid_perl::encode($content, $message);
}
elsif ($content) {
$message->setContent($content);
@@ -98,7 +98,7 @@ eval {
my $receiver;
if ($replyto) {
- my $responseQueue = new cqpid::Address($replyto);
+ my $responseQueue = new cqpid_perl::Address($replyto);
$receiver = $session->createReceiver($responseQueue);
$message->setReplyTo($responseQueue);
}
diff --git a/cpp/bindings/qpid/perl/CMakeLists.txt b/cpp/bindings/qpid/perl/CMakeLists.txt
new file mode 100644
index 0000000000..6edaf284b1
--- /dev/null
+++ b/cpp/bindings/qpid/perl/CMakeLists.txt
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+##------------------------------------------------------
+## Use Swig to generate a literal binding to the C++ API
+##------------------------------------------------------
+set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/perl.i PROPERTIES CPLUSPLUS ON)
+set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/perl.i PROPERTIES SWIG_FLAGS "-I${qpid-cpp_SOURCE_DIR}/include")
+
+swig_add_module(cqpid_perl perl ${CMAKE_CURRENT_SOURCE_DIR}/perl.i)
+swig_link_libraries(cqpid_perl qpidmessaging qpidtypes qmf2 ${PERL_LIBRARY})
+
+set_source_files_properties(${swig_generated_file_fullname} PROPERTIES COMPILE_FLAGS "-fno-strict-aliasing -I${PERL_INCLUDE_PATH} -I${qpid-cpp_SOURCE_DIR}/include")
+
+##----------------------------------
+## Install the complete Perl binding
+##----------------------------------
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libcqpid_perl.so
+ ${CMAKE_CURRENT_BINARY_DIR}/cqpid_perl.pm
+ DESTINATION ${PERL_VENDORARCH}
+ COMPONENT ${QPID_COMPONENT_CLIENT}
+ )
diff --git a/cpp/bindings/qpid/perl/Makefile.am b/cpp/bindings/qpid/perl/Makefile.am
index 982d493ba0..da082896e8 100644
--- a/cpp/bindings/qpid/perl/Makefile.am
+++ b/cpp/bindings/qpid/perl/Makefile.am
@@ -21,22 +21,22 @@ if HAVE_PERL_DEVEL
INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src -I$(top_builddir)/src -I$(PERL_INC)
-EXTRA_DIST = perl.i
-BUILT_SOURCES = cqpid.cpp
+EXTRA_DIST = CMakeLists.txt perl.i
+BUILT_SOURCES = cqpid_perl.cpp
SWIG_FLAGS = -w362,401
-cqpid.cpp: $(srcdir)/perl.i $(srcdir)/../qpid.i $(srcdir)/../../swig_perl_typemaps.i
- $(SWIG) -perl -c++ $(SWIG_FLAGS) $(INCLUDES) $(QPID_CXXFLAGS) -I/usr/include -o cqpid.cpp $(srcdir)/perl.i
+cqpid_perl.cpp: $(srcdir)/perl.i $(srcdir)/../qpid.i $(srcdir)/../../swig_perl_typemaps.i
+ $(SWIG) -perl -c++ $(SWIG_FLAGS) $(INCLUDES) $(QPID_CXXFLAGS) -I/usr/include -o cqpid_perl.cpp $(srcdir)/perl.i
-lib_LTLIBRARIES = cqpid.la
-cqpid_PERL = cqpid.pm
+lib_LTLIBRARIES = libcqpid_perl.la
+cqpid_perl_PERL = cqpid_perl.pm
-cqpid_la_LDFLAGS = -avoid-version -module -shared
-cqpid_la_LIBADD = -L$(top_builddir)/src/.libs -lqpidmessaging -lqpidtypes \
+libcqpid_perl_la_LDFLAGS = -avoid-version -shared
+libcqpid_perl_la_LIBADD = -L$(top_builddir)/src/.libs -lqpidmessaging -lqpidtypes \
$(top_builddir)/src/libqpidmessaging.la $(top_builddir)/src/libqpidtypes.la
-cqpid_la_CXXFLAGS = $(INCLUDES)
-nodist_cqpid_la_SOURCES = cqpid.cpp
+libcqpid_perl_la_CXXFLAGS = $(INCLUDES) -fno-strict-aliasing
+nodist_libcqpid_perl_la_SOURCES = cqpid_perl.cpp
-CLEANFILES = cqpid.cpp cqpid.pm
+CLEANFILES = cqpid_perl.cpp cqpid_perl.pm
endif # HAVE_PERL_DEVEL
diff --git a/cpp/bindings/qpid/perl/perl.i b/cpp/bindings/qpid/perl/perl.i
index b7ae0568b6..38ac91761f 100644
--- a/cpp/bindings/qpid/perl/perl.i
+++ b/cpp/bindings/qpid/perl/perl.i
@@ -17,7 +17,7 @@
* under the License.
*/
-%module cqpid
+%module cqpid_perl
%include "std_string.i"
%include "../../swig_perl_typemaps.i"
diff --git a/cpp/bindings/qpid/python/CMakeLists.txt b/cpp/bindings/qpid/python/CMakeLists.txt
new file mode 100644
index 0000000000..5e4649cd7c
--- /dev/null
+++ b/cpp/bindings/qpid/python/CMakeLists.txt
@@ -0,0 +1,45 @@
+#
+# 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.
+#
+
+##------------------------------------------------------
+## Use Swig to generate a literal binding to the C++ API
+##------------------------------------------------------
+set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/python.i PROPERTIES CPLUSPLUS ON)
+set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/python.i PROPERTIES SWIG_FLAGS "-I${qpid-cpp_SOURCE_DIR}/include")
+
+swig_add_module(cqpid python ${CMAKE_CURRENT_SOURCE_DIR}/python.i)
+swig_link_libraries(cqpid qpidmessaging qpidtypes qmf2 ${PYTHON_LIBRARIES})
+
+set_source_files_properties(${swig_generated_file_fullname} PROPERTIES COMPILE_FLAGS "-fno-strict-aliasing -I${PYTHON_INCLUDE_PATH} -I${qpid-cpp_SOURCE_DIR}/include")
+
+##------------------------------------
+## Install the complete Python binding
+##------------------------------------
+execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()" OUTPUT_VARIABLE PYTHON_SITE_PACKAGES OUTPUT_STRIP_TRAILING_WHITESPACE)
+install(CODE "execute_process(COMMAND ${PYTHON_EXECUTABLE} -m py_compile cqpid.py
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})")
+install(CODE "execute_process(COMMAND ${PYTHON_EXECUTABLE} -O -m py_compile cqpid.py
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})")
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cqpid.py
+ ${CMAKE_CURRENT_BINARY_DIR}/cqpid.pyc
+ ${CMAKE_CURRENT_BINARY_DIR}/cqpid.pyo
+ ${CMAKE_CURRENT_BINARY_DIR}/_cqpid.so
+ DESTINATION ${PYTHON_SITE_PACKAGES}
+ COMPONENT ${QPID_COMPONENT_CLIENT}
+ )
diff --git a/cpp/bindings/qpid/python/Makefile.am b/cpp/bindings/qpid/python/Makefile.am
index 7fa4106be0..dd25f34829 100644
--- a/cpp/bindings/qpid/python/Makefile.am
+++ b/cpp/bindings/qpid/python/Makefile.am
@@ -25,17 +25,17 @@ generated_file_list = \
cqpid.cpp \
cqpid.py
-EXTRA_DIST = python.i
+EXTRA_DIST = CMakeLists.txt python.i
BUILT_SOURCES = $(generated_file_list)
SWIG_FLAGS = -w362,401
$(generated_file_list): $(srcdir)/python.i $(srcdir)/../qpid.i $(srcdir)/../../swig_python_typemaps.i
- swig -c++ -python $(SWIG_FLAGS) $(INCLUDES) $(QPID_CXXFLAGS) -I$(top_srcdir)/src/qmf -I/usr/include -o cqpid.cpp $(srcdir)/python.i
+ $(SWIG) -c++ -python $(SWIG_FLAGS) $(INCLUDES) $(QPID_CXXFLAGS) -I$(top_srcdir)/src/qmf -I/usr/include -o cqpid.cpp $(srcdir)/python.i
pylibdir = $(PYTHON_LIB)
lib_LTLIBRARIES = _cqpid.la
-cqpiddir = $(pythondir)
+cqpiddir = $(pyexecdir)
cqpid_PYTHON = cqpid.py
_cqpid_la_LDFLAGS = -avoid-version -module -shared
diff --git a/cpp/bindings/qpid/python/python.i b/cpp/bindings/qpid/python/python.i
index bf61cb10b7..9d45bf54ee 100644
--- a/cpp/bindings/qpid/python/python.i
+++ b/cpp/bindings/qpid/python/python.i
@@ -21,21 +21,357 @@
%include "std_string.i"
%include "../../swig_python_typemaps.i"
+/* Needed for get/setPriority methods. Surprising SWIG 1.3.40 doesn't
+ * convert uint8_t by default. */
+%apply unsigned char { uint8_t };
+
+
+/*
+ * Exceptions
+ *
+ * The convention below is that exceptions in _cqpid.so have the same
+ * names as in the C++ library. They get renamed to their Python
+ * equivalents when brought into the Python wrapping
+ */
+%{
+static PyObject* pNoMessageAvailable;
+static PyObject* pTargetCapacityExceeded;
+static PyObject* pNotFound;
+static PyObject* pTransportFailure;
+%}
+
+%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);
+%}
+
+%pythoncode %{
+ Empty = _cqpid.NoMessageAvailable
+ TargetCapacityExceeded = _cqpid.TargetCapacityExceeded
+ NotFound = _cqpid.NotFound
+ ConnectError = _cqpid.TransportFailure
+%}
+
/* Define the general-purpose exception handling */
%exception {
+ PyObject * pExceptionType = NULL;
std::string error;
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();
}
Py_END_ALLOW_THREADS;
if (!error.empty()) {
- PyErr_SetString(PyExc_RuntimeError, error.c_str());
+ PyErr_SetString(pExceptionType, error.c_str());
return NULL;
}
}
+
+/* This only renames the non-const version (I believe). Then again, I
+ * don't even know why there is a non-const version of the method. */
+%rename(opened) qpid::messaging::Connection::isOpen();
+%rename(receiver) qpid::messaging::Session::createReceiver;
+%rename(sender) qpid::messaging::Session::createSender;
+%rename(_acknowledge_all) qpid::messaging::Session::acknowledge(bool);
+%rename(_acknowledge_msg) qpid::messaging::Session::acknowledge(
+ Message &, bool);
+
+%rename(_fetch) qpid::messaging::Receiver::fetch;
+%rename(unsettled) qpid::messaging::Receiver::getUnsettled;
+%rename(available) qpid::messaging::Receiver::getAvailable;
+
+%rename(unsettled) qpid::messaging::Sender::getUnsettled;
+%rename(available) qpid::messaging::Sender::getAvailable;
+%rename(_send) qpid::messaging::Sender::send;
+
+%rename(_getReplyTo) qpid::messaging::Message::getReplyTo;
+%rename(_setReplyTo) qpid::messaging::Message::setReplyTo;
+%rename(_getTtl) qpid::messaging::Message::getTtl;
+%rename(_setTtl) qpid::messaging::Message::setTtl;
+
+
%include "../qpid.i"
+%extend qpid::messaging::Connection {
+ %pythoncode %{
+ # Handle the different options by converting underscores to hyphens.
+ # Also, the sasl_mechanisms option in Python has no direct
+ # equivalent in C++, so we will translate them to sasl_mechanism
+ # when possible.
+ def __init__(self, url=None, **options):
+ args = [url] if url else []
+ if options :
+ if "sasl_mechanisms" in options :
+ if ' ' in options.get("sasl_mechanisms",'') :
+ raise Exception(
+ "C++ Connection objects are unable to handle "
+ "multiple sasl-mechanisms")
+ options["sasl_mechanism"] = options.pop("sasl_mechanisms")
+ args.append(options)
+ this = _cqpid.new_Connection(*args)
+ try: self.this.append(this)
+ except: self.this = this
+ %}
+
+ /* Return a pre-existing session with the given name, if one
+ * exists, otherwise return a new one. (Note that if a
+ * pre-existing session exists, the transactional argument is
+ * ignored, and the returned session might not satisfy the desired
+ * setting. */
+ qpid::messaging::Session _session(const std::string & name,
+ bool transactional) {
+ if (!name.empty()) {
+ try {
+ return self->getSession(name);
+ }
+ catch (const qpid::messaging::KeyError &) {
+ }
+ }
+ if (transactional) {
+ return self->createTransactionalSession(name);
+ }
+ else {
+ return self->createSession(name);
+ }
+ }
+
+ %pythoncode %{
+ def session(self, name=None, transactional=False) :
+ if name is None :
+ name = ''
+ return self._session(name, transactional)
+ %}
+
+ %pythoncode %{
+ @staticmethod
+ def establish(url=None, **options) :
+ conn = Connection(url, **options)
+ conn.open()
+ return conn
+ %}
+}
+
+%extend qpid::messaging::Session {
+ %pythoncode %{
+ def acknowledge(self, message=None, disposition=None, sync=True) :
+ if disposition :
+ raise Exception("SWIG does not support dispositions yet. Use "
+ "Session.reject and Session.release instead")
+ if message :
+ self._acknowledge_msg(message, sync)
+ else :
+ self._acknowledge_all(sync)
+
+ __swig_getmethods__["connection"] = getConnection
+ if _newclass: connection = _swig_property(getConnection)
+ %}
+}
+
+
+%extend qpid::messaging::Receiver {
+ %pythoncode %{
+ __swig_getmethods__["capacity"] = getCapacity
+ __swig_setmethods__["capacity"] = setCapacity
+ if _newclass: capacity = _swig_property(getCapacity, setCapacity)
+
+ __swig_getmethods__["session"] = getSession
+ if _newclass: session = _swig_property(getSession)
+ %}
+
+ %pythoncode %{
+ def fetch(self, timeout=None) :
+ if timeout is None :
+ return self._fetch()
+ else :
+ # Python API uses timeouts in seconds,
+ # but C++ API uses milliseconds
+ return self._fetch(Duration(int(1000*timeout)))
+ %}
+}
+
+%extend qpid::messaging::Sender {
+ %pythoncode %{
+ def send(self, object, sync=True) :
+ if isinstance(object, Message):
+ message = object
+ else:
+ message = Message(object)
+ return self._send(message, sync)
+
+ __swig_getmethods__["capacity"] = getCapacity
+ __swig_setmethods__["capacity"] = setCapacity
+ if _newclass: capacity = _swig_property(getCapacity, setCapacity)
+
+ __swig_getmethods__["session"] = getSession
+ if _newclass: session = _swig_property(getSession)
+ %}
+}
+
+
+%extend qpid::messaging::Message {
+ %pythoncode %{
+ # UNSPECIFIED was module level before, but I do not
+ # know how to insert python code at the top of the module.
+ # (A bare "%pythoncode" inserts at the end.
+ UNSPECIFIED=object()
+ def __init__(self, content=None, content_type=UNSPECIFIED, id=None,
+ subject=None, user_id=None, reply_to=None,
+ correlation_id=None, durable=None, priority=None,
+ ttl=None, properties=None):
+ this = _cqpid.new_Message('')
+ try: self.this.append(this)
+ except: self.this = this
+ if content :
+ self.content = content
+ if content_type != UNSPECIFIED :
+ self.content_type = content_type
+ if id is not None :
+ self.id = id
+ if subject is not None :
+ self.subject = subject
+ if user_id is not None :
+ self.user_id = user_id
+ if reply_to is not None :
+ self.reply_to = reply_to
+ if correlation_id is not None :
+ self.correlation_id = correlation_id
+ if durable is not None :
+ self.durable = durable
+ if priority is not None :
+ self.priority = priority
+ if ttl is not None :
+ self.ttl = ttl
+ if properties is not None :
+ # Can't set properties via (inst).getProperties, because
+ # the typemaps make a copy of the underlying properties.
+ # Instead, set via setProperty for the time-being
+ for k, v in properties.iteritems() :
+ self.setProperty(k, v)
+
+ def _get_content(self) :
+ if self.content_type == "amqp/list" :
+ return decodeList(self)
+ if self.content_type == "amqp/map" :
+ return decodeMap(self)
+ return self.getContent()
+ def _set_content(self, content) :
+ if isinstance(content, basestring) :
+ self.setContent(content)
+ elif isinstance(content, list) or isinstance(content, dict) :
+ encode(content, self)
+ else :
+ # Not a type we can handle. Try setting it anyway,
+ # although this will probably lead to a swig error
+ self.setContent(content)
+ __swig_getmethods__["content"] = _get_content
+ __swig_setmethods__["content"] = _set_content
+ if _newclass: content = _swig_property(_get_content, _set_content)
+
+ __swig_getmethods__["content_type"] = getContentType
+ __swig_setmethods__["content_type"] = setContentType
+ if _newclass: content_type = _swig_property(getContentType,
+ setContentType)
+
+ __swig_getmethods__["id"] = getMessageId
+ __swig_setmethods__["id"] = setMessageId
+ if _newclass: id = _swig_property(getMessageId, setMessageId)
+
+ __swig_getmethods__["subject"] = getSubject
+ __swig_setmethods__["subject"] = setSubject
+ if _newclass: subject = _swig_property(getSubject, setSubject)
+
+ __swig_getmethods__["priority"] = getPriority
+ __swig_setmethods__["priority"] = setPriority
+ if _newclass: priority = _swig_property(getPriority, setPriority)
+
+ def getTtl(self) :
+ return self._getTtl().getMilliseconds()/1000.0
+ def setTtl(self, duration) :
+ self._setTtl(Duration(int(1000*duration)))
+ __swig_getmethods__["ttl"] = getTtl
+ __swig_setmethods__["ttl"] = setTtl
+ if _newclass: ttl = _swig_property(getTtl, setTtl)
+
+ __swig_getmethods__["user_id"] = getUserId
+ __swig_setmethods__["user_id"] = setUserId
+ if _newclass: user_id = _swig_property(getUserId, setUserId)
+
+ __swig_getmethods__["correlation_id"] = getCorrelationId
+ __swig_setmethods__["correlation_id"] = setCorrelationId
+ if _newclass: correlation_id = _swig_property(getCorrelationId,
+ setCorrelationId)
+
+ __swig_getmethods__["redelivered"] = getRedelivered
+ __swig_setmethods__["redelivered"] = setRedelivered
+ if _newclass: redelivered = _swig_property(getRedelivered,
+ setRedelivered)
+
+ __swig_getmethods__["durable"] = getDurable
+ __swig_setmethods__["durable"] = setDurable
+ if _newclass: durable = _swig_property(getDurable, setDurable)
+
+ __swig_getmethods__["properties"] = getProperties
+ if _newclass: properties = _swig_property(getProperties)
+
+ def getReplyTo(self) :
+ return self._getReplyTo().str()
+ def setReplyTo(self, address_str) :
+ self._setReplyTo(Address(address_str))
+ __swig_getmethods__["reply_to"] = getReplyTo
+ __swig_setmethods__["reply_to"] = setReplyTo
+ if _newclass: reply_to = _swig_property(getReplyTo, setReplyTo)
+
+ def __repr__(self):
+ args = []
+ for name in ["id", "subject", "user_id", "reply_to",
+ "correlation_id", "priority", "ttl",
+ "durable", "redelivered", "properties",
+ "content_type"] :
+ value = getattr(self, name)
+ if value : args.append("%s=%r" % (name, value))
+ if self.content is not None:
+ if args:
+ args.append("content=%r" % self.content)
+ else:
+ args.append(repr(self.content))
+ return "Message(%s)" % ", ".join(args)
+ %}
+}
+
+%pythoncode %{
+# Bring into module scope
+UNSPECIFIED = Message.UNSPECIFIED
+%}
diff --git a/cpp/bindings/qpid/qpid.i b/cpp/bindings/qpid/qpid.i
index e60ce1ce7c..352bafa3c8 100644
--- a/cpp/bindings/qpid/qpid.i
+++ b/cpp/bindings/qpid/qpid.i
@@ -27,6 +27,7 @@
#include <qpid/messaging/Sender.h>
#include <qpid/messaging/Message.h>
#include <qpid/messaging/Duration.h>
+#include <qpid/messaging/FailoverUpdates.h>
//
// Wrapper functions for map-decode and list-decode. This allows us to avoid
@@ -48,6 +49,7 @@ qpid::types::Variant::List& decodeList(const qpid::messaging::Message& msg) {
%}
+%include <qpid/ImportExport.h>
%include <qpid/messaging/ImportExport.h>
%include <qpid/messaging/Address.h>
%include <qpid/messaging/Duration.h>
@@ -56,6 +58,7 @@ qpid::types::Variant::List& decodeList(const qpid::messaging::Message& msg) {
%include <qpid/messaging/Sender.h>
%include <qpid/messaging/Session.h>
%include <qpid/messaging/Connection.h>
+%include <qpid/messaging/FailoverUpdates.h>
qpid::types::Variant::Map& decodeMap(const qpid::messaging::Message&);
qpid::types::Variant::List& decodeList(const qpid::messaging::Message&);
diff --git a/cpp/bindings/qpid/ruby/.gitignore b/cpp/bindings/qpid/ruby/.gitignore
new file mode 100644
index 0000000000..ab78513491
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/.gitignore
@@ -0,0 +1,2 @@
+pkg
+html
diff --git a/cpp/bindings/qpid/ruby/CMakeLists.txt b/cpp/bindings/qpid/ruby/CMakeLists.txt
new file mode 100644
index 0000000000..25258cfc6a
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/CMakeLists.txt
@@ -0,0 +1,63 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+##--------------------------------------------------
+## Properties used for generating the Ruby bindings.
+##--------------------------------------------------
+set(GEM_BINDINGS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/cqpid)
+set(GEM_BINDINGS_SOURCE_FILE ${GEM_BINDINGS_SOURCE_DIR}/cqpid.cpp)
+set(GEM_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR})
+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")
+
+swig_add_module(cqpid ruby ${CMAKE_CURRENT_SOURCE_DIR}/ruby.i)
+swig_link_libraries(cqpid 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")
+
+##----------------------------------
+## Install the complete Ruby binding
+##----------------------------------
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libcqpid.so
+ RENAME cqpid.so
+ DESTINATION ${RUBY_SITEARCH_DIR}
+ COMPONENT ${QPID_COMPONENT_CLIENT}
+ )
+
+add_custom_command(OUTPUT ${GEM_BINDINGS_SOURCE_FILE}
+ COMMAND cp ${swig_generated_file_fullname} ${GEM_BINDINGS_SOURCE_FILE}
+ DEPENDS ${swig_generated_file_fullname}
+ )
+
+add_custom_command(OUTPUT ${GEM_OUTPUT_FILE}
+ COMMAND OUTPUT_DIR=${GEM_OUTPUT_PATH} rake clean clobber package
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ DEPENDS ${swig_generated_file_fullname} ${GEM_BINDINGS_SOURCE_FILE}
+ )
+
+add_custom_target(gemfile
+ DEPENDS ${GEM_OUTPUT_FILE}
+ )
+
diff --git a/cpp/bindings/qpid/ruby/LICENSE b/cpp/bindings/qpid/ruby/LICENSE
new file mode 100644
index 0000000000..cff2a5e25d
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/LICENSE
@@ -0,0 +1,234 @@
+=========================================================================
+== Apache License ==
+=========================================================================
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+=========================================================================
+== 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/Makefile.am b/cpp/bindings/qpid/ruby/Makefile.am
index 67a3615362..a2a5dd76bd 100644
--- a/cpp/bindings/qpid/ruby/Makefile.am
+++ b/cpp/bindings/qpid/ruby/Makefile.am
@@ -21,7 +21,7 @@ if HAVE_RUBY_DEVEL
INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src -I$(top_builddir)/src
-EXTRA_DIST = ruby.i
+EXTRA_DIST = CMakeLists.txt ruby.i
BUILT_SOURCES = cqpid.cpp
SWIG_FLAGS = -w362,401
@@ -33,10 +33,10 @@ cqpid.cpp: $(srcdir)/ruby.i $(srcdir)/../qpid.i $(srcdir)/../../swig_ruby_typema
rubylibarchdir = $(RUBY_LIB_ARCH)
rubylibarch_LTLIBRARIES = cqpid.la
-cqpid_la_LDFLAGS = -avoid-version -module -shrext ".$(RUBY_DLEXT)"
+cqpid_la_LDFLAGS = -avoid-version -module -shared -shrext ".$(RUBY_DLEXT)"
cqpid_la_LIBADD = $(RUBY_LIBS) -L$(top_builddir)/src/.libs -lqpidmessaging -lqpidtypes \
$(top_builddir)/src/libqpidmessaging.la $(top_builddir)/src/libqpidtypes.la
-cqpid_la_CXXFLAGS = $(INCLUDES) -I$(RUBY_INC) -I$(RUBY_INC_ARCH)
+cqpid_la_CXXFLAGS = $(INCLUDES) -I$(RUBY_INC) -I$(RUBY_INC_ARCH) -fno-strict-aliasing
nodist_cqpid_la_SOURCES = cqpid.cpp
CLEANFILES = cqpid.cpp
diff --git a/cpp/bindings/qpid/ruby/README.rdoc b/cpp/bindings/qpid/ruby/README.rdoc
new file mode 100644
index 0000000000..0ae7e5cbed
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/README.rdoc
@@ -0,0 +1,45 @@
+= Qpid - Open Source AMQP Messaging
+
+Qpid is an cross-platform enterprise messaging system.
+
+Version :: 0.10.0.alpha.0
+
+= Links
+
+Documents :: http://qpid.apache.org/
+
+= Installation
+
+You can install Qpid with the following command.
+
+ $ gem install qpid
+
+== Building The Native Code
+
+The Qpid gem requires that you have available the Qpid libraries and
+development header files. To install them, please see:
+
+http://cwiki.apache.org/qpid/developer-pages.html
+
+If you are building the gem within the Qpid development environment
+itself, you can specify the location of the Qpid headers and
+libraries with:
+
+$ ruby extconfig.rb --with-qpid-lib=[path to libqpidclient.so, etc.]
+$ make
+
+== Examples
+
+Take a look at the integration tests for examples on how to leverage
+the messaging capabilities of Qpid in your Ruby applications.
+
+== License
+
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor licensing agreements.
+
+Author:: Darryl L. Pierce (mailto:dpierce@redhat.com)
+Copyright:: Copyright (c) 2011, Red Hat, Inc.
+Homepage:: http://qpid.apache.org
+License:: Apache License 2.0 - http://www.apache.org/licenses/LICENSE-2.0.html
+
diff --git a/cpp/bindings/qpid/ruby/Rakefile b/cpp/bindings/qpid/ruby/Rakefile
new file mode 100644
index 0000000000..07cfff9844
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/Rakefile
@@ -0,0 +1,130 @@
+# Rakefile for Qpid -*- ruby -*-
+#
+# 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.
+#
+
+task :noop
+
+# look for a root directory for out-of-tree builds
+
+OUTPUT_DIR=ENV["OUTPUT_DIR"] || "."
+
+require "rubygems"
+require "rubygems/package_task"
+
+require "rake/clean"
+require "rake/extensiontask"
+require "rake/rdoctask"
+require "rake/testtask"
+
+CLOBBER.include("pkg")
+
+load "./lib/qpid/version.rb"
+
+#-------------
+# Gem Details.
+#-------------
+
+NAME = "qpid"
+# VERSION = Qpid::VERSION
+AUTHOR = "Darryl L. Pierce"
+EMAIL = "dpierce@redhat.com"
+HOMEPAGE = "http://qpid.apache.org"
+SUMMARY = "Qpid is an enterprise messaging framework."
+
+desc "Default: run all tests."
+task :default => :"test:all"
+
+#---------------
+# Testing tasks.
+#---------------
+
+desc "Run all tests (alias for test:all)."
+task :test => :"test:all"
+
+namespace :test do
+ desc "Run all tests (default)."
+ task :all => [:units, :integrations]
+
+ desc "Run unit tests."
+ Rake::TestTask.new(:units) do |t|
+ t.libs << "."
+ t.pattern = "test/test*.rb"
+ t.verbose = true
+ end
+
+ desc "Run integration tests."
+ Rake::TestTask.new(:integrations) do |t|
+ t.libs << "."
+ t.pattern = "test/integration/*.rb"
+ t.verbose = true
+ end
+
+end
+
+#---------------------
+# Documentation tasks.
+#---------------------
+
+Rake::RDocTask.new(:rdoc => "rdoc",
+ :clobber_rdoc => "rdoc:clean",
+ :rerdoc => "rdoc:force") do |rd|
+ rd.main = "README.rdoc"
+ rd.options << "--all"
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
+end
+
+#-----------------
+# Package the gem.
+#-----------------
+
+spec = Gem::Specification.new do |s|
+ s.name = NAME
+ s.version = Qpid::VERSION
+ s.platform = Gem::Platform::RUBY
+ s.extra_rdoc_files = ["README.rdoc"]
+ s.summary = SUMMARY
+ s.description = s.summary
+ s.author = AUTHOR
+ s.email = EMAIL
+ s.homepage = HOMEPAGE
+
+ s.extensions = FileList["ext/**/extconf.rb"]
+
+ s.require_path = "lib"
+ # DEPRECATED s.autorequire = NAME
+ s.files = FileList["LICENSE",
+ "README.rdoc",
+ "Rakefile",
+ "TODO",
+ "lib/**/*.rb",
+ "test/**/*.rb",
+ "examples/**/*.rb",
+ "ext/**/*"]
+end
+
+Gem::PackageTask.new(spec) do |pkg|
+ pkg.package_dir = "#{OUTPUT_DIR}/pkg"
+end
+
+#------------------
+# Build native code
+#------------------
+
+Rake::ExtensionTask.new("cqpid", spec)
+
diff --git a/cpp/bindings/qpid/ruby/TODO b/cpp/bindings/qpid/ruby/TODO
new file mode 100644
index 0000000000..454aac9200
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/TODO
@@ -0,0 +1,7 @@
+TODO Items
+-----------------------------------------------------------------------------
+
+Version 0.11.0:
+ * Deliver the Ruby bindings as a gem.
+ * Rework the blocking tasks to not bring the main thread to a halt.
+
diff --git a/cpp/bindings/qpid/ruby/examples/client.rb b/cpp/bindings/qpid/ruby/examples/client.rb
new file mode 100644
index 0000000000..f42f25cfc9
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/examples/client.rb
@@ -0,0 +1,50 @@
+#
+# 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.
+#
+
+$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
+
+require 'qpid'
+
+if __FILE__ == $0
+ broker = ARGV[1] || "amqp:tcp:localhost:5672"
+ options = ARGV[2] || ""
+
+ connection = Qpid::Messaging::Connection.new broker, options
+ connection.open
+ session = connection.create_session
+ sender = session.create_sender "service_queue"
+ response_queue = Qpid::Messaging::Address.new("#response-queue", "",
+ :create => :always,
+ :delete => :always)
+ receiver = session.create_receiver response_queue
+
+ ["Twas brillig, and the slithy toves",
+ "Did gire and gymble in the wabe.",
+ "All mimsy were the borogroves,",
+ "And the mome raths outgrabe."].each do |line|
+ request = Qpid::Messaging::Message.new :content => line
+ request.reply_to = response_queue
+ sender.send request
+ response = receiver.fetch
+ puts "#{request.content} -> #{response.content}"
+ end
+
+ connection.close
+end
+
diff --git a/cpp/bindings/qpid/ruby/examples/drain.rb b/cpp/bindings/qpid/ruby/examples/drain.rb
new file mode 100644
index 0000000000..a6cf35e189
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/examples/drain.rb
@@ -0,0 +1,111 @@
+#
+# 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.
+#
+
+$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
+
+require 'qpid'
+require 'optparse'
+
+options = {
+ :broker => "localhost",
+ :timeout => Qpid::Messaging::Duration::IMMEDIATE,
+ :count => 1,
+ :forever => false,
+ :connection_options => ""
+}
+
+opts = OptionParser.new do |opts|
+ opts.banner = "Usage: drain.rb [OPTIONS] ADDRESS"
+
+ opts.separator ""
+ opts.separator "Drains messages from the specified address"
+ opts.separator ""
+
+ opts.on("-h", "--help",
+ "show this message") do
+ puts opts
+ exit
+ end
+
+ opts.on("-b", "--broker VALUE",
+ "url of broker to connect to") do |broker|
+ options[:broker] = broker
+ end
+
+ opts.on("-t", "--timeout VALUE", Integer,
+ "timeout in seconds to wait before exiting") do |timeout|
+ options[:timeout] = Qpid::Messaging::Duration.new timeout * 1000
+ end
+
+ opts.on("-f", "--forever",
+ "ignore timeout and wait forever") do
+ options[:forever] = true
+ end
+
+ opts.on("--connection-options VALUE",
+ "connection options string in the form {name1:value,name2:value2}") do |conopts|
+ options[:connection_options] = conopts
+ end
+
+ opts.on("-c", "--count VALUE", Integer,
+ "number of messages to read before exiting") do |count|
+ options[:count] = count
+ end
+end
+
+opts.parse!(ARGV)
+
+options[:address] = ARGV[0] || ""
+
+connection = Qpid::Messaging::Connection.new options[:broker], options[:connection_options]
+connection.open
+
+def render_map map
+ print "{"
+ map.keys.sort.each_with_index {|key,index| print "#{index > 0 ? ', ' : ''}#{key}:#{map[key]}"}
+ print "}"
+end
+
+begin
+ session = connection.create_session
+ receiver = session.create_receiver options[:address]
+ done = false
+ count = 0
+ options[:timeout] = Qpid::Messaging::Duration::FOREVER if options[:forever]
+
+ while !done && (count < options[:count])
+ message = receiver.fetch(options[:timeout])
+ print "Message(properties="
+ render_map message.properties
+ print ", content="
+ if message.content_type == "amqp/map"
+ print "'#{render_map message.content}')"
+ else
+ print "'#{message.content}'"
+ end
+ print ")\n"
+ session.acknowledge message
+ count += 1
+ end
+rescue Exception => error
+ puts "Exception: #{error.to_s}"
+end
+
+connection.close
+
diff --git a/cpp/bindings/qpid/ruby/examples/hello_world.rb b/cpp/bindings/qpid/ruby/examples/hello_world.rb
new file mode 100644
index 0000000000..703febeba1
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/examples/hello_world.rb
@@ -0,0 +1,49 @@
+#
+# 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.
+#
+
+$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
+
+require 'qpid'
+
+# This is your classic Hello World application, written in
+# Ruby, that uses Qpid. It demonstrates how to send and
+# also receive messages.
+#
+if __FILE__ == $0
+ broker = ARGV[0] || "localhost:5672"
+ address = ARGV[1] || "amq.topic"
+ options = ARGV[2] || ""
+
+ connection = Qpid::Messaging::Connection.new broker
+ connection.open
+ session = connection.create_session
+ receiver = session.create_receiver address
+ sender = session.create_sender address
+
+ # Send a simple message
+ sender.send Qpid::Messaging::Message.new :content => "Hello world!"
+
+ # Now receive the message
+ message = receiver.fetch Qpid::Messaging::Duration::SECOND
+ puts "#{message.content}"
+ session.acknowledge
+
+ connection.close
+end
+
diff --git a/cpp/bindings/qpid/ruby/examples/map_receiver.rb b/cpp/bindings/qpid/ruby/examples/map_receiver.rb
new file mode 100644
index 0000000000..805943a0a4
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/examples/map_receiver.rb
@@ -0,0 +1,63 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
+
+require 'qpid'
+
+broker = ARGV[0] || "amqp:tcp:127.0.0.1:5672"
+address = ARGV[1] || "message_queue; {create: always}"
+options = ARGV[2] || ""
+
+connection = Qpid::Messaging::Connection.new broker, options
+connection.open
+
+def display_value value
+ case value
+ when Array
+ result = ""
+ value.each_with_index {|element, index| result += "#{', ' if index > 0}#{element}"}
+ return "[#{result}]"
+ end
+
+ value.to_s
+end
+
+begin
+ session = connection.create_session
+ receiver = session.create_receiver address
+
+ message = receiver.fetch
+ content = message.content
+
+ print "content-type:#{message.content_type}"
+ print "{"
+ content.keys.sort.each_with_index do |key, index|
+ print "#{', ' if index > 0}#{key}:#{display_value content[key]}"
+ end
+ print "}\n"
+
+ session.acknowledge
+
+rescue Exception => error
+ puts "Exception: #{error.message}"
+end
+
+connection.close
+
diff --git a/cpp/bindings/qpid/ruby/examples/map_sender.rb b/cpp/bindings/qpid/ruby/examples/map_sender.rb
new file mode 100644
index 0000000000..fa0c6e4562
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/examples/map_sender.rb
@@ -0,0 +1,52 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
+
+require 'qpid'
+
+broker = ARGV[0] || "amqp:tcp:127.0.0.1:5672"
+address = ARGV[1] || "message_queue; {create: always}"
+options = ARGV[2] || []
+
+connection = Qpid::Messaging::Connection.new broker, options
+connection.open
+
+begin
+ session = connection.create_session
+ sender = session.create_sender address
+ message = Qpid::Messaging::Message.new
+
+ content = {
+ :id => 987654321,
+ :name => "Widget",
+ :percent => 0.99,
+ :colors => ["red", "green", "blue"]
+ }
+
+ message.content = content
+
+ sender.send message
+
+rescue Exception => error
+ puts "Exception: #{error.message}"
+end
+
+connection.close
+
diff --git a/cpp/bindings/qpid/ruby/examples/server.rb b/cpp/bindings/qpid/ruby/examples/server.rb
new file mode 100644
index 0000000000..ead9d58472
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/examples/server.rb
@@ -0,0 +1,51 @@
+#
+# 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.
+#
+
+$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
+
+require 'qpid'
+
+if __FILE__ == $0
+ broker = ARGV[0] || "amqp:tcp:localhost:5672"
+ options = ARGV[1] || ""
+
+ connection = Qpid::Messaging::Connection.new broker, options
+ connection.open
+ session = connection.create_session
+ receiver = session.create_receiver "service_queue; {create:always}"
+
+ loop do
+ request = receiver.fetch
+ address = request.reply_to
+
+ if !address.nil?
+ sender = session.create_sender address
+ response = Qpid::Messaging::Message.new :content => request.content.upcase
+ sender.send response
+ puts "Processed request: #{request.content} -> #{response.content}"
+ session.acknowledge
+ else
+ puts "Error: no reply address specified for request: #{request.content}"
+ session.reject request
+ end
+ end
+
+ connection.close
+end
+
diff --git a/cpp/bindings/qpid/ruby/examples/spout.rb b/cpp/bindings/qpid/ruby/examples/spout.rb
new file mode 100644
index 0000000000..c012e31f9d
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/examples/spout.rb
@@ -0,0 +1,126 @@
+#
+# 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.
+#
+
+$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
+
+require 'qpid'
+require 'optparse'
+
+options = {
+ :broker => "127.0.0.1",
+ :address => "",
+ :timeout => 0,
+ :count => 1,
+ :properties => {},
+ :content => nil,
+ :mapped => {}
+}
+
+opts = OptionParser.new do |opts|
+ opts.banner = "Usage: spout.rb [OPTIONS] ADDRESS"
+
+ opts.on("-h", "--help",
+ "show this message") do |help|
+ puts opts
+ exit
+ end
+
+ opts.on("-b","--broker VALUE",
+ "url of broker to connect to ") do |broker|
+ options[:broker] = broker
+ end
+
+ opts.on("-t", "--timeout VALUE", Integer,
+ "exit after the specified time") do |timeout|
+ options[:timeout] = Qpid::Messaging::Duration.new timeout * 1000
+ end
+
+ opts.on("-c", "--count VALUE", Integer,
+ "stop after count messages have been sent, zero disables") do |count|
+ options[:count] = count
+ end
+
+ opts.on("-i", "--id VALUE",
+ "use the supplied id instead of generating one") do |id|
+ options[:id] = id
+ end
+
+ opts.on("--reply-to VALUE",
+ "specify reply-to address") do |replyto|
+ options[:replyto] = replyto
+ end
+
+ opts.on("-P", "--property VALUE",
+ "specify message property") do |property|
+ name = property.split(/=/)[0]
+ value = property.split(/=/)[1]
+ options[:properties][name] = value
+ end
+
+ opts.on("-M", "--map VALUE",
+ "specify entry for map content") do |mapped|
+ name = mapped.split(/=/)[0]
+ value = mapped.split(/=/)[1]
+ options[:mapped][name] = value
+ end
+
+ opts.on("--content VALUE",
+ "specify textual content") do |content|
+ options[:content] = content
+ end
+
+ opts.on(nil, "--connection-options VALUE",
+ "connection options string in the form {name1:value1, name2:value2}") do |conopts|
+ options[:connection_options] = conopts
+ end
+end
+
+begin
+ opts.parse!(ARGV)
+rescue => error
+ opts.parse(["-h"])
+end
+
+# now get the non-arg options
+options[:address] = ARGV[0] unless ARGV[0].nil?
+
+connection = Qpid::Messaging::Connection.new options[:broker], options[:connection_options]
+connection.open
+session = connection.create_session
+sender = session.create_sender options[:address]
+message = Qpid::Messaging::Message.new
+
+options[:properties].each_key {|key| message.properties[key] = options[:properties][key]}
+
+(1..options[:count]).each do |count|
+ if !options[:mapped].keys.empty?
+ message.content = options[:mapped]
+ elsif options[:content]
+ message.content = options[:content]
+ end
+ message.content = options[:content] unless options[:content].nil?
+ message.properties["spout-id"] = "#{count}"
+ message.reply_to = options[:replyto] unless options[:replyto].nil? || options[:replyto].empty?
+ sender.send message
+end
+
+# session.sync
+
+connection.close
+
diff --git a/cpp/bindings/qpid/ruby/ext/cqpid/extconf.rb b/cpp/bindings/qpid/ruby/ext/cqpid/extconf.rb
new file mode 100644
index 0000000000..90292d4bec
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/ext/cqpid/extconf.rb
@@ -0,0 +1,73 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# To create the Makefile then you need to specify the location
+# of the Qpid shared libraries using the commandline:
+#
+# $ ruby extconf.rb --with-qpid-lib=[path to libraries]
+#
+
+require 'mkmf'
+
+# Setup the build environment.
+$CFLAGS = "-fPIC -fno-inline -x c++"
+
+REQUIRED_LIBRARIES = [
+ 'qpidclient',
+ 'qpidcommon',
+ 'qpidmessaging',
+ 'qpidtypes'
+ ]
+
+REQUIRED_HEADERS = [
+ 'qpid/messaging/Address.h',
+ 'qpid/messaging/Connection.h',
+ 'qpid/messaging/Duration.h',
+ 'qpid/messaging/exceptions.h',
+ 'qpid/messaging/FailoverUpdates.h',
+ 'qpid/messaging/Handle.h',
+ 'qpid/messaging/ImportExport.h',
+ 'qpid/messaging/Message.h',
+ 'qpid/messaging/Receiver.h',
+ 'qpid/messaging/Sender.h',
+ 'qpid/messaging/Session.h'
+ ]
+
+dir_config('qpid')
+
+def abort_build filetype, filename
+ abort "Missing required #{filetype}: #{filename}"
+end
+
+def require_library lib
+ abort_build "library", lib unless have_library lib
+end
+
+def require_header header
+ abort_build "header", header unless have_header header
+end
+
+have_library('stdc++')
+
+REQUIRED_LIBRARIES.each {|library| require_library library}
+
+REQUIRED_HEADERS.each {|header| require_header header}
+
+create_makefile('cqpid')
+
diff --git a/cpp/bindings/qpid/ruby/lib/qpid.rb b/cpp/bindings/qpid/ruby/lib/qpid.rb
new file mode 100644
index 0000000000..1f00c136c1
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/lib/qpid.rb
@@ -0,0 +1,29 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'qpid/errors'
+require 'qpid/duration'
+require 'qpid/address'
+require 'qpid/encoding'
+require 'qpid/message'
+require 'qpid/sender'
+require 'qpid/receiver'
+require 'qpid/session'
+require 'qpid/connection'
+
diff --git a/cpp/bindings/qpid/ruby/lib/qpid/address.rb b/cpp/bindings/qpid/ruby/lib/qpid/address.rb
new file mode 100644
index 0000000000..73b61bb1c7
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/lib/qpid/address.rb
@@ -0,0 +1,125 @@
+#
+# 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.
+#
+
+require 'cqpid'
+
+module Qpid
+
+ module Messaging
+
+ # Address represents an address to which messages can be sent or from
+ # which they can be received.
+ #
+ # An Address can be described using the following pattern:
+ #
+ # <address> [ / <subject> ] ; [ { <key> : <value> , ... } ]
+ #
+ # where *address* is a simple name and *subject* is a subject or subject
+ # pattern.
+ #
+ # The options, enclosed in curly braces, are key:value pairs delimited by
+ # a comma. The values can be nested maps also enclosed in curly braces.
+ # Or they can be lists of values, where they are contained within square
+ # brackets but still comma delimited, such as:
+ #
+ # [value1,value2,value3]
+ #
+ # The following are the list of supported options:
+ #
+ # create:: Indicates if the address should be created; values are *always*,
+ # *never*, *sender* or *reciever*.
+ #
+ # assert:: Indicates whether or not to assert any specified node properties;
+ # values are *always*, *never*, *sender* or *receiver*.
+ #
+ # delete:: Indicates whether or not to delete the addressed node when a
+ # sender or receiver is cancelled; values are *always*, *never*,
+ # *sender* or *receiver*.
+ #
+ # node:: A nested map describing properties for the addressed node.
+ # Properties are *type* (*topic* or *queue*), *durable* (a boolean),
+ # *x-declare* (a nested map of amqp 0.10-specific options) and
+ # *x-bindings*. (nested list which specifies a queue, exchange or
+ # a binding key and arguments.
+ #
+ # link:: A nested map through which properties of the link can be specified;
+ # properties are *durable*, *reliability*, *x-declare*, *x-subscribe*
+ # and *x-bindings*.
+ #
+ # mode:: (*For receivers only*) indicates whether the receiver should consume
+ # or browse messages; values are *consume* (the default) and *browse*.
+ class Address
+
+ def initialize(name, subject, options = {}, _type = "", address_impl = nil)
+ @address_impl = address_impl || Cqpid::Address.new(name, subject, convert_options(options), _type)
+ end
+
+ def address_impl # :nodoc:
+ @address_impl
+ end
+
+ # Returns the name.
+ def name; @address_impl.getName; end
+
+ # Sets the name.
+ def name=(name); @address_impl.setName name; end
+
+ # Returns the subject.
+ def subject; @address_impl.getSubject; end
+
+ # Sets the subject.
+ def subject=(subject); @address_impl.setSubject(subject); end
+
+ # Returns the type.
+ #---
+ # We cannot use "type" since that clashes with the Ruby object.type
+ # identifier.
+ def _type; @address_impl.getType; end
+
+ # Sets the type.
+ #
+ # The type of the address determines how Sender and Receiver objects
+ # are constructed for it. If no type is specified then it will be
+ # determined by querying the broker.
+ def _type=(_type); @address_impl.setType(_type); end
+
+ # Returns the options.
+ def options; @address_impl.getOptions; end
+
+ # Sets the options for the address.
+ # Any symbols are converted to strings.
+ def options=(options); @address_impl.setOptions(convert_options(options)); end
+
+ def to_s; @address_impl.str; end
+
+ private
+
+ def convert_options(options)
+ result = {}
+ options.each_pair {|key, value| result[key.to_s] = value.to_s}
+
+ return result
+ end
+
+ end
+
+ end
+
+end
+
diff --git a/cpp/bindings/qpid/ruby/lib/qpid/connection.rb b/cpp/bindings/qpid/ruby/lib/qpid/connection.rb
new file mode 100644
index 0000000000..5c56c1f5d0
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/lib/qpid/connection.rb
@@ -0,0 +1,134 @@
+#
+# 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.
+#
+
+require 'cqpid'
+
+module Qpid
+
+ module Messaging
+
+ # Connection allows for establishing connections to a remote endpoint.
+ class Connection
+
+ # The following general options are supported (as strings or symbols):
+ #
+ # username::
+ # password::
+ # heartbeat::
+ # tcp_nodelay::
+ # sasl_mechanism::
+ # sasl_service::
+ # sasl_min_ssf::
+ # sasl_max_ssf::
+ # transport::
+ #
+ # The following options specifically control reconnection behavior:
+ #
+ # reconnect:: *true* or *false*; indicates whether to attempt reconnections
+ # reconnect_timeout:: the number of seconds to attempt reconnecting
+ # reconnect_limit:: the number of retries before reporting failure
+ # reconnect_interval_min:: initial delay, in seconds, before attempting a reconnecting
+ # reconnect_interval_max:: number of seconds to wait before additional reconnect attempts
+ # reconnect_interval:: shorthand for setting box min and max values
+ # reconnect_urls:: a list of alternate URLs to use for reconnection attempts
+ def initialize(url, options = {}, connection_impl = nil)
+ @url = url
+ @connection_impl = connection_impl
+ @options = options
+ end
+
+ def connection_impl # :nodoc:
+ @connection_impl
+ end
+
+ # Opens the connection.
+ def open
+ @connection_impl = Cqpid::Connection.new(@url, convert_options)
+ @connection_impl.open
+ end
+
+ # Reports whether the connection is open.
+ def open?; false || (@connection_impl.isOpen if @connection_impl); end
+
+ # Closes the connection.
+ def close; @connection_impl.close if open?; end
+
+ # Creates a new session.
+ #
+ # If :transactional => true then a transactional session is created.
+ # Otherwise a standard session is created.
+ def create_session(args = {})
+ name = args[:name] || ""
+ if open?
+ if args[:transactional]
+ session = @connection_impl.createTransactionalSession name
+ else
+ session = @connection_impl.createSession name
+ end
+ return Session.new(session)
+ else
+ raise RuntimeError.new "No connection available."
+ end
+ end
+
+ # Returns a session for the specified session name.
+ def session name
+ session_impl = @connection_impl.getSession name
+ Qpid::Messaging::Session.new session_impl if session_impl
+ end
+
+ # Returns the username used to authenticate with the connection.
+ def authenticated_username; @connection_impl.getAuthenticatedUsername if open?; end
+
+ # inherited from Handle
+
+ # Returns whether the underlying handle is valid; i.e., not null.
+ def valid?
+ @connection_impl.isValid
+ end
+
+ # Returns whether the underlying handle is null.
+ def null?
+ @connection_impl.isNull
+ end
+
+ # Swaps the underlying connection handle.
+ def swap connection
+ @connection_impl.swap connection.connection_impl
+ end
+
+ private
+
+ def convert_options
+ result = {}
+ # map only those options defined in the C++ layer
+ # TODO when new options are added, this needs to be updated.
+ unless @options.nil? || @options.empty?
+ @options.each_pair {|key, value| result[key.to_s] = value.to_s}
+ end
+
+ return result
+ end
+
+ end
+
+ end
+
+end
+
diff --git a/cpp/bindings/qpid/ruby/lib/qpid/duration.rb b/cpp/bindings/qpid/ruby/lib/qpid/duration.rb
new file mode 100644
index 0000000000..c1f44e9281
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/lib/qpid/duration.rb
@@ -0,0 +1,63 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+require 'cqpid'
+
+module Qpid
+
+ module Messaging
+
+ # A Duration represents a period of time in milliseconds
+ #
+ # It defines the following named values as symbols:
+ #
+ # :FOREVER :: the maximum integer value for the platform
+ # :IMMEDIATE :: an alias for 0
+ # :SECOND :: 1,000ms
+ # :MINUTE :: 60,000ms
+ class Duration
+
+ def initialize duration # :nodoc:
+ @duration_impl = Cqpid::Duration.new duration
+ end
+
+ def duration_impl # :nodoc:
+ @duration_impl
+ end
+
+ def self.add_item(key, value) # :nodoc:
+ @hash ||= {}
+ @hash[key] = Duration.new value
+ end
+
+ def self.const_missing(key) # :nodoc:
+ @hash[key]
+ end
+
+ self.add_item :FOREVER, Cqpid::Duration.FOREVER.getMilliseconds
+ self.add_item :IMMEDIATE, Cqpid::Duration.IMMEDIATE.getMilliseconds
+ self.add_item :SECOND, Cqpid::Duration.SECOND.getMilliseconds
+ self.add_item :MINUTE, Cqpid::Duration.MINUTE.getMilliseconds
+
+ end
+
+ end
+
+end
+
diff --git a/cpp/bindings/qpid/ruby/lib/qpid/encoding.rb b/cpp/bindings/qpid/ruby/lib/qpid/encoding.rb
new file mode 100644
index 0000000000..c8b843b597
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/lib/qpid/encoding.rb
@@ -0,0 +1,56 @@
+#
+# 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.
+#
+
+require 'cqpid'
+
+module Qpid
+
+ module Messaging
+
+ # Encodes the supplied content into the given message.
+ def self.encode content, message, encoding = nil
+ prepared = content
+ case content
+ when Hash
+ prepared = {}
+ content.each_pair do |key,value|
+ prepared[key.to_s] = value.to_s
+ end
+ Cqpid::encode prepared, message.message_impl
+ when Array
+ prepared = []
+ content.each {|value| prepared << value.to_s}
+ Cqpid::encode prepared, message.message_impl
+ end
+ end
+
+ # Decodes and returns the message's content.
+ def self.decode(message, content_type = nil)
+ content_type = message.content_type unless content_type
+
+ case content_type
+ when "amqp/map": Cqpid.decodeMap message.message_impl
+ when "amqp/list": Cqpid.decodeList message.message_impl
+ end
+ end
+
+ end
+
+end
+
diff --git a/cpp/bindings/qpid/ruby/lib/qpid/errors.rb b/cpp/bindings/qpid/ruby/lib/qpid/errors.rb
new file mode 100644
index 0000000000..7a16d08d84
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/lib/qpid/errors.rb
@@ -0,0 +1,30 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+module Qpid
+
+ module Messaging
+
+ class KeyError < RuntimeError
+ end
+
+ end
+
+end
+
diff --git a/cpp/bindings/qpid/ruby/lib/qpid/message.rb b/cpp/bindings/qpid/ruby/lib/qpid/message.rb
new file mode 100644
index 0000000000..9b1b68c7c3
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/lib/qpid/message.rb
@@ -0,0 +1,157 @@
+#
+# 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.
+#
+
+require 'cqpid'
+
+module Qpid
+
+ module Messaging
+
+ # Message represents a message.
+ class Message
+
+ def initialize(args = {}, message_impl = nil)
+ @message_impl = message_impl
+ @message_impl = Cqpid::Message.new if @message_impl.nil?
+ @message_impl.setContent args[:content].to_s if args[:content]
+ @content = nil
+ end
+
+ def message_impl # :nodoc:
+ @message_impl
+ end
+
+ # Assigns the reply to address.
+ # The address must be an instance of Address.
+ def reply_to=(address); @message_impl.setReplyTo address.address_impl; end
+
+ # Returns the reply to address for the message as an instance of +Address+.
+ def reply_to
+ address_impl = @message_impl.getReplyTo
+ # only return an address if a reply to was specified
+ Qpid::Messaging::Address.new(nil, nil, nil, nil, address_impl) if address_impl
+ end
+
+ # Sets the subject.
+ def subject=(subject); @message_impl.setSubject subject; end
+
+ # Returns the subject.
+ def subject; @message_impl.getSubject; end
+
+ # Sets the content type.
+ def content_type=(content_type); @message_impl.setContentType content_type; end
+
+ # Returns the content type.
+ def content_type; @message_impl.getContentType; end
+
+ # Sets the message id.
+ def message_id=(message_id); @message_impl.setMessageId message_id.to_s; end
+
+ # Returns the message id.
+ def message_id; @message_impl.getMessageId; end
+
+ # Sets the user id.
+ def user_id=(user_id); @message_impl.setUserId user_id; end
+
+ # Returns the user id.
+ def user_id; @message_impl.getUserId; end
+
+ # Sets the correlation id.
+ def correlation_id=(correlation_id); @message_impl.setCorrelationId correlation_id; end
+
+ # Returns the correlation id.
+ def correlation_id; @message_impl.getCorrelationId; end
+
+ # Sets the priority.
+ def priority=(priority); @message_impl.setPriority priority; end
+
+ # Returns the priority.
+ def priority; @message_impl.getPriority; end
+
+ # Sets the time-to-live in milliseconds.
+ def ttl=(duration); @message_impl.setTtl duration; end
+
+ # Returns the time-to-live in milliseconds.
+ def ttl; @message_impl.getTtl; end
+
+ # Sets the durability.
+ def durable=(durable); @message_impl.setDurable durable; end
+
+ # Returns the durability.
+ def durable; @message_impl.getDurable; end
+
+ # Allows marking the message as redelivered.
+ def redelivered=(redelivered); @message_impl.setRedelivered redelivered; end
+
+ # Returns if the message was redelivered.
+ def redelivered; @message_impl.getRedelivered; end
+
+ # Returns all named properties.
+ # *NOTE:* It is recommended to use the +foo[key]+ method for
+ # retrieving properties.
+ def properties; @message_impl.getProperties; end
+
+ # Returns the value for the named property.
+ def [](key); self.properties[key.to_s]; end
+
+ # Assigns a value to the named property.
+ def []=(key, value); @message_impl.setProperty(key.to_s, value.to_s); end
+
+ # Sets the content.
+ def content=(content)
+ content_type = nil
+ @content = content
+ case @content
+ when Hash
+ content_type = "amqp/map"
+ when Array
+ content_type = "amqp/list"
+ end
+ if content_type.nil?
+ @message_impl.setContent @content
+ else
+ Qpid::Messaging.encode @content, self, content_type
+ end
+ end
+
+ # Returns the content.
+ def content
+ if @content.nil?
+ @content = @message_impl.getContent
+
+ # decode the content is necessary if it
+ # has an encoded content type
+ if ["amqp/list", "amqp/map"].include? @message_impl.getContentType
+ @content = Qpid::Messaging.decode(self,
+ @message_impl.getContentType)
+ end
+
+ end
+ @content
+ end
+
+ # Returns the content's size.
+ def content_size; @message_impl.getContentSize; end
+
+ end
+
+ end
+
+end
+
diff --git a/cpp/bindings/qpid/ruby/lib/qpid/receiver.rb b/cpp/bindings/qpid/ruby/lib/qpid/receiver.rb
new file mode 100644
index 0000000000..d498aa922b
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/lib/qpid/receiver.rb
@@ -0,0 +1,102 @@
+#
+# 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.
+#
+
+require 'cqpid'
+
+require 'qpid/duration'
+
+module Qpid
+
+ module Messaging
+
+ # Receiver defines a type for receiving messages.
+ class Receiver
+
+ def initialize(receiver_impl) # :nodoc:
+ @receiver_impl = receiver_impl
+ end
+
+ def receiver_impl # :nodoc:
+ @receiver_impl
+ end
+
+ # Retrieves a message from the receiver's local queue, or waits
+ # for up to the duration specified for one to become available.
+ def get(duration = Qpid::Messaging::Duration::FOREVER)
+ message_impl = @receiver_impl.get duration.duration_impl
+ create_message_wrapper message_impl unless message_impl.nil?
+ end
+
+ # Retrieves a message from the receiver's subscription, or waits
+ # for up to the duration specified for one to become available.
+ def fetch(duration = Qpid::Messaging::Duration::FOREVER)
+ message_impl = @receiver_impl.fetch duration.duration_impl
+ create_message_wrapper message_impl unless message_impl.nil?
+ end
+
+ # Sets the capacity.
+ #
+ # The capacity for a receiver determines the number of messages that
+ # can be held in the receiver before being fetched.
+ def capacity=(capacity); @receiver_impl.setCapacity capacity; end
+
+ # Returns the capacity.
+ def capacity; @receiver_impl.getCapacity; end
+
+ # Returns the number of available messages waiting to be fetched.
+ def available; @receiver_impl.getAvailable; end
+
+ # Returns the number of messages that have been received and acknowledged
+ # but whose acknowledgements have not been confirmed by the sender.
+ def unsettled; @receiver_impl.getUnsettled; end
+
+ # Cancels the reciever.
+ def close; @receiver_impl.close; end
+
+ # Returns whether the receiver is closed.
+ def closed?; @receiver_impl.isClosed; end
+
+ # Returns the name of the receiver
+ def name; @receiver_impl.getName; end
+
+ # Returns the Session for this receiver.
+ def session; Qpid::Messaging::Session.new(@receiver_impl.getSession); end
+
+ # Returns whether the underlying handle is valid.
+ def valid?; @receiver_impl.isValid; end
+
+ # Returns whether the underlying handle is null.
+ def null?; @receiver_impl.isNull; end
+
+ def swap receiver
+ @receiver_impl.swap receiver.receiver_impl
+ end
+
+ private
+
+ def create_message_wrapper message_impl
+ Qpid::Messaging::Message.new({}, message_impl)
+ end
+
+ end
+
+ end
+
+end
+
diff --git a/cpp/bindings/qpid/ruby/lib/qpid/sender.rb b/cpp/bindings/qpid/ruby/lib/qpid/sender.rb
new file mode 100644
index 0000000000..5d59c20d7e
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/lib/qpid/sender.rb
@@ -0,0 +1,82 @@
+#
+# 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.
+#
+
+module Qpid
+
+ module Messaging
+
+ # Sender defines a type for sending messages.
+ class Sender
+
+ def initialize(sender_impl) # :nodoc:
+ @sender_impl = sender_impl
+ end
+
+ def sender_impl # :nodoc:
+ @sender_impl
+ end
+
+ # Sends a message.
+ def send(message, args = {})
+ block = args[:block] || false
+ @sender_impl.send message.message_impl, block
+ end
+
+ # Closes the sender.
+ def close; @sender_impl.close; end
+
+ # Returns the name for the sender.
+ def name; @sender_impl.getName; end
+
+ # Sets the capacity for the sender, which is the number of outgoing
+ # messages that can be held pending confirmation or receipt by
+ # the broker.
+ def capacity=(capacity); @sender_impl.setCapacity capacity; end
+
+ # Returns the capacity.
+ def capacity; @sender_impl.getCapacity; end
+
+ # Returns the number of messages sent that are pending receipt
+ # confirmation by the broker.
+ def unsettled; @sender_impl.getUnsettled; end
+
+ # Returns the available capacity for sending messages.
+ def available
+ @sender_impl.getAvailable
+ end
+
+ # Returns the Session for this sender.
+ def session; Qpid::Messaging::Session.new @sender_impl.getSession; end
+
+ # Returns if the underlying sender is valid.
+ def valid?; @sender_impl.isValid; end
+
+ # Returns if the underlying sender is null.
+ def null?; @sender_impl.isNull; end
+
+ def swap sender
+ @sender_impl.swap sender.sender_impl
+ end
+
+ end
+
+ end
+
+end
+
diff --git a/cpp/bindings/qpid/ruby/lib/qpid/session.rb b/cpp/bindings/qpid/ruby/lib/qpid/session.rb
new file mode 100644
index 0000000000..543c26cc70
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/lib/qpid/session.rb
@@ -0,0 +1,186 @@
+#
+# 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.
+#
+
+require 'cqpid'
+
+require 'qpid/errors'
+
+module Qpid
+
+ module Messaging
+
+ # A Session represents a distinct conversation between end points.
+ class Session
+
+ def initialize(session) # :nodoc:
+ @session_impl = session
+ end
+
+ def session_impl # :nodoc:
+ @session_impl
+ end
+
+ # Returns the +Connection+ for the +Session+.
+ def connection
+ connection_impl = @session_impl.getConnection
+ Qpid::Messaging::Connection.new "", {}, connection_impl
+ end
+
+ # Creates a new endpoint for sending messages.
+ def create_sender(address)
+ _address = address
+
+ if address.class == Qpid::Messaging::Address
+ _address = address.address_impl
+ end
+
+ Qpid::Messaging::Sender.new(@session_impl.createSender(_address))
+ end
+
+ # Retrieves the +Sender+ with the specified name.
+ def sender(name)
+ result = nil
+
+ begin
+ sender_impl = @session_impl.getSender name
+ result = Sender.for_impl sender_impl
+ rescue
+ # treat any error as a key error
+ end
+
+ raise Qpid::Messaging::KeyError, "No such sender: #{name}" if result.nil?
+ result
+ end
+
+ # Retrieves the +Receiver+ with the specified name.
+ def receiver(name)
+ result = nil
+
+ begin
+ receiver_impl = @session_impl.getReceiver name
+ result = Receiver.for_impl receiver_impl
+ rescue
+ # treat any error as a key error
+ end
+
+ raise Qpid::Messaging::KeyError, "No such receiver: #{name}" if result.nil?
+ result
+ end
+
+ # Creates a new endpoint for receiving messages.
+ def create_receiver(address)
+ result = nil
+
+ if address.class == Qpid::Messaging::Address
+ address_impl = address.address_impl
+ result = Qpid::Messaging::Receiver.new(@session_impl.createReceiver(address_impl))
+ else
+ result = Qpid::Messaging::Receiver.new(@session_impl.createReceiver(address))
+ end
+
+ return result
+ end
+
+ # Closes the Session and all associated Senders and Receivers.
+ # All Sessions are closed when the associated Connection is closed.
+ def close; @session_impl.close; end
+
+ # Commits any pending transactions for a transactional session.
+ def commit; @session_impl.commit; end
+
+ # Rolls back any uncommitted transactions on a transactional session.
+ def rollback; @session_impl.rollback; end
+
+ # Acknowledges one or more outstanding messages that have been received
+ # on this session.
+ #
+ # If a message is submitted (:message => something_message) then only
+ # that message is acknowledged. Otherwise all messsages are acknowledged.
+ #
+ # If :sync => true then the call will block until the server completes
+ # processing the acknowledgements.
+ # If :sync => true then the call will block until processed by the server (def. false)
+ def acknowledge(args = {})
+ sync = args[:sync] || false
+ message = args[:message] if args[:message]
+
+ unless message.nil?
+ @session_impl.acknowledge message.message_impl, sync
+ else
+ @session_impl.acknowledge sync
+ end
+ end
+
+ # Rejects the specified message. A rejected message will not be redelivered.
+ #
+ # NOTE: A message cannot be rejected once it has been acknowledged.
+ def reject(message); @session_impl.reject message.message_impl; end
+
+ # Releases the message, which allows the broker to attempt to
+ # redeliver it.
+ #
+ # NOTE: A message connot be released once it has been acknowled.
+ def release(message); @session_impl.release message.message_impl; end
+
+ # Requests synchronization with the server.
+ #
+ # If :block => true then the call will block until the server acknowledges.
+ #
+ # If :block => false (default) then the call will complete and the server
+ # will send notification on completion.
+ def sync(args = {})
+ block = args[:block] || false
+ @session_impl.sync block
+ end
+
+ # Returns the total number of receivable messages, and messages already received,
+ # by Receivers associated with this session.
+ def receivable; @session_impl.getReceivable; end
+
+ # Returns the number of messages that have been acknowledged by this session
+ # whose acknowledgements have not been confirmed as processed by the server.
+ def unsettled_acks; @session_impl.getUnsettledAcks; end
+
+ # Fetches the receiver for the next message.
+ def next_receiver(timeout = Qpid::Messaging::Duration::FOREVER)
+ receiver_impl = @session_impl.nextReceiver(timeout.duration_impl)
+ Qpid::Messaging::Receiver.new receiver_impl
+ end
+
+ # Returns whether there are errors on this session.
+ def error?; @session_impl.hasError; end
+
+ def check_error; @session_impl.checkError; end
+
+ # Returns if the underlying session is valid.
+ def valid?; @session_impl.isValid; end
+
+ # Returns if the underlying session is null.
+ def null?; @session_impl.isNull; end
+
+ def swap session
+ @session_impl.swap session.session_impl
+ end
+
+ end
+
+ end
+
+end
+
diff --git a/cpp/bindings/qpid/ruby/lib/qpid/version.rb b/cpp/bindings/qpid/ruby/lib/qpid/version.rb
new file mode 100644
index 0000000000..f387ba98dc
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/lib/qpid/version.rb
@@ -0,0 +1,31 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+module Qpid
+
+ module Version
+
+ NUMBERS = [MAJOR = 0,
+ MINOR = 13,
+ BUILD = 0]
+ end
+
+ VERSION = Version::NUMBERS.join('.')
+
+end
diff --git a/cpp/bindings/qpid/ruby/test/lib/setup.rb b/cpp/bindings/qpid/ruby/test/lib/setup.rb
new file mode 100644
index 0000000000..c4901ed907
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/test/lib/setup.rb
@@ -0,0 +1,29 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
+
+require 'qpid'
+
+def create_session url, session_name
+ conn = Qpid::Messaging::Connection.new url
+ conn.open
+ conn.create_session session_name
+end
+
diff --git a/cpp/bindings/qpid/ruby/test/test_address.rb b/cpp/bindings/qpid/ruby/test/test_address.rb
new file mode 100644
index 0000000000..f54e93aa3d
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/test/test_address.rb
@@ -0,0 +1,39 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
+
+require 'test/unit'
+require 'flexmock/test_unit'
+
+require 'cqpid'
+require 'qpid/address'
+
+class TestAddress < Test::Unit::TestCase
+
+ def test_constructor
+ result = Qpid::Messaging::Address.new "name", "subject", {:foo => :bar}, "type"
+
+ assert_equal "name", result.name
+ assert_equal "subject", result.subject
+ assert_equal "type", result._type
+ end
+
+end
+
diff --git a/cpp/bindings/qpid/ruby/test/test_connection.rb b/cpp/bindings/qpid/ruby/test/test_connection.rb
new file mode 100644
index 0000000000..648fb0588a
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/test/test_connection.rb
@@ -0,0 +1,257 @@
+#
+# 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.
+#
+
+$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
+
+require 'test/unit'
+require 'flexmock/test_unit'
+
+require 'cqpid'
+require 'qpid/connection'
+
+class TestConnection < Test::Unit::TestCase
+
+ def setup
+ @connection_impl = flexmock("connection_impl")
+ @other_connection = flexmock("other_connection")
+ @other_connection_impl = flexmock("other_connection_impl")
+ @cqpid_connection = flexmock(Cqpid::Connection)
+
+ @session = flexmock("session")
+ @session_name = "test-session"
+
+ @url = "localhost"
+ @options = {}
+
+ @connection = Qpid::Messaging::Connection.new(@url, @options, @connection_impl)
+ end
+
+ def test_create_with_username_and_password
+ @cqpid_connection.
+ should_receive(:new).
+ once.with("localhost",
+ {"username" => "username",
+ "password" => "password"}).
+ and_return(@connection_impl)
+ @connection_impl.
+ should_receive(:open).
+ once
+
+ result = Qpid::Messaging::Connection.new("localhost",
+ :username => "username",
+ :password => "password")
+ result.open
+
+ assert_same @connection_impl, result.connection_impl
+ end
+
+ def test_create_with_hostname
+ result = Qpid::Messaging::Connection.new("localhost")
+
+ assert_not_nil result
+ end
+
+ def test_open
+ @cqpid_connection.
+ should_receive(:new).
+ once.
+ with(@url, {}).
+ and_return(@connection_impl)
+ @connection_impl.
+ should_receive(:open).
+ once
+
+ @connection.open
+
+ assert_same @connection_impl, @connection.connection_impl
+ end
+
+ def test_check_open_when_open
+ @connection_impl.
+ should_receive(:isOpen).
+ once.
+ and_return(true)
+
+ assert @connection.open?
+ end
+
+ def test_check_open_before_connection
+ result = Qpid::Messaging::Connection.new("hostname")
+
+ assert !result.open?
+ end
+
+ def test_check_open_when_closed
+ @connection_impl.
+ should_receive(:isOpen).
+ once.
+ and_return(false)
+
+ assert !@connection.open?
+ end
+
+ def test_close_an_unopened_session
+ @connection_impl.
+ should_receive(:isOpen).
+ once.
+ and_return(false)
+
+ @connection.close
+ end
+
+ def test_close
+ @connection_impl.
+ should_receive(:isOpen).
+ once.
+ and_return(true).
+ should_receive(:close).
+ once
+
+ @connection.close
+ end
+
+ def test_create_session_without_name
+ @connection_impl.
+ should_receive(:isOpen).
+ once.
+ and_return(true).
+ should_receive(:createSession).
+ once.
+ with("").
+ and_return(@session)
+
+ result = @connection.create_session
+
+ assert_not_nil result
+ assert_same @session, result.session_impl
+ end
+
+ def test_create_session
+ @connection_impl.
+ should_receive(:isOpen).
+ once.
+ and_return(true).
+ should_receive(:createSession).
+ once.
+ with(@session_name).
+ and_return(@session)
+
+ result = @connection.create_session :name => @session_name
+
+ assert_not_nil result
+ assert_same @session, result.session_impl
+ end
+
+ def test_create_session_raises_exception_when_closed
+ @connection_impl.
+ should_receive(:isOpen).
+ once.
+ and_return(false)
+
+ assert_raise(RuntimeError) {@connection.create_session @session_name}
+ end
+
+ def test_create_transactional_session
+ @connection_impl.
+ should_receive(:isOpen).
+ once.
+ and_return(true).
+ should_receive(:createTransactionalSession).
+ once.
+ with("").
+ and_return(@session)
+
+ result = @connection.create_session :transactional => true
+
+ assert_not_nil result
+ assert_same @session, result.session_impl
+ end
+
+ def test_authenticated_username_when_not_connected
+ @connection_impl.
+ should_receive(:isOpen).
+ once.
+ and_return(false)
+
+ result = @connection.authenticated_username
+
+ assert_nil result
+ end
+
+ def test_authenticated_username
+ @connection_impl.
+ should_receive(:isOpen).
+ once.
+ and_return(true).
+ should_receive(:getAuthenticatedUsername).
+ once.
+ and_return("farkle")
+
+ result = @connection.authenticated_username
+
+ assert_equal "farkle", result
+ end
+
+ def test_get_session_with_invalid_name
+ @connection_impl.
+ should_receive(:getSession).
+ once.
+ with(@session_name).
+ and_return(nil)
+
+ result = @connection.session @session_name
+
+ assert_nil result
+ end
+
+ # APIs inherited from Handle
+
+ def test_is_valid
+ @connection_impl.
+ should_receive(:isValid).
+ once.
+ and_return(true)
+
+ assert @connection.valid?
+ end
+
+ def test_is_null
+ @connection_impl.
+ should_receive(:isNull).
+ once.
+ and_return(false)
+
+ assert !@connection.null?
+ end
+
+ def test_swap
+ @other_connection.
+ should_receive(:connection_impl).
+ once.
+ and_return(@other_connection_impl)
+ @connection_impl.
+ should_receive(:swap).
+ once.
+ with(@other_connection_impl)
+
+ @connection.swap @other_connection
+ end
+
+end
+
diff --git a/cpp/bindings/qpid/ruby/test/test_encoding.rb b/cpp/bindings/qpid/ruby/test/test_encoding.rb
new file mode 100644
index 0000000000..060975a1d5
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/test/test_encoding.rb
@@ -0,0 +1,146 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
+
+require 'test/unit'
+require 'flexmock/test_unit'
+
+require 'cqpid'
+require 'qpid/encoding'
+
+class TestEncoding < Test::Unit::TestCase
+
+ def setup
+ @cqpid = flexmock(Cqpid)
+
+ @message = flexmock("message")
+ @message_impl = flexmock("message_impl")
+
+ @encoded = {"foo" => "bar"}
+ end
+
+ def test_encode_map_with_symbols
+ @message.
+ should_receive(:message_impl).
+ once.
+ and_return(@message_impl)
+ @cqpid.
+ should_receive(:encode).
+ once.
+ with({"foo" => "bar"}, @message_impl).
+ and_return(@encoded)
+
+ result = Qpid::Messaging.encode({:foo => :bar}, @message)
+
+ assert_same @encoded, result
+ end
+
+ def test_encode_list_with_symbols
+ @message.
+ should_receive(:message_impl).
+ once.
+ and_return(@message_impl)
+ @cqpid.
+ should_receive(:encode).
+ once.
+ with(["foo", "bar"], @message_impl).
+ and_return(@encoded)
+
+ result = Qpid::Messaging.encode([:foo, :bar], @message)
+
+ assert_same @encoded, result
+ end
+
+ def test_encode_with_content_type
+ @message.
+ should_receive(:message_impl).
+ once.
+ and_return(@message_impl)
+ @cqpid.
+ should_receive(:encode).
+ once.
+ with({"foo" => "bar"}, @message_impl).
+ and_return(@encoded)
+
+ result = Qpid::Messaging.encode({:foo => :bar}, @message)
+
+ assert_same @encoded, result
+ end
+
+ def test_encode
+ @message.
+ should_receive(:message_impl).
+ once.
+ and_return(@message_impl)
+ @cqpid.
+ should_receive(:encode).
+ once.
+ with({"foo" => "bar"}, @message_impl).
+ and_return(@encoded)
+
+ result = Qpid::Messaging.encode({"foo" => "bar"}, @message)
+
+ assert_same @encoded, result
+ end
+
+ def test_decode_for_map
+ decoded = {"foo" => "bar"}
+ @message.
+ should_receive(:content_type).
+ once.
+ and_return("amqp/map")
+ @message.
+ should_receive(:message_impl).
+ once.
+ and_return(@message_impl)
+ @cqpid.
+ should_receive(:decodeMap).
+ once.
+ with(@message_impl).
+ and_return(decoded)
+
+ result = Qpid::Messaging.decode(@message)
+
+ assert_same decoded, result
+ end
+
+ def test_decode_for_list
+ decoded = ["foo", "bar"]
+ @message.
+ should_receive(:content_type).
+ once.
+ and_return("amqp/list")
+ @message.
+ should_receive(:message_impl).
+ once.
+ and_return(@message_impl)
+ @cqpid.
+ should_receive(:decodeList).
+ once.
+ with(@message_impl).
+ and_return(decoded)
+
+ result = Qpid::Messaging.decode(@message)
+
+ assert_same decoded, result
+ end
+
+end
+
diff --git a/cpp/bindings/qpid/ruby/test/test_message.rb b/cpp/bindings/qpid/ruby/test/test_message.rb
new file mode 100644
index 0000000000..3fc705bf7e
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/test/test_message.rb
@@ -0,0 +1,353 @@
+#
+# 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.
+#
+
+$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
+
+require 'test/unit'
+require 'flexmock/test_unit'
+
+require 'qpid'
+
+class TestMessage < Test::Unit::TestCase
+
+ def setup
+ @address = flexmock("address")
+ @address_impl = flexmock("address_impl")
+
+ @messaging = flexmock(Qpid::Messaging)
+ @message_impl = flexmock("message")
+ @message = Qpid::Messaging::Message.new({}, @message_impl)
+ end
+
+ def test_message_impl
+ assert_same @message_impl, @message.message_impl
+ end
+
+ def test_set_reply_to
+ @address.
+ should_receive(:address_impl).
+ once.
+ and_return(@address_impl)
+ @message_impl.
+ should_receive(:setReplyTo).
+ once.
+ with(@address_impl)
+
+ @message.reply_to = @address
+ end
+
+ def test_get_reply_to
+ @message_impl.
+ should_receive(:getReplyTo).
+ once.
+ and_return(@address_impl)
+
+ result = @message.reply_to
+
+ assert_not_nil result
+ assert_same @address_impl, result.address_impl
+ end
+
+ def test_set_subject
+ @message_impl.
+ should_receive(:setSubject).
+ once.
+ with("New Subject")
+
+ @message.subject = "New Subject"
+ end
+
+ def test_get_subject
+ @message_impl.
+ should_receive(:getSubject).
+ once.
+ and_return("Old Subject")
+
+ assert_equal "Old Subject", @message.subject
+ end
+
+ def test_set_content_type
+ @message_impl.
+ should_receive(:setContentType).
+ once.
+ and_return("amqp/map")
+
+ @message.content_type = "amqp/map"
+ end
+
+ def test_get_content_type
+ @message_impl.
+ should_receive(:getContentType).
+ once.
+ and_return("amqp/list")
+
+ assert_equal "amqp/list", @message.content_type
+ end
+
+ def test_set_message_id
+ @message_impl.
+ should_receive(:setMessageId).
+ once.
+ with("717")
+
+ @message.message_id = "717"
+ end
+
+ def test_get_message_id
+ @message_impl.
+ should_receive(:getMessageId).
+ once.
+ and_return("1965")
+
+ assert_equal "1965", @message.message_id
+ end
+
+ def test_set_user_id
+ @message_impl.
+ should_receive(:setUserId).
+ once.
+ with("129")
+
+ @message.user_id = "129"
+ end
+
+ def test_get_user_id
+ @message_impl.
+ should_receive(:getUserId).
+ once.
+ and_return("1971")
+
+ assert_equal "1971", @message.user_id
+ end
+
+ def test_set_correlation_id
+ @message_impl.
+ should_receive(:setCorrelationId).
+ once.
+ with("320")
+
+ @message.correlation_id = "320"
+ end
+
+ def test_get_correlation_id
+ @message_impl.
+ should_receive(:getCorrelationId).
+ once.
+ and_return("1996")
+
+ assert_equal "1996", @message.correlation_id
+ end
+
+ def test_set_priority
+ @message_impl.
+ should_receive(:setPriority).
+ once.
+ with(9)
+
+ @message.priority = 9
+ end
+
+ def test_get_priority
+ @message_impl.
+ should_receive(:getPriority).
+ once.
+ and_return(21)
+
+ assert_equal 21, @message.priority
+ end
+
+ def test_set_ttl
+ @message_impl.
+ should_receive(:setTtl).
+ once.
+ with(Qpid::Messaging::Duration::FOREVER)
+
+ @message.ttl = Qpid::Messaging::Duration::FOREVER
+ end
+
+ def test_get_ttl
+ @message_impl.
+ should_receive(:getTtl).
+ once.
+ and_return(Qpid::Messaging::Duration::SECOND)
+
+ assert_equal Qpid::Messaging::Duration::SECOND, @message.ttl
+ end
+
+ def test_set_durable
+ @message_impl.
+ should_receive(:setDurable).
+ once.
+ with(true)
+
+ @message.durable = true
+ end
+
+ def test_set_not_durable
+ @message_impl.
+ should_receive(:setDurable).
+ once.
+ with(false)
+
+ @message.durable = false
+ end
+
+ def test_get_durable
+ @message_impl.
+ should_receive(:getDurable).
+ once.
+ and_return(true)
+
+ assert @message.durable
+ end
+
+ def test_set_redelivered
+ @message_impl.
+ should_receive(:setRedelivered).
+ once.
+ with(true)
+
+ @message.redelivered = true
+ end
+
+ def test_set_not_redelivered
+ @message_impl.
+ should_receive(:setRedelivered).
+ once.
+ with(false)
+
+ @message.redelivered = false
+ end
+
+ def test_get_redelivered
+ @message_impl.
+ should_receive(:getRedelivered).
+ once.
+ and_return(false)
+
+ assert !@message.redelivered
+ end
+
+ def test_get_properties
+ properties = {"foo" => "bar"}
+ @message_impl.
+ should_receive(:getProperties).
+ once.
+ and_return(properties)
+
+ result = @message.properties
+
+ assert_equal properties, result
+ end
+
+ def test_get_property
+ @message_impl.
+ should_receive(:getProperties).
+ once.
+ and_return({"foo" => "bar"})
+
+ result = @message["foo"]
+
+ assert_equal "bar", result
+ end
+
+ def test_set_property
+ @message_impl.
+ should_receive(:setProperty).
+ once.
+ with("foo", "bar")
+
+ @message["foo"] = "bar"
+ end
+
+ def test_set_content
+ @message_impl.
+ should_receive(:setContent).
+ once.
+ with("foo")
+
+ @message.content = "foo"
+ assert_equal "foo", @message.content
+ end
+
+ def test_set_content_with_array
+ content = ["one", "two", "three"]
+
+ @messaging.
+ should_receive(:encode).
+ once.
+ with(content, @message, "amqp/list")
+
+ @message.content = content
+ assert_same content, @message.content
+ end
+
+ def test_set_content_with_map
+ content = {:foo => "bar", :dog => "cat"}
+
+ @messaging.
+ should_receive(:encode).
+ once.
+ with(content, @message, "amqp/map")
+
+ @message.content = content
+ assert_same content, @message.content
+ end
+
+ def test_get_content
+ @message_impl.
+ should_receive(:getContent).
+ and_return("foo")
+ @message_impl.
+ should_receive(:getContentType).
+ and_return(String)
+
+ assert_equal "foo", @message.content
+ end
+
+ def test_get_content_with_array
+ decoded = ["foo", "bar"]
+
+ @message_impl.
+ should_receive(:getContent).
+ and_return("[foo,bar]")
+ @message_impl.
+ should_receive(:getContentType).
+ and_return("amqp/list")
+ @messaging.
+ should_receive(:decode).
+ once.
+ with(@message, "amqp/list").
+ and_return(decoded)
+
+ result = @message.content
+ assert_same decoded, result
+ end
+
+ def test_get_content_size
+ @message_impl.
+ should_receive(:getContentSize).
+ once.
+ and_return(68)
+
+ assert_equal 68, @message.content_size
+ end
+
+end
+
diff --git a/cpp/bindings/qpid/ruby/test/test_receiver.rb b/cpp/bindings/qpid/ruby/test/test_receiver.rb
new file mode 100644
index 0000000000..61a4db17f2
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/test/test_receiver.rb
@@ -0,0 +1,238 @@
+#
+# 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.
+#
+
+$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
+
+require 'test/unit'
+require 'flexmock/test_unit'
+
+require 'qpid/receiver'
+
+class TestReceiver < Test::Unit::TestCase
+
+ def setup
+ @session_impl = flexmock("session")
+
+ @Message_class = flexmock(Qpid::Messaging::Message)
+ @Messaging_module = flexmock(Qpid::Messaging)
+ @message_impl = flexmock("message_impl")
+ @message = flexmock("message")
+
+ @receiver_impl = flexmock("receiver")
+ @other_receiver = flexmock("other_receiver")
+ @other_receiver_impl = flexmock("other_receiver_impl")
+ @receiver = Qpid::Messaging::Receiver.new @receiver_impl
+ end
+
+ def test_receiver_impl
+ assert_same @receiver_impl, @receiver.receiver_impl
+ end
+
+ def test_get
+ @receiver_impl.
+ should_receive(:get).
+ once.
+ with_any_args.
+ and_return(@message_impl)
+
+ result = @receiver.get
+
+ assert_not_nil result
+ assert_same @message_impl, result.message_impl
+ end
+
+ def test_get_with_duration
+ @receiver_impl.
+ should_receive(:get).
+ once.
+ with_any_args.
+ and_return(@message_impl)
+
+ result = @receiver.get Qpid::Messaging::Duration::MINUTE
+
+ assert_not_nil result
+ assert_same @message_impl, result.message_impl
+ end
+
+ def test_get_with_no_message_received
+ @receiver_impl.
+ should_receive(:get).
+ once.
+ with_any_args.
+ and_return(nil)
+
+ result = @receiver.get Qpid::Messaging::Duration::SECOND
+
+ assert_nil result
+ end
+
+ def test_fetch
+ @receiver_impl.
+ should_receive(:fetch).
+ once.
+ with_any_args.
+ and_return(@message_impl)
+
+ result = @receiver.fetch
+
+ assert_not_nil result
+ assert_same @message_impl, result.message_impl
+ end
+
+ def test_fetch_with_duration
+ @receiver_impl.
+ should_receive(:fetch).
+ once.
+ with_any_args.
+ and_return(@message_impl)
+
+ result = @receiver.fetch Qpid::Messaging::Duration::MINUTE
+
+ assert_not_nil result
+ assert_same @message_impl, result.message_impl
+ end
+
+ def test_fetch_with_no_message_received
+ @receiver_impl.
+ should_receive(:fetch).
+ once.
+ with_any_args.
+ and_return(nil)
+
+ result = @receiver.fetch Qpid::Messaging::Duration::SECOND
+
+ assert_nil result
+ end
+
+ def test_set_capacity
+ @receiver_impl.
+ should_receive(:setCapacity).
+ once.
+ with(15)
+
+ @receiver.capacity = 15
+ end
+
+ def test_get_capacity
+ @receiver_impl.
+ should_receive(:getCapacity).
+ once.
+ and_return(17)
+
+ assert_equal 17, @receiver.capacity
+ end
+
+ def test_get_available
+ @receiver_impl.
+ should_receive(:getAvailable).
+ once.
+ and_return(2)
+
+ assert_equal 2, @receiver.available
+ end
+
+ def test_get_unsettled
+ @receiver_impl.
+ should_receive(:getUnsettled).
+ once.
+ and_return(12)
+
+ assert_equal 12, @receiver.unsettled
+ end
+
+ def test_close
+ @receiver_impl.
+ should_receive(:close).
+ once
+
+ @receiver.close
+ end
+
+ def test_closed_when_open
+ @receiver_impl.
+ should_receive(:isClosed).
+ once.
+ and_return(false)
+
+ assert !@receiver.closed?
+ end
+
+ def test_closed
+ @receiver_impl.
+ should_receive(:isClosed).
+ once.
+ and_return(true)
+
+ assert @receiver.closed?
+ end
+
+ def test_get_name
+ @receiver_impl.
+ should_receive(:getName).
+ once.
+ and_return("my-queue")
+
+ assert_equal "my-queue", @receiver.name
+ end
+
+ def test_get_session
+ @receiver_impl.
+ should_receive(:getSession).
+ once.
+ and_return(@session_impl)
+
+ result = @receiver.session
+
+ assert_not_nil result
+ assert_same @session_impl, result.session_impl
+ end
+
+ def test_is_valid
+ @receiver_impl.
+ should_receive(:isValid).
+ once.
+ and_return(false)
+
+ assert !@receiver.valid?
+ end
+
+ def test_is_null
+ @receiver_impl.
+ should_receive(:isNull).
+ once.
+ and_return(true)
+
+ assert @receiver.null?
+ end
+
+ def test_swap
+ @other_receiver.
+ should_receive(:receiver_impl).
+ once.
+ and_return(@other_receiver_impl)
+ @receiver_impl.
+ should_receive(:swap).
+ once.
+ with(@other_receiver_impl)
+
+ @receiver.swap @other_receiver
+ end
+
+end
+
diff --git a/cpp/bindings/qpid/ruby/test/test_sender.rb b/cpp/bindings/qpid/ruby/test/test_sender.rb
new file mode 100644
index 0000000000..64348b9f72
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/test/test_sender.rb
@@ -0,0 +1,183 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
+
+require 'test/unit'
+require 'flexmock/test_unit'
+
+require 'qpid/sender'
+
+class TestSender < Test::Unit::TestCase
+
+ def setup
+ @messaging = flexmock(Qpid::Messaging)
+ @message = flexmock("message")
+
+ @session_impl = flexmock("session_impl")
+
+ @sender_impl = flexmock("sender_impl")
+ @other_sender_impl = flexmock("other_sender_impl")
+ @sender = Qpid::Messaging::Sender.new @sender_impl
+ @other_sender = flexmock("other_sender")
+ end
+
+ def test_send
+ message_impl = "message_impl"
+ content = {:foo => :bar}
+ @message.
+ should_receive(:message_impl).
+ once.
+ and_return(message_impl)
+ @sender_impl.
+ should_receive(:send).
+ once.
+ with(message_impl, false)
+
+ @sender.send @message
+ end
+
+ def test_send_and_dont_block
+ message_impl = "message_impl"
+ content = {:foo => :bar}
+ @message.
+ should_receive(:message_impl).
+ once.
+ and_return(message_impl)
+ @sender_impl.
+ should_receive(:send).
+ once.
+ with(message_impl, false)
+
+ @sender.send @message, :block => false
+ end
+
+ def test_send_and_block
+ message_impl = "message_impl"
+ content = {:foo => :bar}
+ @message.
+ should_receive(:message_impl).
+ once.
+ and_return(message_impl)
+ @sender_impl.
+ should_receive(:send).
+ once.
+ with(message_impl, true)
+
+ @sender.send @message, :block => true
+ end
+
+ def test_close
+ @sender_impl.
+ should_receive(:close).
+ once
+
+ @sender.close
+ end
+
+ def test_set_capacity
+ @sender_impl.
+ should_receive(:setCapacity).
+ once.
+ with(17)
+
+ @sender.capacity = 17
+ end
+
+ def test_get_capacity
+ @sender_impl.
+ should_receive(:getCapacity).
+ once.
+ and_return(12)
+
+ assert_equal 12, @sender.capacity
+ end
+
+ def test_unsettled
+ @sender_impl.
+ should_receive(:getUnsettled).
+ once.
+ and_return(5)
+
+ assert_equal 5, @sender.unsettled
+ end
+
+ def test_available
+ @sender_impl.
+ should_receive(:getAvailable).
+ once.
+ and_return(15)
+
+ assert_equal 15, @sender.available
+ end
+
+ def test_name
+ @sender_impl.
+ should_receive(:getName).
+ once.
+ and_return("myname")
+
+ assert_equal "myname", @sender.name
+ end
+
+ def test_session
+ @sender_impl.
+ should_receive(:getSession).
+ once.
+ and_return(@session_impl)
+
+ result = @sender.session
+
+ assert_not_nil result
+ assert_same @session_impl, result.session_impl
+ end
+
+ def test_is_valid
+ @sender_impl.
+ should_receive(:isValid).
+ once.
+ and_return(true)
+
+ assert @sender.valid?
+ end
+
+ def test_is_null
+ @sender_impl.
+ should_receive(:isNull).
+ once.
+ and_return(false)
+
+ assert !@sender.null?
+ end
+
+ def test_swap
+ @other_sender.
+ should_receive(:sender_impl).
+ once.
+ and_return(@other_sender_impl)
+ @sender_impl.
+ should_receive(:swap).
+ once.
+ with(@other_sender_impl)
+
+ @sender.swap @other_sender
+ end
+
+end
+
diff --git a/cpp/bindings/qpid/ruby/test/test_session.rb b/cpp/bindings/qpid/ruby/test/test_session.rb
new file mode 100644
index 0000000000..20f055967b
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/test/test_session.rb
@@ -0,0 +1,445 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
+
+require 'test/unit'
+require 'flexmock/test_unit'
+
+require 'qpid/errors'
+require 'qpid/duration'
+require 'qpid/session'
+
+class TestSession < Test::Unit::TestCase
+
+ def setup
+ @session_impl = flexmock("session_impl")
+ @other_session = flexmock("other_session")
+ @other_session_impl = flexmock("other_session_impl")
+ @sender = flexmock("sender")
+
+ @Connection_class = flexmock(Qpid::Messaging::Connection)
+ @connection_impl = flexmock("connection_impl")
+ @connection = flexmock("connection")
+
+ @Receiver_class = flexmock(Qpid::Messaging::Receiver)
+ @receiver = flexmock("receiver")
+ @receiver_impl = flexmock("receiver_impl")
+
+ @address = flexmock("address")
+ @address_impl = flexmock("address_impl")
+
+ @Sender_class = flexmock(Qpid::Messaging::Sender)
+ @sender = flexmock("sender")
+ @sender_impl = flexmock("sender_impl")
+
+ @message = flexmock("message")
+ @message_impl = flexmock("message_impl")
+
+ @duration = flexmock("duration")
+ @duration_impl = flexmock("duration_impl")
+
+ @session = Qpid::Messaging::Session.new(@session_impl)
+ end
+
+ def test_create_sender_with_Address
+ @address.
+ should_receive(:class).
+ once.
+ and_return(Qpid::Messaging::Address).
+ should_receive(:address_impl).
+ once.
+ and_return(@address_impl)
+ @session_impl.
+ should_receive(:createSender).
+ once.
+ with(@address_impl).
+ and_return(@sender_impl)
+
+ result = @session.create_sender @address
+
+ assert_not_nil result
+ end
+
+ def test_create_sender
+ @session_impl.
+ should_receive(:createSender).
+ once.
+ with_any_args.
+ and_return(@sender_impl)
+
+ result = @session.create_sender("my-queue")
+
+ assert_not_nil result
+ end
+
+ def test_create_sender_with_address_string
+ @session_impl.
+ should_receive(:createSender).
+ once.
+ with("my-queue;{create:always}").
+ and_return(@sender_impl)
+
+ result = @session.create_sender "my-queue;{create:always}"
+
+ assert_same @sender_impl, result.sender_impl
+ end
+
+ def test_create_receiver
+ @address.
+ should_receive(:class).
+ once.
+ and_return(Qpid::Messaging::Address).
+ should_receive(:address_impl).
+ once.
+ and_return(@address_impl)
+ @session_impl.
+ should_receive(:createReceiver).
+ once.
+ with(@address_impl).
+ and_return(@receiver_impl)
+
+ result = @session.create_receiver(@address)
+
+ assert_equal @receiver_impl, result.receiver_impl
+ end
+
+ def test_create_receiver_with_address_string
+ @session_impl.
+ should_receive(:createReceiver).
+ once.
+ with("my-queue").
+ and_return(@receiver_impl)
+
+ result = @session.create_receiver("my-queue")
+
+ assert_same @receiver_impl, result.receiver_impl
+ end
+
+ def test_close
+ @session_impl.
+ should_receive(:close).
+ once
+
+ @session.close
+ end
+
+ def test_commit
+ @session_impl.
+ should_receive(:commit).
+ once
+
+ @session.commit
+ end
+
+ def test_rollback
+ @session_impl.
+ should_receive(:rollback).
+ once
+
+ @session.rollback
+ end
+
+ def test_acknowledge_with_no_args
+ @session_impl.
+ should_receive(:acknowledge).
+ once.
+ with(false)
+
+ @session.acknowledge
+ end
+
+ def test_acknowledge_and_sync
+ @session_impl.
+ should_receive(:acknowledge).
+ once.
+ with(true)
+
+ @session.acknowledge :sync => true
+ end
+
+ def test_acknowledge_and_dont_sync
+ @session_impl.
+ should_receive(:acknowledge).
+ once.
+ with(false)
+
+ @session.acknowledge :sync => false
+ end
+
+ def test_acknowledge_message_without_sync
+ @message.
+ should_receive(:message_impl).
+ once.
+ and_return(@message_impl)
+ @session_impl.
+ should_receive(:acknowledge).
+ once.
+ with(@message_impl, false)
+
+ @session.acknowledge :message => @message
+ end
+
+ def test_acknowledge_message_and_sync
+ @message.
+ should_receive(:message_impl).
+ once.
+ and_return(@message_impl)
+ @session_impl.
+ should_receive(:acknowledge).
+ once.
+ with(@message_impl, true)
+
+ @session.acknowledge :message => @message, :sync => true
+ end
+
+ def test_acknowledge_message_and_dont_sync
+ @message.
+ should_receive(:message_impl).
+ once.
+ and_return(@message_impl)
+ @session_impl.
+ should_receive(:acknowledge).
+ once.
+ with(@message_impl, false)
+
+ @session.acknowledge :message => @message, :sync => false
+ end
+
+ def test_reject_message
+ @message.
+ should_receive(:message_impl).
+ once.
+ and_return(@message_impl)
+ @session_impl.
+ should_receive(:reject).
+ once.
+ with(@message_impl)
+
+ @session.reject @message
+ end
+
+ def test_release_message
+ @message.
+ should_receive(:message_impl).
+ once.
+ and_return(@message_impl)
+ @session_impl.
+ should_receive(:release).
+ once.
+ with(@message_impl)
+
+ @session.release @message
+ end
+
+ def test_sync_without_block
+ @session_impl.
+ should_receive(:sync).
+ once
+
+ @session.sync
+ end
+
+ def test_sync_and_block
+ @session_impl.
+ should_receive(:sync).
+ once.
+ with(true)
+
+ @session.sync :block => true
+ end
+
+ def test_sync_and_dont_block
+ @session_impl.
+ should_receive(:sync).
+ once.
+ with(false)
+
+ @session.sync :block => false
+ end
+
+ def test_receivable
+ @session_impl.
+ should_receive(:getReceivable).
+ once.
+ and_return(5)
+
+ assert_equal 5, @session.receivable
+ end
+
+ def test_unsettled_acks
+ @session_impl.
+ should_receive(:getUnsettledAcks).
+ once.
+ and_return(17)
+
+ assert_equal 17, @session.unsettled_acks
+ end
+
+ def test_next_receiver_with_no_duration
+ @session_impl.
+ should_receive(:nextReceiver).
+ once.
+ with(Qpid::Messaging::Duration::FOREVER.duration_impl).
+ and_return(@receiver_impl)
+
+ result = @session.next_receiver
+
+ assert_same @receiver_impl, result.receiver_impl
+ end
+
+ def test_next_receiver_with_duration
+ @duration.
+ should_receive(:duration_impl).
+ once.
+ and_return(@duration_impl)
+ @session_impl.
+ should_receive(:nextReceiver).
+ once.
+ with(@duration_impl).
+ and_return(@receiver_impl)
+
+ result = @session.next_receiver @duration
+
+ assert_same @receiver_impl, result.receiver_impl
+ end
+
+ def test_sender
+ @session_impl.
+ should_receive(:getSender).
+ once.
+ with("farkle").
+ and_return(@sender_impl)
+ @Sender_class.
+ should_receive(:for_impl).
+ once.
+ with(@sender_impl).
+ and_return(@sender)
+
+ result = @session.sender "farkle"
+
+ assert_same @sender, result
+ end
+
+ def test_sender_with_invalid_name
+ @session_impl.
+ should_receive(:getSender).
+ once.
+ with("farkle").
+ and_throw(RuntimeError)
+
+ assert_raise(Qpid::Messaging::KeyError) {@session.sender "farkle"}
+ end
+
+ def test_receiver
+ @session_impl.
+ should_receive(:getReceiver).
+ once.
+ with("farkle").
+ and_return(@receiver_impl)
+ @Receiver_class.
+ should_receive(:for_impl).
+ once.
+ with(@receiver_impl).
+ and_return(@receiver)
+
+ result = @session.receiver "farkle"
+
+ assert_same @receiver, result
+ end
+
+ def test_receiver_with_invalid_name
+ @session_impl.
+ should_receive(:getReceiver).
+ once.
+ with("farkle").
+ and_throw(RuntimeError)
+
+ assert_raise(Qpid::Messaging::KeyError) {@session.receiver "farkle"}
+ end
+
+ def test_connection
+ @session_impl.
+ should_receive(:getConnection).
+ once.
+ and_return(@connection_impl)
+
+ result = @session.connection
+
+ assert_same @connection_impl, result.connection_impl
+ end
+
+ def test_error_with_none
+ @session_impl.
+ should_receive(:hasError).
+ once.
+ and_return(false)
+
+ assert !@session.error?
+ end
+
+ def test_error
+ @session_impl.
+ should_receive(:hasError).
+ once.
+ and_return(true)
+
+ assert @session.error?
+ end
+
+ def test_check_error
+ @session_impl.
+ should_receive(:checkError).
+ once
+
+ @session.check_error
+ end
+
+ def test_is_valid
+ @session_impl.
+ should_receive(:isValid).
+ once.
+ and_return(false)
+
+ assert !@session.valid?
+ end
+
+ def test_is_null
+ @session_impl.
+ should_receive(:isNull).
+ once.
+ and_return(false)
+
+ assert !@session.null?
+ end
+
+ def test_swap
+ @other_session.
+ should_receive(:session_impl).
+ once.
+ and_return(@other_session_impl)
+ @session_impl.
+ should_receive(:swap).
+ once.
+ with(@other_session_impl)
+
+ @session.swap @other_session
+ end
+
+end
diff --git a/cpp/bindings/qpid/ruby/test/ts_bindings.rb b/cpp/bindings/qpid/ruby/test/ts_bindings.rb
new file mode 100644
index 0000000000..7aa410c8f8
--- /dev/null
+++ b/cpp/bindings/qpid/ruby/test/ts_bindings.rb
@@ -0,0 +1,30 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
+
+require 'test/unit'
+require 'test_encoding'
+require 'test_address'
+require 'test_message'
+require 'test_sender'
+require 'test_receiver'
+require 'test_session'
+require 'test_connection'
+
diff --git a/cpp/bindings/swig_python_typemaps.i b/cpp/bindings/swig_python_typemaps.i
index b69784a6de..18bfd48f72 100644
--- a/cpp/bindings/swig_python_typemaps.i
+++ b/cpp/bindings/swig_python_typemaps.i
@@ -17,6 +17,25 @@
* under the License.
*/
+/* For UUID objects, to convert them to Python uuid.UUID objects,
+ * we'll need a reference to the uuid module.
+ */
+%{
+static PyObject* pUuidModule;
+%}
+
+%init %{
+ pUuidModule = PyImport_ImportModule("uuid");
+
+ /* Although it is not required, we'll publish the uuid module in our
+ * module, as if this module was a python module and we called
+ * "import uuid"
+ */
+ Py_INCREF(pUuidModule);
+ PyModule_AddObject(m, "uuid", pUuidModule);
+%}
+
+
%wrapper %{
#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
@@ -28,6 +47,7 @@ typedef int Py_ssize_t;
PyObject* MapToPy(const qpid::types::Variant::Map*);
PyObject* ListToPy(const qpid::types::Variant::List*);
+ PyObject* UuidToPy(const qpid::types::Uuid*);
void PyToMap(PyObject*, qpid::types::Variant::Map*);
void PyToList(PyObject*, qpid::types::Variant::List*);
@@ -104,6 +124,9 @@ typedef int Py_ssize_t;
break;
}
case qpid::types::VAR_UUID : {
+ qpid::types::Uuid uuid = v->asUuid();
+ result = UuidToPy(&uuid);
+ break;
}
}
} catch (qpid::types::Exception& ex) {
@@ -143,6 +166,30 @@ typedef int Py_ssize_t;
return result;
}
+ PyObject* UuidToPy(const qpid::types::Uuid * uuid) {
+ PyObject* pUuidClass = PyObject_GetAttrString(pUuidModule, "UUID");
+ if (!pUuidClass) {
+ // Failed to get UUID class
+ return 0;
+ }
+
+ PyObject* pArgs = PyTuple_New(0);
+ PyObject* pKw = PyDict_New();
+ PyObject* pData = PyString_FromStringAndSize(
+ (const char*)(uuid->data()), 16);
+ PyDict_SetItemString(pKw, "bytes", pData);
+
+ PyObject* result = PyObject_Call(pUuidClass, pArgs, pKw);
+
+ Py_DECREF(pData);
+ Py_DECREF(pKw);
+ Py_DECREF(pArgs);
+ Py_DECREF(pUuidClass);
+
+ return result;
+ }
+
+
void PyToMap(PyObject* obj, qpid::types::Variant::Map* map) {
map->clear();
Py_ssize_t iter(0);
@@ -304,6 +351,15 @@ typedef int Py_ssize_t;
Py_INCREF($result);
}
+/*
+ * UUID type: C++ --> Python
+ */
+%typemap(out) qpid::types::UUID & {
+ $result = UuidToPy($1);
+ if ($result)
+ Py_INCREF($result);
+}
+
/*
* Variant types: Ruby --> C++
diff --git a/cpp/bindings/swig_ruby_typemaps.i b/cpp/bindings/swig_ruby_typemaps.i
index 79e679663d..326d607c8d 100644
--- a/cpp/bindings/swig_ruby_typemaps.i
+++ b/cpp/bindings/swig_ruby_typemaps.i
@@ -49,7 +49,7 @@
}
VALUE VariantToRb(const qpid::types::Variant* v) {
- VALUE result;
+ VALUE result = Qnil;
try {
switch (v->getType()) {
case qpid::types::VAR_VOID: {
diff --git a/cpp/bld-winsdk.ps1 b/cpp/bld-winsdk.ps1
index 8f0a5886dc..bea46da28f 100644
--- a/cpp/bld-winsdk.ps1
+++ b/cpp/bld-winsdk.ps1
@@ -186,9 +186,6 @@ function BuildAPlatform
'examples/qmf-console',
'examples/request-response',
'examples/tradedemo',
- 'examples/old-examples.sln',
- 'examples/README.*',
- 'examples/verify*',
'include',
'plugins')
diff --git a/cpp/configure.ac b/cpp/configure.ac
index ea1a1b49ea..092694d56b 100644
--- a/cpp/configure.ac
+++ b/cpp/configure.ac
@@ -68,8 +68,10 @@ if test x$GXX = xyes; then
# The following warnings are deliberately omitted, they warn on valid code.
# -Wunreachable-code -Wpadded -Winline
# -Wshadow - warns about boost headers.
+ # Can't test for -Werror as whether it fails or not depends on what's in
+ # CFLAGS/CXXFLAGS. In any case it's been in gcc for a long time (since 2.95 at least)
if test "${enableval}" = yes; then
- gl_COMPILER_FLAGS(-Werror)
+ COMPILER_FLAGS="-Werror"
gl_COMPILER_FLAGS(-pedantic)
gl_COMPILER_FLAGS(-Wall)
gl_COMPILER_FLAGS(-Wextra)
@@ -521,18 +523,19 @@ AM_PATH_PYTHON()
builddir_lib_suffix="/.libs"
AC_SUBST([builddir_lib_suffix])
-# Files to generate
+# Files to generate
AC_CONFIG_FILES([
Makefile
examples/Makefile
- examples/direct/Makefile
- examples/fanout/Makefile
- examples/pub-sub/Makefile
- examples/request-response/Makefile
- examples/failover/Makefile
- examples/xml-exchange/Makefile
+ examples/old_api/Makefile
+ examples/old_api/direct/Makefile
+ examples/old_api/fanout/Makefile
+ examples/old_api/pub-sub/Makefile
+ examples/old_api/request-response/Makefile
+ examples/old_api/failover/Makefile
+ examples/old_api/xml-exchange/Makefile
examples/qmf-console/Makefile
- examples/tradedemo/Makefile
+ examples/old_api/tradedemo/Makefile
examples/messaging/Makefile
bindings/qpid/Makefile
bindings/qpid/ruby/Makefile
diff --git a/cpp/design_docs/hot-standby-design.txt b/cpp/design_docs/hot-standby-design.txt
new file mode 100644
index 0000000000..99a5dc0199
--- /dev/null
+++ b/cpp/design_docs/hot-standby-design.txt
@@ -0,0 +1,239 @@
+-*-org-*-
+# 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.
+
+* Another new design for Qpid clustering.
+
+For background see [[./new-cluster-design.txt]] which describes the issues
+with the old design and a new active-active design that could replace it.
+
+This document describes an alternative hot-standby approach.
+
+** Delivery guarantee
+
+We guarantee N-way redundant, at least once delivey. Once a message
+from a client has been acknowledged by the broker, it will be
+delivered even if N-1 brokers subsequently fail. There may be
+duplicates in the event of a failure. We don't make duplicates
+during normal operation (i.e when no brokers have failed)
+
+This is the same guarantee as the old cluster and the alternative
+active-active design.
+
+** Active-active vs. hot standby (aka primary-backup)
+
+An active-active cluster allows clients to connect to any broker in
+the cluster. If a broker fails, clients can fail-over to any other
+live broker.
+
+A hot-standby cluster has only one active broker at a time (the
+"primary") and one or more brokers on standby (the "backups"). Clients
+are only served by the leader, clients that connect to a backup are
+redirected to the leader. The backpus are kept up-to-date in real time
+by the primary, if the primary fails a backup is elected to be the new
+primary.
+
+Aside: A cold-standby cluster is possible using a standalone broker,
+CMAN and shared storage. In this scenario only one broker runs at a
+time writing to a shared store. If it fails, another broker is started
+(by CMAN) and recovers from the store. This bears investigation but
+the store recovery time is probably too long for failover.
+
+** Why hot standby?
+
+Active-active has some advantages:
+- Finding a broker on startup or failover is simple, just pick any live broker.
+- All brokers are always running in active mode, there's no
+- Distributing clients across brokers gives better performance, but see [1].
+- A broker failure affects only clients connected to that broker.
+
+The main problem with active-active is co-ordinating consumers of the
+same queue on multiple brokers such that there are no duplicates in
+normal operation. There are 2 approaches:
+
+Predictive: each broker predicts which messages others will take. This
+the main weakness of the old design so not appealing.
+
+Locking: brokers "lock" a queue in order to take messages. This is
+complex to implement, its not straighforward to determine the most
+performant strategie for passing the lock.
+
+Hot-standby removes this problem. Only the primary can modify queues
+so it just has to tell the backups what it is doing, there's no
+locking.
+
+The primary can enqueue messages and replicate asynchronously -
+exactly like the store does, but it "writes" to the replicas over the
+network rather than writing to disk.
+
+** Failover in a hot-standby cluster.
+
+Hot-standby has some potential performance issues around failover:
+
+- Failover "spike": when the primary fails every client will fail over
+ at the same time, putting strain on the system.
+
+- Until a new primary is elected, cluster cannot serve any clients or
+ redirect clients to the primary.
+
+We want to minimize the number of re-connect attempts that clients
+have to make. The cluster can use a well-known algorithm to choose the
+new primary (e.g. round robin on a known sequence of brokers) so that
+clients can guess the new primary correctly in most cases.
+
+Even if clients do guess correctly it may be that the new primary is
+not yet aware of the death of the old primary, which is may to cause
+multiple failed connect attempts before clients eventually get
+connected. We will need to prototype to see how much this happens in
+reality and how we can best get clients redirected.
+
+** Threading and performance.
+
+The primary-backup cluster operates analogously to the way the disk store does now:
+- use the same MessageStore interface as the store to interact with the broker
+- use the same asynchronous-completion model for replicating messages.
+- use the same recovery interfaces (?) for new backups joining.
+
+Re-using the well-established store design gives credibility to the new cluster design.
+
+The single CPG dispatch thread was a severe performance bottleneck for the old cluster.
+
+The primary has the same threading model as a a standalone broker with
+a store, which we know that this performs well.
+
+If we use CPG for replication of messages, the backups will receive
+messages in the CPG dispatch thread. To get more concurency, the CPG
+thread can dump work onto internal PollableQueues to be processed in
+parallel.
+
+Messages from the same broker queue need to go onto the same
+PollableQueue. There could be a separate PollableQueue for each broker
+queue. If that's too resource intensive we can use a fixed set of
+PollableQueues and assign broker queues to PollableQueues via hashing
+or round robin.
+
+Another possible optimization is to use multiple CPG queues: one per
+queue or a hashed set, to get more concurrency in the CPG layer. The
+old cluster is not able to keep CPG busy.
+
+TODO: Transactions pose a challenge with these concurrent models: how
+to co-ordinate multiple messages being added (commit a publish or roll
+back an accept) to multiple queues so that all replicas end up with
+the same message sequence while respecting atomicity.
+
+** Use of CPG
+
+CPG provides several benefits in the old cluster:
+- tracking membership (essential for determining the primary)
+- handling "spit brain" (integrates with partition support from CMAN)
+- reliable multicast protocol to distribute messages.
+
+I believe we still need CPG for membership and split brain. We could
+experiment with sending the bulk traffic over AMQP conections.
+
+** Flow control
+
+Need to ensure that
+1) In-memory internal queues used by the cluster don't overflow.
+2) The backups don't fall too far behind on processing CPG messages
+
+** Recovery
+When a new backup joins an active cluster it must get a snapshot
+from one of the other backups, or the primary if there are none. In
+store terms this is "recovery" (old cluster called it an "update)
+
+Compared to old cluster we only replidate well defined data set of the store.
+This is the crucial sore spot of old cluster.
+
+We can also replicated it more efficiently by recovering queues in
+reverse (LIFO) order. That means as clients actively consume messages
+from the front of the queue, they are redeucing the work we have to do
+in recovering from the back. (NOTE: this may not be compatible with
+using the same recovery interfaces as the store.)
+
+** Selective replication
+In this model it's easy to support selective replication of individual queues via
+configuration.
+- Explicit exchange/queue declare argument and message boolean: x-qpid-replicate.
+ Treated analogously to persistent/durable properties for the store.
+- if not explicitly marked, provide a choice of default
+ - default is replicate (replicated message on replicated queue)
+ - default is don't replicate
+ - default is replicate persistent/durable messages.
+
+** Inconsistent errors
+
+The new design eliminates most sources of inconsistent errors in the
+old design (connections, sessions, security, management etc.) and
+eliminates the need to stall the whole cluster till an error is
+resolved. We still have to handle inconsistent store errors when store
+and cluster are used together.
+
+We also have to include error handling in the async completion loop to
+guarantee N-way at least once: we should only report success to the
+client when we know the message was replicated and stored on all N-1
+backups.
+
+TODO: We have a lot more options than the old cluster, need to figure
+out the best approach, or possibly allow mutliple approaches. Need to
+go thru the various failure cases. We may be able to do recovery on a
+per-queue basis rather than restarting an entire node.
+
+** New members joining
+
+We should be able to catch up much faster than the the old design. A
+new backup can catch up ("recover") the current cluster state on a
+per-queue basis.
+- queues can be updated in parallel
+- "live" updates avoid the the "endless chase"
+
+During a "live" update several things are happening on a queue:
+- clients are publishing messages to the back of the queue, replicated to the backup
+- clients are consuming messages from the front of the queue, replicated to the backup.
+- the primary is sending pre-existing messages to the new backup.
+
+The primary sends pre-existing messages in LIFO order - starting from
+the back of the queue, at the same time clients are consuming from the front.
+The active consumers actually reduce the amount of work to be done, as there's
+no need to replicate messages that are no longer on the queue.
+
+* Steps to get there
+
+** Baseline replication
+Validate the overall design get initial notion of performance. Just
+message+wiring replication, no update/recovery for new members joining,
+single CPG dispatch thread on backups, no failover, no transactions.
+
+** Failover
+Electing primary, backups redirect to primary. Measure failover time
+for large # clients. Strategies to minimise number of retries after a
+failure.
+
+** Flow Control
+Keep internal queues from over-flowing. Similar to internal flow control in old cluster.
+Needed for realistic performance/stress tests
+
+** Concurrency
+Experiment with multiple threads on backups, multiple CPG groups.
+
+** Recovery/new member joining
+Initial status handshake for new member. Recovering queues from the back.
+
+** Transactions
+TODO: How to implement transactions with concurrency. Worst solution:
+a global --cluster-use-transactions flag that forces single thread
+mode. Need to find a better solution.
diff --git a/cpp/design_docs/new-cluster-design.txt b/cpp/design_docs/new-cluster-design.txt
index 7adb46fee3..936530a39a 100644
--- a/cpp/design_docs/new-cluster-design.txt
+++ b/cpp/design_docs/new-cluster-design.txt
@@ -17,7 +17,6 @@
# under the License.
* A new design for Qpid clustering.
-
** Issues with current design.
The cluster is based on virtual synchrony: each broker multicasts
@@ -84,19 +83,21 @@ context.
** A new cluster design.
-Clearly defined interface between broker code and cluster plug-in.
+1. Clearly defined interface between broker code and cluster plug-in.
-Replicate queue events rather than client data.
-- Broker behavior only needs to match per-queue.
-- Smaller amount of code (queue implementation) that must behave predictably.
-- Events only need be serialized per-queue, allows concurrency between queues
+2. Replicate queue events rather than client data.
+ - Only requires consistent enqueue order.
+ - Events only need be serialized per-queue, allows concurrency between queues
+ - Allows for replicated and non-replicated queues.
-Use a moving queue ownership protocol to agree order of dequeues.
-No longer relies on identical state and lock-step behavior to cause
-identical dequeues on each broker.
+3. Use a lock protocol to agree order of dequeues: only the broker
+ holding the lock can acqiure & dequeue. No longer relies on
+ identical state and lock-step behavior to cause identical dequeues
+ on each broker.
-Each queue has an associated thread-context. Events for a queue are executed
-in that queues context, in parallel with events for other queues.
+4. Use multiple CPG groups to process different queues in
+ parallel. Use a fixed set of groups and hash queue names to choose
+ the group for each queue.
*** Requirements
@@ -149,7 +150,7 @@ a release-queue event, allowing another interested broker to take
ownership.
*** Asynchronous completion of accept
-### HERE
+
In acknowledged mode a message is not forgotten until it is accepted,
to allow for requeue on rejection or crash. The accept should not be
completed till the message has been forgotten.
@@ -162,19 +163,32 @@ On receiving an accept the broker:
NOTE: The message store does not currently implement asynchronous
completions of accept, this is a bug.
+*** Multiple CPG groups.
+
+The old cluster was bottlenecked by processing everything in a single
+CPG deliver thread.
+
+The new cluster uses a set of CPG groups, one per core. Queue names
+are hashed to give group indexes, so statistically queues are likely
+to be spread over the set of groups.
+
+Operations on a given queue always use the same group, so we have
+order within each queue, but operations on different queues can use
+different groups giving greater throughput sending to CPG and multiple
+handler threads to process CPG messages.
+
** Inconsistent errors.
-The new design eliminates most sources of inconsistent errors
-(connections, sessions, security, management etc.) The only points
-where inconsistent errors can occur are at enqueue and dequeue (most
-likely store-related errors.)
+An inconsistent error means that after multicasting an enqueue, accept
+or dequeue, some brokers succeed in processing it and others fail.
-The new design can use the exisiting error-handling protocol with one
-major improvement: since brokers are no longer required to maintain
-identical state they do not have to stall processing while an error is
-being resolved.
+The new design eliminates most sources of inconsistent errors in the
+old broker: connections, sessions, security, management etc. Only
+store journal errors remain.
-#TODO: The only source of dequeue errors is probably an unrecoverable journal failure.
+The new inconsistent error protocol is similar to the old one with one
+major improvement: brokers do not have to stall processing while an
+error is being resolved.
** Updating new members
@@ -193,60 +207,44 @@ catch up (which is not guaranteed to happen in a bounded time.)
With the new cluster design only exchanges, queues, bindings and
messages need to be replicated.
-Update of wiring (exchanges, queues, bindings) is the same as current
-design.
-
-Update of messages is different:
-- per-queue rather than per-broker, separate queues can be updated in parallel.
-- updates queues in reverse order to eliminate unbounded catch-up
-- does not require updater & updatee to stall during update.
+We update individual objects (queues and exchanges) independently.
+- create queues first, then update all queues and exchanges in parallel.
+- multiple updater threads, per queue/exchange.
-Replication events, multicast to cluster:
-- enqueue(q,m): message m pushed on back of queue q .
-- acquire(q,m): mark m acquired
-- dequeue(q,m): forget m.
-Messages sent on update connection:
-- update_front(q,m): during update, receiver pushes m to *front* of q
-- update_done(q): during update, update of q is complete.
+Queue updater:
+- marks the queue position at the sync point
+- sends messages starting from the sync point working towards the head of the queue.
+- send "done" message.
-Updater:
-- when updatee joins set iterator i = q.end()
-- while i != q.begin(): --i; send update_front(q,*i) to updatee
-- send update_done(q) to updatee
+Queue updatee:
+- enqueues received from CPG: add to back of queue as normal.
+- dequeues received from CPG: apply if found, else save to check at end of update.
+- messages from updater: add to the *front* of the queue.
+- update complete: apply any saved dequeues.
-Updatee:
-- q initially in locked state, can't dequeue locally.
-- start processing replication events for q immediately (enqueue, dequeue, acquire etc.)
-- receive update_front(q,m): q.push_front(m)
-- receive update_done(q): q can be unlocked for local dequeing.
+Exchange updater:
+- updater: send snapshot of exchange as it was at the sync point.
-Benefits:
-- Stall only for wiring update: updater & updatee can process multicast messages while messages are updated.
-- No unbounded catch-up: update consists of at most N update_front() messages where N=q.size() at start of update.
-- During update consumers actually help by removing messages before they need to be updated.
-- Needs no separate "work to do" queue, only the broker queues themselves.
+Exchange updatee:
+- queue exchange operations after the sync point.
+- when snapshot is received: apply saved operations.
-# TODO how can we recover from updater crashing before update complete?
-# Clear queues that are not updated & send request for udpates on those queues?
+Note:
+- Updater is active throughout, no stalling.
+- Consuming clients actually reduce the size of the update.
+- Updatee stalls clients until the update completes.
+ (Note: May be possible to avoid updatee stall as well, needs thought)
-# TODO updatee may receive a dequeue for a message it has not yet seen, needs
-# to hold on to that so it can drop the message when it is seen.
-# Similar problem exists for wiring?
+** Internal cluster interface
-** Cluster API
-
-The new cluster API is similar to the MessageStore interface.
-(Initially I thought it would be an extension of the MessageStore interface,
-but as the design develops it seems better to make it a separate interface.)
+The new cluster interface is similar to the MessageStore interface, but
+provides more detail (message positions) and some additional call
+points (e.g. acquire)
The cluster interface captures these events:
- wiring changes: queue/exchange declare/bind
- message enqueued/acquired/released/rejected/dequeued.
-
-The cluster will require some extensions to the Queue:
-- Queues can be "locked", locked queues are ignored by IO-driven output.
-- Cluster must be able to apply queue events from the cluster to a queue.
- These appear to fit into existing queue operations.
+- transactional events.
** Maintainability
@@ -273,106 +271,48 @@ A number of specific ways the code will be simplified:
** Performance
-The only way to verify the relative performance of the new design is
-to prototype & profile. The following points suggest the new design
-may scale/perform better:
-
-Some work moved from virtual synchrony thread to connection threads:
-- All connection/session logic moves to connection thread.
-- Exchange routing logic moves to connection thread.
-- On local broker dequeueing is done in connection thread
-- Local broker dequeue is IO driven as for a standalone broker.
-
-For queues with all consumers on a single node dequeue is all
-IO-driven in connection thread. Pay for time-sharing only if queue has
-consumers on multiple brokers.
-
-Doing work for different queues in parallel scales on multi-core boxes when
-there are multiple queues.
-
-One difference works against performance, thre is an extra
-encode/decode. The old design multicasts raw client data and decodes
-it in the virtual synchrony thread. The new design would decode
-messages in the connection thread, re-encode them for multicast, and
-decode (on non-local brokers) in the virtual synchrony thread. There
-is extra work here, but only in the *connection* thread: on a
-multi-core machine this happens in parallel for every connection, so
-it probably is not a bottleneck. There may be scope to optimize
-decode/re-encode by re-using some of the original encoded data, this
-could also benefit the stand-alone broker.
-
-** Asynchronous queue replication
-
-The existing "asynchronous queue replication" feature maintains a
-passive backup passive backup of queues on a remote broker over a TCP
-connection.
-
-The new cluster replication protocol could be re-used to implement
-asynchronous queue replication: its just a special case where the
-active broker is always the queue owner and the enqueue/dequeue
-messages are sent over a TCP connection rather than multicast.
-
-The new update update mechanism could also work with 'asynchronous
-queue replication', allowing such replication (over a TCP connection
-on a WAN say) to be initiated after the queue had already been created
-and been in use (one of the key missing features).
-
-** Increasing Concurrency and load sharing
-
-The current cluster is bottlenecked by processing everything in the
-CPG deliver thread. By removing the need for identical operation on
-each broker, we open up the possiblility of greater concurrency.
-
-Handling multicast enqueue, acquire, accpet, release etc: concurrency
-per queue. Operatons on different queues can be done in different
-threads.
-
-The new design does not force each broker to do all the work in the
-CPG thread so spreading load across cluster members should give some
-scale-up.
-
-** Misc outstanding issues & notes
-
-Replicating wiring
-- Need async completion of wiring commands?
-- qpid.sequence_counter: need extra work to support in new design, do we care?
-
-Cluster+persistence:
-- finish async completion: dequeue completion for store & cluster
-- cluster restart from store: clean stores *not* identical, pick 1, all others update.
-- need to generate cluster ids for messages recovered from store.
-
-Live updates: we don't need to stall brokers during an update!
-- update on queue-by-queue basis.
-- updatee locks queues during update, no dequeue.
-- update in reverse: don't update messages dequeued during update.
-- updatee adds update messages at front (as normal), replicated messages at back.
-- updater starts from back, sends "update done" when it hits front of queue.
-
-Flow control: need to throttle multicasting
-1. bound the number of outstanding multicasts.
-2. ensure the entire cluster keeps up, no unbounded "lag"
-The existing design uses read-credit to solve 1., and does not solve 2.
-New design should stop reading on all connections while flow control
-condition exists?
-
-Can federation also be unified, at least in configuration?
-
-Consider queues (and exchanges?) as having "reliability" attributes:
-- persistent: is the message stored on disk.
-- backed-up (to another broker): active/passive async replication.
-- replicated (to a cluster): active/active multicast replication to cluster.
-- federated: federation link to a queue/exchange on another broker.
-
-"Reliability" seems right for the first 3 but not for federation, is
-there a better term?
-
-Clustering and scalability: new design may give us the flexibility to
-address scalability as part of cluster design. Think about
-relationship to federation and "fragmented queues" idea.
-
-* Design debates/descisions
+The standalone broker processes _connections_ concurrently, so CPU
+usage increases as you add more connections.
+
+The new cluster processes _queues_ concurrently, so CPU usage increases as you
+add more queues.
+
+In both cases, CPU usage peaks when the number of "units of
+ concurrency" (connections or queues) goes above the number of cores.
+
+When all consumers on a queue are connected to the same broker the new
+cluster uses the same messagea allocation threading/logic as a
+standalone broker, with a little extra asynchronous book-keeping.
+
+If a queue has multiple consumers connected to multiple brokers, the
+new cluster time-shares the queue which is less efficient than having
+all consumers on a queue connected to the same broker.
+** Flow control
+New design does not queue up CPG delivered messages, they are
+processed immediately in the CPG deliver thread. This means that CPG's
+flow control is sufficient for qpid.
+
+** Live upgrades
+
+Live upgrades refers to the ability to upgrade a cluster while it is
+running, with no downtime. Each brokers in the cluster is shut down,
+and then re-started with a new version of the broker code.
+
+To achieve this
+- Cluster protocl XML file has a new element <version number=N> attached
+ to each method. This is the version at which the method was added.
+- New versions can only add methods, existing methods cannot be changed.
+- The cluster handshake for new members includes the protocol version
+ at each member.
+- The cluster's version is the lowest version among its members.
+- A newer broker can join and older cluster. When it does, it must restrict
+ itself to speaking the older version protocol.
+- When the cluster version increases (because the lowest version member has left)
+ the remaining members may move up to the new version.
+
+
+* Design debates
** Active/active vs. active passive
An active-active cluster can be used in an active-passive mode. In
@@ -385,7 +325,7 @@ An active/passive implementation allows some simplifications over active/active:
- can do immediate local enqueue and still guarantee order.
Active/passive introduces a few extra requirements:
-- Exactly one broker hast to take over if primary fails.
+- Exactly one broker has to take over if primary fails.
- Passive members must refuse client connections.
- On failover, clients must re-try all known addresses till they find the active member.
@@ -393,43 +333,17 @@ Active/active benefits:
- A broker failure only affects the subset of clients connected to that broker.
- Clients can switch to any other broker on failover
- Backup brokers are immediately available on failover.
-- Some load sharing: reading from client + multicast only done on direct node.
-
-Active/active drawbacks:
-- Co-ordinating message acquisition may impact performance (not tested)
-- Code may be more complex that active/passive.
+- As long as a client can connect to any broker in the cluster, it can be served.
Active/passive benefits:
-- Don't need message allocation strategy, can feed consumers at top speed.
-- Code may be simpler than active/active.
+- Don't need to replicate message allocation, can feed consumers at top speed.
Active/passive drawbacks:
- All clients on one node so a failure affects every client in the system.
- After a failure there is a "reconnect storm" as every client reconnects to the new active node.
- After a failure there is a period where no broker is active, until the other brokers realize the primary is gone and agree on the new primary.
- Clients must find the single active node, may involve multiple connect attempts.
+- No service if a partition separates a client from the active broker,
+ even if the client can see other brokers.
-** Total ordering.
-
-Initial thinking: allow message ordering to differ between brokers.
-New thinking: use CPG total ordering, get identical ordering on all brokers.
-- Allowing variation in order introduces too much chance of unexpected behavior.
-- Usign total order allows other optimizations, see Message Identifiers below.
-
-** Message identifiers.
-
-Initial thinking: message ID = CPG node id + 64 bit sequence number.
-This involves a lot of mapping between cluster IDs and broker messsages.
-
-New thinking: message ID = queue name + queue position.
-- Removes most of the mapping and memory management for cluster code.
-- Requires total ordering of messages (see above)
-
-** Message rejection
-
-Initial thinking: add special reject/rejected points to cluster interface so
-rejected messages could be re-queued without multicast.
-New thinking: treat re-queueing after reject as entirely new message.
-- Simplifies cluster interface & implementation
-- Not on the critical path.
diff --git a/cpp/design_docs/new-cluster-plan.txt b/cpp/design_docs/new-cluster-plan.txt
index 781876e55a..626e443be7 100644
--- a/cpp/design_docs/new-cluster-plan.txt
+++ b/cpp/design_docs/new-cluster-plan.txt
@@ -17,376 +17,156 @@
# specific language governing permissions and limitations
# under the License.
+* Status of impementation
-Notes on new cluster implementation. See also: new-cluster-design.txt
+Meaning of priorities:
+[#A] Essential for basic functioning.
+[#B] Required for first release.
+[#C] Can be addressed in a later release.
-* Implementation plan.
+The existig prototype is bare bones to do performance benchmarks:
+- Implements publish and consumer locking protocol.
+- Defered delivery and asynchronous completion of message.
+- Optimize the case all consumers are on the same node.
+- No new member updates, no failover updates, no transactions, no persistence etc.
-Co-existence with old cluster code and tests:
-- Separate plugin cluster2, options --cluster2-*. Eventually renamed to replace cluster.
-- Double up tests with old version/new version as the new code develops.
+Prototype code is on branch qpid-2920-active, in cpp/src/qpid/cluster/exp/
-Minimal POC for message delivery & perf test.
-- no wiring replication, no updates, no failover, no persistence, no async completion.
-- just implement publish and acquire/dequeue locking protocol.
-- optimize the special case where all consumers are on the same node.
-- measure performance: compare active-passive and active-active modes of use.
+** Similarities to existing cluster.
-Full implementation of transient cluster
-- Update (based on existing update), async completion etc.
-- Passing all existing transient cluster tests.
+/Active-active/: the new cluster can be a drop-in replacement for the
+old, existing tests & customer deployment configurations are still
+valid.
-Persistent cluster
-- Make sure async completion works correctly.
-- InitialStatus protoocl etc. to support persistent start-up (existing code)
-- cluster restart from store: stores not identical. Load one, update the rest.
- - assign cluster ID's to messages recovered from store, don't replicate.
+/Virtual synchrony/: Uses corosync to co-ordinate activity of members.
-Improved update protocol
-- per-queue, less stalling, bounded catch-up.
+/XML controls/: Uses XML to define the primitives multicast to the
+cluster.
-* Task list
+** Differences with existing cluster.
-** TODO [#A] Minimal POC: publish/acquire/dequeue protocol.
+/Report rather than predict consumption/: brokers explicitly tell each
+other which messages have been acquired or dequeued. This removes the
+major cause of bugs in the existing cluster.
-NOTE: as implementation questions arise, take the easiest option and make
-a note for later optimization/improvement.
+/Queue consumer locking/: to avoid duplicates only one broker can acquire or
+dequeue messages at a time - while has the consume-lock on the
+queue. If multiple brokers are consuming from the same queue the lock
+is passed around to time-share access to the queue.
-*** Tests
-- python test: 4 senders, numbered messages, 4 receivers, verify message set.
-- acquire then release messages: verify can be dequeued on any member
-- acquire then kill broker: verify can be dequeued other members.
-- acquire then reject: verify goes on alt-exchange once only.
+/Per-queue concurrency/: uses a fixed-size set of CPG groups (reflecting
+the concurrency of the host) to allow concurrent processing on
+different queues. Queues are hashed onto the groups.
-*** DONE broker::Cluster interface and call points.
+* Completed tasks
+** DONE [#A] Minimal POC: publish/acquire/dequeue protocol.
+ CLOSED: [2011-10-05 Wed 16:03]
-Initial interface commited.
+Defines broker::Cluster interface and call points.
+Initial interface commite
-*** Main classes
+Main classes
+Core: central object holding cluster classes together (replaces cluster::Cluster)
+BrokerContext: implements broker::Cluster interface.
+QueueContext: Attached to a broker::Queue, holds cluster status.
+MessageHolder:holds local messages while they are being enqueued.
-BrokerHandler:
-- implements broker::Cluster intercept points.
-- sends mcast events to inform cluster of local actions.
-- thread safe, called in connection threads.
+Implements multiple CPG groups for better concurrency.
-LocalMessageMap:
-- Holds local messages while they are being enqueued.
-- thread safe: called by both BrokerHandler and MessageHandler
-
-MessageHandler:
-- handles delivered mcast messages related to messages.
-- initiates local actions in response to mcast events.
-- thread unsafe, only called in deliver thread.
-- maintains view of cluster state regarding messages.
+** DONE [#A] Large message replication.
+ CLOSED: [2011-10-05 Wed 17:22]
+Multicast using fixed-size (64k) buffers, allow fragmetation of messages across buffers (frame by frame)
-QueueOwnerHandler:
-- handles delivered mcast messages related to queue consumer ownership.
-- thread safe, called in deliver, connection and timer threads.
-- maintains view of cluster state regarding queue ownership.
-
-cluster::Core: class to hold new cluster together (replaces cluster::Cluster)
-- thread safe: manage state used by both MessageHandler and BrokerHandler
-
-The following code sketch illustrates only the "happy path" error handling
-is omitted.
-
-*** BrokerHandler
-Types:
-- struct QueuedMessage { Message msg; QueueName q; SequenceNumber position; }
-- struct
-
-NOTE:
-- Messages on queues are identified by a queue name + a position.
-- Messages being routed are identified by a sequence number.
-
-Members:
-- thread_local bool noReplicate // suppress replication.
-- thread_local bool isRouting // suppress operations while routing
-- Message localMessage[SequenceNumber] // local messages being routed.
-- thread_local SequenceNumber routingSequence
-
-NOTE: localMessage is also modified by MessageHandler.
-
-broker::Cluster intercept functions:
-
-routing(msg)
- if noReplicate: return
- # Supress everything except enqueues while we are routing.
- # We don't want to replicate acquires & dequeues caused by an enqueu,
- # e.g. removal of messages from ring/LV queues.
- isRouting = true
-
-enqueue(qmsg):
- if noReplicate: return
- if routingSequence == 0 # thread local
- routingSequence = nextRoutingSequence()
- mcast create(encode(qmsg.msg),routingSeq)
- mcast enqueue(qmsg.q,routingSeq)
-
-routed(msg):
- if noReplicate: return
- isRouting = false
-
-acquire(qmsg):
- if noReplicate: return
- if isRouting: return # Ignore while we are routing a message.
- if msg.id: mcast acquire(qmsg)
-
-release(QueuedMessage)
- if noReplicate: return
- if isRouting: return # Ignore while we are routing a message.
- mcast release(qmsg)
-
-accept(QueuedMessage):
- if noReplicate: return
- if isRouting: return # Ignore while we are routing a message.
- mcast accept(qmsg)
-
-reject(QueuedMessage):
- isRejecting = true
- mcast reject(qmsg)
-
-# FIXME no longer needed?
-drop(QueuedMessage)
- cleanup(qmsg)
-
-*** MessageHandler and mcast messages
-Types:
-- struct QueueEntry { QueuedMessage qmsg; NodeId acquired; }
-- struct QueueKey { MessageId id; QueueName q; }
-- typedef map<QueueKey, QueueEntry> Queue
-- struct Node { Message routing[SequenceNumber]; list<QueueKey> acquired; }
-
-Members:
-- QueueEntry enqueued[QueueKey]
-- Node node[NodeId]
-
-Mcast messages in Message class:
-
-create(msg,seq)
- if sender != self: node[sender].routing[seq] = decode(msg)
-
-enqueue(q,seq):
- id = (sender,seq)
- if sender == self:
- enqueued[id,q] = (localMessage[seq], acquired=None)
- else:
- msg = sender.routing[seq]
- enqueued[id,q] = (qmsg, acquired=None)
- with noReplicate=true: qmsg = broker.getQueue(q).push(msg)
-
-routed(seq):
- if sender == self: localMessage.erase(msg.id.seq)
- else: sender.routing.erase(seq)
-
-acquire(id,q):
- enqueued[id,q].acquired = sender
- node[sender].acquired.push_back((id,q))
- if sender != self:
- with noReplicate=true: broker.getQueue(q).acquire(enqueued[id,q])
-
-release(id,q)
- enqueued[id,q].acquired = None
- node[sender].acquired.erase((id,q))
- if sender != self
- with noReplicate=true: broker.getQueue(q).requeue(enqueued[id,q])
-
-reject(id,q):
- sender.routing[id] = enqueued[id,q] # prepare for re-queueing
-
-rejected(id,q)
- sender.routing.erase[id]
-
-dequeue(id,q)
- entry = enqueued[id,q]
- enqueued.erase[id,q]
- node[entry.acquired].acquired.erase(id,q)
- if sender != self:
- with noReplicate=true: broker.getQueue(q).dequeue(entry.qmsg)
-
-member m leaves cluster:
- for key in node[m].acquired:
- release(key.id, key.q)
- node.erase(m)
-
-*** Queue consumer locking
-
-When a queue is locked it does not deliver messages to its consumers.
-
-New broker::Queue functions:
-- stopConsumers(): set consumersStopped flag, wait for currently busy consumers to exit.
-- startConsumers(): reset consumersStopped flag
-
-Implementation sketch, locking omitted:
-
-void Queue::stopConsumers() {
- consumersStopped = true;
- while (consumersBusy) consumersBusyMonitor.wait();
-}
-
-void Queue::startConsumers() {
- consumersStopped = false;
- listeners.notify();
-}
-
-bool Queue::dispatch(consumer) {
- if (consumersStopped) return false;
- ++consumersBusy;
- do_regular_dispatch_body()
- if (--consumersBusy == 0) consumersBusyMonitor.notify();
-}
-
-*** QueueOwnerHandler
-
-Invariants:
-- Each queue is owned by at most one node at any time.
-- Each node is interested in a set of queues at any given time.
-- A queue is un-owned if no node is interested.
-
-The queue owner releases the queue when
-- it loses interest i.e. queue has no consumers with credit.
-- a configured time delay expires and there are other interested nodes.
-
-The owner mcasts release(q). On delivery the new queue owner is the
-next node in node-id order (treating nodes as a circular list)
-starting from the old owner that is interested in the queue.
-
-Queue consumers initially are stopped, only started when we get
-ownership from the cluster.
-
-Thread safety: called by deliver, connection and timer threads, needs locking.
-
-Thread safe object per queue holding queue ownership status.
-Called by deliver, connection and timer threads.
-
-class QueueOwnership {
- bool owned;
- Timer timer;
- BrokerQueue q;
-
- drop(): # locked
- if owned:
- owned = false
- q.stopConsumers()
- mcast release(q.name, false)
- timer.stop()
-
- take(): # locked
- if not owned:
- owned = true
- q.startConsumers()
- timer.start(timeout)
-
- timer.fire(): drop()
-}
-
-Data Members, only modified/examined in deliver thread:
-- typedef set<NodeId> ConsumerSet
-- map<QueueName, ConsumerSet> consumers
-- map<QueueName, NodeId> owner
+* Open questions
-Thread safe data members, accessed in connection threads (via BrokerHandler):
-- map<QueueName, QueueOwnership> ownership
+** TODO [#A] Queue sequence numbers vs. independant message IDs.
+ SCHEDULED: <2011-10-07 Fri>
-Multicast messages in QueueOwner class:
+Current prototype uses queue sequence numbers to identify
+message. This is tricky for updating new members as the sequence
+numbers are only known on delivery.
-consume(q):
- if sender==self and consumers[q].empty(): ownership[q].take()
- consumers[q].insert(sender)
+Independent message IDs that can be generated and sent with the message simplify
+this and potentially allow performance benefits by relaxing total ordering.
+However they imply additional map lookups that might hurt performance.
-release(q):
- asssert(owner[q] == sender and owner[q] in consumers[q])
- owner[q] = circular search from sender in consumers[q]
- if owner==self: ownership[q].take()
+- [X] Prototype independent message IDs, check performance.
+Throughput worse by 30% in contented case, 10% in uncontended.
+Sticking with queue sequence numbers.
-cancel(q):
- assert(queue[q].owner != sender) # sender must release() before cancel()
- consumers[q].erase(sender)
+* Outstanding Tasks
+** TODO [#A] Defer and async completion of wiring commands.
-member-leaves:
- for q in queue: if owner[q] = left: left.release(q)
+Testing requirement: Many tests assume wiring changes are visible
+across the cluster once the commad completes.
-Need 2 more intercept points in broker::Cluster:
+Name clashes: need to avoid race if same name queue/exchange declared
+on 2 brokers simultaneously
-consume(q,consumer,consumerCount) - Queue::consume()
- if consumerCount == 1: mcast consume(q)
+** TODO [#A] Passing all existing cluster tests.
-cancel(q,consumer,consumerCount) - Queue::cancel()
- if consumerCount == 0:
- ownership[q].drop()
- mcast cancel(q)
+The new cluster should be a drop-in replacement for the old, so it
+should be able to pass all the existing tests.
-#TODO: lifecycle, updating cluster data structures when queues are destroyed
-
-*** Increasing concurrency
-The major performance limitation of the old cluster is that it does
-everything in the single CPG deliver thread context.
-
-We can get additional concurrency by creating a thread context _per queue_
-for queue operations: enqueue, acquire, accept etc.
-
-We associate a PollableQueue of queue operations with each AMQP queue.
-The CPG deliver thread would
-- build messages and associate with cluster IDs.
-- push queue ops to the appropriate PollableQueue to be dispatched the queues thread.
-
-Serializing operations on the same queue avoids contention, but takes advantage
-of the independence of operations on separate queues.
+** TODO [#A] Update to new members joining.
-*** Re-use of existing cluster code
-- re-use Event
-- re-use Multicaster
-- re-use same PollableQueueSetup (may experiment later)
-- new Core class to replace Cluster.
-- keep design modular, keep threading rules clear.
+Need to resolve [[Queue sequence numbers vs. independant message IDs]] first.
+- implicit sequence numbers are more tricky to replicate to new member.
-** TODO [#B] Large message replication.
-Multicast should encode messages in fixed size buffers (64k)?
-Can't assume we can send message in one chunk.
-For 0-10 can use channel numbers & send whole frames packed into larger buffer.
-** TODO [#B] Transaction support.
-Extend broker::Cluster interface to capture transaction context and completion.
-Sequence number to generate per-node tx IDs.
-Replicate transaction completion.
-** TODO [#B] Batch CPG multicast messages
-The new cluster design involves a lot of small multicast messages,
-they need to be batched into larger CPG messages for efficiency.
-** TODO [#B] Genuine async completion
-Replace current synchronous waiting implementation with genuine async completion.
+Update individual objects (queues and exchanges) independently.
+- create queues first, then update all queues and exchanges in parallel.
+- multiple updater threads, per queue/exchange.
+- updater sends messages to special exchange(s) (not using extended AMQP controls)
+
+Queue updater:
+- marks the queue position at the sync point
+- sends messages starting from the sync point working towards the head of the queue.
+- send "done" message.
+Note: updater remains active throughout, consuming clients actually reduce the
+size of the update.
-Test: enhance test_store.cpp to defer enqueueComplete till special message received.
+Queue updatee:
+- enqueues received from CPG: add to back of queue as normal.
+- dequeues received from CPG: apply if found, else save to check at end of update.
+- messages from updater: add to the *front* of the queue.
+- update complete: apply any saved dequeues.
-Async callback uses *requestIOProcessing* to queue action on IO thread.
+Exchange updater:
+- updater: send snapshot of exchange as it was at the sync point.
-** TODO [#B] Async completion of accept when dequeue completes.
-Interface is already there on broker::Message, just need to ensure
-that store and cluster implementations call it appropriately.
+Exchange updatee:
+- queue exchange operations after the sync point.
+- when snapshot is received: apply saved operations.
-** TODO [#B] Replicate wiring.
-From messageStore create/destroy/bind, replicate encoded declare/destroy/bind command.
+Updater remains active throughout.
+Updatee stalls clients until the update completes.
-** TODO [#B] New members joining - first pass
+Updating queue/exchange/binding objects is via the same encode/decode
+that is used by the store. Updatee to use recovery interfaces to
+recover?
-Re-use update code from old cluster but don't replicate sessions &
-connections.
+** TODO [#A] Failover updates to client.
+Implement the amq.failover exchange to notify clients of membership.
-Need to extend it to send cluster IDs with messages.
+** TODO [#B] Initial status protocol.
+Handshake to give status of each broker member to new members joining.
+Status includes
+- persistent store state (clean, dirty)
+- cluster protocol version.
-Need to replicate the queue ownership data as part of the update.
+** TODO [#B] Replace boost::hash with our own hash function.
+The hash function is effectively part of the interface so
+we need to be sure it doesn't change underneath us.
-** TODO [#B] Persistence support.
-InitialStatus protoocl etc. to support persistent start-up (existing code)
+** TODO [#B] Persistent cluster support.
+Initial status protoocl to support persistent start-up (see existing code)
Only one broker recovers from store, update to others.
Assign cluster IDs to messages recovered from store, don't replicate. See Queue::recover.
-** TODO [#B] Handle other ways that messages can leave a queue.
-
-Other ways (other than via a consumer) that messages are take off a queue.
-
-NOTE: Not controlled by queue lock, how to make them consistent?
-
+** TODO [#B] Management support
+Replicate management methods that modify queues - e.g. move, purge.
Target broker may not have all messages on other brokers for purge/destroy.
- Queue::move() - need to wait for lock? Replicate?
- Queue::get() - ???
@@ -395,66 +175,48 @@ Target broker may not have all messages on other brokers for purge/destroy.
Need to add callpoints & mcast messages to replicate these?
-** TODO [#B] Flow control for internal queues.
-
-Need to bound the size of internal queues: delivery and multicast.
-- stop polling for read on client connections when we reach a bound.
-- restart polling when we get back under it.
-
-That will stop local multicasting, we still have to deal with remote
-multicasting (note existing cluster does not do this.) Something like:
-- when over bounds multicast a flow-control event.
-- on delivery of flow-control all members stop polling to read client connections
-- when back under bounds send flow-control-end, all members resume
-- if flow-controling member dies others resume
-
-** TODO [#B] Integration with transactions.
-Do we want to replicate during transaction & replicate commit/rollback
-or replicate only on commit?
-No integration with DTX transactions.
-** TODO [#B] Make new cluster work with replication exchange.
-Possibly re-use some common logic. Replication exchange is like clustering
-except over TCP.
-** TODO [#B] Better concurrency, scalabiility on multi-cores.
-Introduce PollableQueue of operations per broker queue. Queue up mcast
-operations (enqueue, acquire, accept etc.) to be handled concurrently
-on different queue. Performance testing to verify improved scalability.
-** TODO [#C] Async completion for declare, bind, destroy queues and exchanges.
-Cluster needs to complete these asynchronously to guarantee resources
-exist across the cluster when the command completes.
+** TODO [#B] TX transaction support.
+Extend broker::Cluster interface to capture transaction context and completion.
+Running brokers exchange TX information.
+New broker update includes TX information.
-** TODO [#C] Allow non-replicated exchanges, queues.
+ // FIXME aconway 2010-10-18: As things stand the cluster is not
+ // compatible with transactions
+ // - enqueues occur after routing is complete
+ // - no call to Cluster::enqueue, should be in Queue::process?
+ // - no transaction context associated with messages in the Cluster interface.
+ // - no call to Cluster::accept in Queue::dequeueCommitted
-Set qpid.replicated=false in declare arguments, set flag on Exchange, Queue objects.
-- save replicated status to store.
-- support in management tools.
-Replicated exchange: replicate binds to replicated queues.
-Replicated queue: replicate all messages.
+** TODO [#B] DTX transaction support.
+Extend broker::Cluster interface to capture transaction context and completion.
+Running brokers exchange DTX information.
+New broker update includes DTX information.
-** TODO [#C] New members joining - improved.
+** TODO [#B] Async completion of accept.
+When this is fixed in the standalone broker, it should be fixed for cluster.
-Replicate wiring like old cluster, stall for wiring but not for
-messages. Update messages on a per-queue basis from back to front.
+** TODO [#B] Network partitions and quorum.
+Re-use existing implementation.
-Updater:
-- stall & push wiring: declare exchanges, queues, bindings.
-- start update iterator thread on each queue.
-- unstall and process normally while iterator threads run.
+** TODO [#B] Review error handling, put in a consitent model.
+- [ ] Review all asserts, for possible throw.
+- [ ] Decide on fatal vs. non-fatal errors.
-Update iterator thread:
-- starts at back of updater queue, message m.
-- send update_front(q,m) to updatee and advance towards front
-- at front: send update_done(q)
+** TODO [#B] Implement inconsistent error handling policy.
+What to do if a message is enqueued sucessfully on the local broker,
+but fails on one or more backups - e.g. due to store limits?
+- we have more flexibility, we don't *have* to crash
+- but we've loste some of our redundancy guarantee, how should we inform client?
-Updatee:
-- stall, receive wiring, lock all queues, mark queues "updating", unstall
-- update_front(q,m): push m to *front* of q
-- update_done(q): mark queue "ready"
+** TODO [#C] Allow non-replicated exchanges, queues.
-Updatee cannot take the queue consume lock for a queue that is updating.
-Updatee *can* push messages onto a queue that is updating.
+Set qpid.replicate=false in declare arguments, set flag on Exchange, Queue objects.
+- save replicated status to store.
+- support in management tools.
+Replicated queue: replicate all messages.
+Replicated exchange: replicate bindings to replicated queues only.
-TODO: Is there any way to eliminate the stall for wiring?
+Configurable default? Defaults to true.
** TODO [#C] Refactoring of common concerns.
@@ -469,9 +231,46 @@ Look for ways to capitalize on the similarity & simplify the code.
In particular QueuedEvents (async replication) strongly resembles
cluster replication, but over TCP rather than multicast.
-** TODO [#C] Concurrency for enqueue events.
-All enqueue events are being processed in the CPG deliver thread context which
-serializes all the work. We only need ordering on a per queue basis, can we
-enqueue in parallel on different queues and will that improve performance?
+
** TODO [#C] Handling immediate messages in a cluster
Include remote consumers in descision to deliver an immediate message?
+** TODO [#C] Remove old cluster hacks and workarounds
+The old cluster has workarounds in the broker code that can be removed.
+- [ ] drop code to replicate management model.
+- [ ] drop timer workarounds for TTL, management, heartbeats.
+- [ ] drop "cluster-safe assertions" in broker code.
+- [ ] drop connections, sessions, management from cluster update.
+- [ ] drop security workarounds: cluster code now operates after message decoding.
+- [ ] drop connection tracking in cluster code.
+- [ ] simpler inconsistent-error handling code, no need to stall.
+
+** TODO [#C] Support for live upgrades.
+
+Allow brokers in a running cluster to be replaced one-by-one with a new version.
+(see new-cluster-design for design notes.)
+
+The old cluster protocol was unstable because any changes in broker
+state caused changes to the cluster protocol.The new design should be
+much more stable.
+
+Points to implement in anticipation of live upgrade:
+- Prefix each CPG message with a version number and length.
+ Version number determines how to decode the message.
+- Brokers ignore messages that have a higher version number than they understand.
+- Protocol version XML element in cluster.xml, on each control.
+- Initial status protocol to include protocol version number.
+
+New member udpates: use the store encode/decode for updates, use the
+same backward compatibility strategy as the store. This allows for
+adding new elements to the end of structures but not changing or
+removing new elements.
+
+** TODO [#C] Support for AMQP 1.0.
+
+* Testing
+** TODO [#A] Pass all existing cluster tests.
+Requires [[Defer and async completion of wiring commands.]]
+** TODO [#A] New cluster tests.
+Stress tests & performance benchmarks focused on changes in new cluster:
+- concurrency by queues rather than connections.
+- different handling shared queues when consuemrs are on different brokers.
diff --git a/cpp/docs/api/developer.doxygen.in b/cpp/docs/api/developer.doxygen.in
index fd3a9ac621..1e1fddab80 100644
--- a/cpp/docs/api/developer.doxygen.in
+++ b/cpp/docs/api/developer.doxygen.in
@@ -1029,7 +1029,7 @@ INCLUDE_FILE_PATTERNS =
# undefined via #undef or recursively expanded use the := operator
# instead of the = operator.
-PREDEFINED = QPID_CLIENT_EXTERN= QPID_COMMON_EXTERN= QPID_CONSOLE_EXTERN= QPID_BROKER_EXTERN= QPID_MESSAGING_EXTERN= QMF_EXTERN=
+PREDEFINED = QPID_CLIENT_EXTERN= QPID_COMMON_EXTERN= QPID_CONSOLE_EXTERN= QPID_BROKER_EXTERN= QPID_MESSAGING_EXTERN= QMF_EXTERN= QMFE_EXTERN=
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
# this tag can be used to specify a list of macro names that should be expanded.
diff --git a/cpp/docs/api/doxygen_mainpage.h b/cpp/docs/api/doxygen_mainpage.h
index cb36d7edb0..9acae52da4 100644
--- a/cpp/docs/api/doxygen_mainpage.h
+++ b/cpp/docs/api/doxygen_mainpage.h
@@ -266,7 +266,46 @@
* else
* session.rollback();
* </pre>
- *
+ *
+ * <h3>Exceptions</h3>
+ *
+ * All exceptions for the messaging API have MessagingException as
+ * their base class.
+
+ * A common class of exception are those related to processing
+ * addresses used to create senders and/or receivers. These all have
+ * AddressError as their base class.
+ *
+ * Where there is a syntax error in the address itself, a
+ * MalformedAddress will be thrown. Where the address is valid, but
+ * there is an error in interpreting (i.e. resolving) it, a
+ * ResolutionError - or a sub-class of it - will be thrown. If the
+ * address has assertions enabled for a given context and the asserted
+ * node properties are not in fact correct then AssertionFailed will
+ * be thrown. If the node is not found, NotFound will be thrown.
+ *
+ * The loss of the underlying connection (e.g. the TCP connection)
+ * results in TransportFailure being thrown. If automatic reconnect is
+ * enabled, this will be caught be the library which will then try to
+ * reconnect. If reconnection - as configured by the connection
+ * options - fails, then TransportFailure will be thrown. This can
+ * occur on any call to the messaging API.
+ *
+ * Sending a message may also result in an exception
+ * (e.g. TargetCapacityExceeded if a queue to which the message is
+ * delivered cannot enqueue it due to lack of capacity). For
+ * asynchronous send the exception may not be thrown on the send
+ * invocation that actually triggers it, but on a subsequent method
+ * call on the API.
+ *
+ * Certain exceptions may render the session invalid; once these
+ * occur, subsequent calls on the session will throw the same class of
+ * exception. This is not an intrinsic property of the class of
+ * exception, but is a result of the current mapping of the API to the
+ * underlying AMQP 0-10 protocol. You can test whether the session is
+ * valid at any time using the hasError() and/or checkError() methods
+ * on Session.
+ *
* <h3>Logging</h3>
*
* The Qpidd broker and C++ clients can both use environment variables to
diff --git a/cpp/docs/api/footer.html b/cpp/docs/api/footer.html
index 883410ce25..5a31e81821 100644
--- a/cpp/docs/api/footer.html
+++ b/cpp/docs/api/footer.html
@@ -25,7 +25,7 @@ Qpid C++ API Reference</small></address>
<address style="text-align: right;">
<small>
-Generated on $datetime for $projectname by&nbsp;<a href="http://www.doxygen.org/index.html"><img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> $doxygenversion</small>
+Generated on $date for $projectname by&nbsp;<a href="http://www.doxygen.org/index.html"><img src="doxygen.png" alt="doxygen" align="middle" border="0"></a> $doxygenversion</small>
</address>
</body>
</html>
diff --git a/cpp/docs/api/user.doxygen.in b/cpp/docs/api/user.doxygen.in
index 2728df47e4..ec0fd1361c 100644
--- a/cpp/docs/api/user.doxygen.in
+++ b/cpp/docs/api/user.doxygen.in
@@ -1021,7 +1021,7 @@ INCLUDE_FILE_PATTERNS =
# undefined via #undef or recursively expanded use the := operator
# instead of the = operator.
-PREDEFINED = QPID_CLIENT_EXTERN= QPID_COMMON_EXTERN= QPID_CONSOLE_EXTERN= QPID_BROKER_EXTERN= QPID_MESSAGING_EXTERN= QMF_EXTERN=
+PREDEFINED = QPID_CLIENT_EXTERN= QPID_COMMON_EXTERN= QPID_CONSOLE_EXTERN= QPID_BROKER_EXTERN= QPID_MESSAGING_EXTERN= QMF_EXTERN= QMFE_EXTERN=
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
# this tag can be used to specify a list of macro names that should be expanded.
diff --git a/cpp/docs/man/Makefile.am b/cpp/docs/man/Makefile.am
index 14295f73bf..b821568f81 100644
--- a/cpp/docs/man/Makefile.am
+++ b/cpp/docs/man/Makefile.am
@@ -16,10 +16,29 @@
# specific language governing permissions and limitations
# under the License.
#
+
+# Generate makefile from qpidd --help
+#
+# Note: qiddd.1 is normally a _checked in_ pre-generated file, so that
+# make dist does not have to build the entire source just for the man page.
+#
+# To update the checked-in file (e.g. for a new release) do the following:
+#
+# - start with a completely clean checkout.
+# - make sure there are no modules installed in your configured prefix,
+# we don't want to pick up configuration from optional modules
+# - do bootstrap; configure
+# - in build-dir: cd src; make # build the broker
+# - in source-dir: cd docs/man; rm qpidd.1 # remove checked-in man page.
+# - in build-dir: cd docs/man; make # make new man page
+# - edit qpidd.1 to remove all default values referring to file/directory locations.
+# these values will differ between builds depending on configuration.
+# - if source-dir != build-dir: copy qpidd.1 from build-dir/docs/man to source-dir/docs/man
+
dist_man_MANS = qpidd.1
-man_aux = $(dist_man_MANS:.1=.x)
-EXTRA_DIST = $(man_aux) generate_manpage groffify_options.sed groffify_template.sed
+man_aux = $(dist_man_MANS:.1=.x)
+EXTRA_DIST = $(man_aux) generate_manpage groffify_options.sed groffify_template.sed
DISTCLEANFILES = $(dist_man_MANS)
CLEANFILES=qpidd.1
diff --git a/cpp/docs/man/qpidd.1 b/cpp/docs/man/qpidd.1
new file mode 100644
index 0000000000..d2cff454cf
--- /dev/null
+++ b/cpp/docs/man/qpidd.1
@@ -0,0 +1,247 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.38.2.
+.TH QPIDD "1" "March 2011" "qpidd (qpidc) version 0.11" "User Commands"
+.SH NAME
+
+qpidd \- the Qpid AMQP Message Broker Daemon
+.SH SYNOPSIS
+
+qpidd [-p port] [--config config_file] [--data-dir directory]
+.SH DESCRIPTION
+
+An AMQP message broker daemon that stores, routes and forwards
+messages using the Advanced Message Queueing Protocol (AMQP).
+.SH OPTIONS
+
+The options below are built-in to qpidd. Installing add-on modules provides additional options. To see the full set of options available type "qpidd --help"
+
+Options may be specified via command line, environment variable or configuration file. See FILES and ENVIRONMENT below for details.
+.PP
+
+.TP
+\fB\-h\fR [ \fB\-\-help\fR ]
+Displays the help message
+.TP
+\fB\-v\fR [ \fB\-\-version\fR ]
+Displays version information
+.TP
+\fB\-\-config\fR FILE
+Reads configuration from FILE
+.SS "Module options:"
+.TP
+\fB\-\-module\-dir\fR DIR
+Load all shareable modules in this
+directory
+.TP
+\fB\-\-load\-module\fR FILE
+Specifies additional module(s) to be
+loaded
+.TP
+\fB\-\-no\-module\-dir\fR
+Don't load modules from module
+directory
+.SS "Broker Options:"
+.TP
+\fB\-\-data\-dir\fR DIR
+Directory to contain persistent data
+generated by the broker
+.TP
+\fB\-\-no\-data\-dir\fR
+Don't use a data directory. No
+persistent configuration will be loaded
+or stored
+.TP
+\fB\-p\fR [ \fB\-\-port\fR ] PORT (5672)
+Tells the broker to listen on PORT
+.TP
+\fB\-\-worker\-threads\fR N (3)
+Sets the broker thread pool size
+.TP
+\fB\-\-max\-connections\fR N (500)
+Sets the maximum allowed connections
+.TP
+\fB\-\-connection\-backlog\fR N (10)
+Sets the connection backlog limit for
+the server socket
+.TP
+\fB\-m\fR [ \fB\-\-mgmt\-enable\fR ] yes|no (1)
+Enable Management
+.TP
+\fB\-\-mgmt\-qmf2\fR yes|no (1)
+Enable broadcast of management
+information over QMF v2
+.TP
+\fB\-\-mgmt\-qmf1\fR yes|no (1)
+Enable broadcast of management
+information over QMF v1
+.TP
+\fB\-\-mgmt\-pub\-interval\fR SECONDS (10)
+Management Publish Interval
+.TP
+\fB\-\-queue\-purge\-interval\fR SECONDS (600)
+Interval between attempts to purge any
+expired messages from queues
+.TP
+\fB\-\-auth\fR yes|no (1)
+Enable authentication, if disabled all
+incoming connections will be trusted
+.TP
+\fB\-\-realm\fR REALM (QPID)
+Use the given realm when performing
+authentication
+.TP
+\fB\-\-default\-queue\-limit\fR BYTES (104857600)
+Default maximum size for queues (in
+bytes)
+.TP
+\fB\-\-tcp\-nodelay\fR
+Set TCP_NODELAY on TCP connections
+.TP
+\fB\-\-require\-encryption\fR
+Only accept connections that are
+encrypted
+.TP
+\fB\-\-known\-hosts\-url\fR URL or 'none'
+URL to send as 'known\-hosts' to clients
+('none' implies empty list)
+.TP
+\fB\-\-sasl\-config\fR DIR
+gets sasl config info from nonstandard
+location
+.TP
+\fB\-\-max\-session\-rate\fR MESSAGES/S (0)
+Sets the maximum message rate per
+session (0=unlimited)
+.TP
+\fB\-\-async\-queue\-events\fR yes|no (0)
+Set Queue Events async, used for
+services like replication
+.TP
+\fB\-\-default\-flow\-stop\-threshold\fR PERCENT (80)
+Percent of queue's maximum capacity at
+which flow control is activated.
+.TP
+\fB\-\-default\-flow\-resume\-threshold\fR PERCENT (70)
+Percent of queue's maximum capacity at
+which flow control is de\-activated.
+.TP
+\fB\-\-default\-event\-threshold\-ratio\fR %age of limit (80)
+The ratio of any specified queue limit
+at which an event will be raised
+.SS "Logging options:"
+.TP
+\fB\-t\fR [ \fB\-\-trace\fR ]
+Enables all logging
+.TP
+\fB\-\-log\-enable\fR RULE (notice+)
+Enables logging for selected levels and
+components. RULE is in the form
+\&'LEVEL[+][:PATTERN]' Levels are one of:
+.IP
+trace debug info notice warning error
+.IP
+critical
+For example:
+\&'\-\-log\-enable warning+' logs all
+warning, error and critical messages.
+\&'\-\-log\-enable debug:framing' logs debug
+messages from the framing namespace.
+This option can be used multiple times
+.TP
+\fB\-\-log\-time\fR yes|no (1)
+Include time in log messages
+.TP
+\fB\-\-log\-level\fR yes|no (1)
+Include severity level in log messages
+.TP
+\fB\-\-log\-source\fR yes|no (0)
+Include source file:line in log
+messages
+.TP
+\fB\-\-log\-thread\fR yes|no (0)
+Include thread ID in log messages
+.TP
+\fB\-\-log\-function\fR yes|no (0)
+Include function signature in log
+messages
+.TP
+\fB\-\-log\-prefix\fR STRING
+Prefix to append to all log messages
+.SS "Logging sink options:"
+.TP
+\fB\-\-log\-to\-stderr\fR yes|no (1)
+Send logging output to stderr
+.TP
+\fB\-\-log\-to\-stdout\fR yes|no (0)
+Send logging output to stdout
+.TP
+\fB\-\-log\-to\-file\fR FILE
+Send log output to FILE.
+.TP
+\fB\-\-log\-to\-syslog\fR yes|no (0)
+Send logging output to syslog;
+customize using \fB\-\-syslog\-name\fR and
+\fB\-\-syslog\-facility\fR
+.TP
+\fB\-\-syslog\-name\fR NAME (lt\-qpidd)
+Name to use in syslog messages
+.TP
+\fB\-\-syslog\-facility\fR LOG_XXX (LOG_DAEMON)
+Facility to use in syslog messages
+.SS "Daemon options:"
+.TP
+\fB\-d\fR [ \fB\-\-daemon\fR ]
+Run as a daemon. Logs to syslog by
+default in this mode.
+.TP
+\fB\-\-transport\fR TRANSPORT (tcp)
+The transport for which to return the
+port
+.TP
+\fB\-\-pid\-dir\fR DIR
+Directory where port\-specific PID file
+is stored
+.TP
+\fB\-w\fR [ \fB\-\-wait\fR ] SECONDS (600)
+Sets the maximum wait time to
+initialize the daemon. If the daemon
+fails to initialize, prints an error
+and returns 1
+.TP
+\fB\-c\fR [ \fB\-\-check\fR ]
+Prints the daemon's process ID to
+stdout and returns 0 if the daemon is
+running, otherwise returns 1
+.TP
+\fB\-q\fR [ \fB\-\-quit\fR ]
+Tells the daemon to shut down
+.SH ENVIRONMENT
+.I QPID_<option>
+.RS
+There is an environment variable for each option.
+.RE
+
+The environment variable is the option name in uppercase, prefixed with QPID_ and '.' or '-' are replaced with '_'. Environment settings are over-ridden by command line settings. For example:
+
+ export QPID_PORT=6000
+ export QPID_MAX_CONNECTIONS=10
+ export QPID_LOG_TO_FILE=/tmp/qpidd.log
+.SH FILES
+.I /etc/qpidd.conf
+.RS
+Default configuration file.
+.RE
+
+Configuration file settings are over-ridden by command line or environment variable settings. '--config <file>' or 'export QPID_CONFIG=<file>' specifies an alternate file.
+
+Each line is a name=value pair. Blank lines and lines beginning with # are ignored. For example:
+
+ # My qpidd configuration file.
+ port=6000
+ max-connections=10
+ log-to-file=/tmp/qpidd.log
+.SH AUTHOR
+
+The Apache Qpid Project, dev@qpid.apache.org
+.SH "REPORTING BUGS"
+
+Please report bugs to users@qpid.apache.org
diff --git a/cpp/docs/man/qpidd.x b/cpp/docs/man/qpidd.x
index af5d9628ee..0ccf3b562a 100644
--- a/cpp/docs/man/qpidd.x
+++ b/cpp/docs/man/qpidd.x
@@ -13,6 +13,8 @@ messages using the Advanced Message Queueing Protocol (AMQP).
[OPTIONS]
+The options below are built-in to qpidd. Installing add-on modules provides additional options. To see the full set of options available type "qpidd --help"
+
Options may be specified via command line, environment variable or configuration file. See FILES and ENVIRONMENT below for details.
[FILES]
diff --git a/cpp/etc/Makefile.am b/cpp/etc/Makefile.am
index c91dbcbbad..1e4db561a7 100644
--- a/cpp/etc/Makefile.am
+++ b/cpp/etc/Makefile.am
@@ -30,30 +30,7 @@ nobase_sysconf_DATA = \
qpidd.conf
if HAVE_SASL
-SASL_DB = qpidd.sasldb
-
nobase_sysconf_DATA += \
$(SASL_CONF)
-sasldbdir = $(localstatedir)/lib/qpidd
-sasldb_DATA = $(SASL_DB)
-
-# Setup the default sasldb file with a single user, guest, with an
-# obvious password. This user and password are the default for many
-# clients.
-#
-# The realm specified by -u is very important, and QPID is the default
-# for the broker so we use it here. The realm is important because it
-# defaults to the local hostname of the machine running the
-# broker. This may not seem to bad at first glance, but it means that
-# the sasldb has to be tailored to each machine that would be running
-# a broker, and if the machine ever changed its name the
-# authentication would stop working until the sasldb was updated. For
-# these reasons we always want the broker to specify a realm where its
-# users live, and we want the users to exist in that realm as well.
-$(SASL_DB):
- echo guest | $(SASL_PASSWD) -c -p -f $(SASL_DB) -u QPID guest
-
-CLEANFILES=$(SASL_DB)
-
endif
diff --git a/cpp/etc/qpidd.conf b/cpp/etc/qpidd.conf
index 8082660f6f..bfe4e38bbd 100644
--- a/cpp/etc/qpidd.conf
+++ b/cpp/etc/qpidd.conf
@@ -21,4 +21,4 @@
#
# (Note: no spaces on either side of '='). Using default settings:
# "qpidd --help" or "man qpidd" for more details.
-cluster-mechanism=ANONYMOUS
+cluster-mechanism=DIGEST-MD5 ANONYMOUS
diff --git a/cpp/etc/sasl2/qpidd.conf b/cpp/etc/sasl2/qpidd.conf
index 3197d7792a..d766cb8ef8 100644
--- a/cpp/etc/sasl2/qpidd.conf
+++ b/cpp/etc/sasl2/qpidd.conf
@@ -17,8 +17,8 @@
# under the License.
#
#
-# This configuation allows for either SASL PLAIN or ANONYMOUS
-# authentication. The PLAIN authentication is done on a
+# This configuation allows for either SASL ANONYMOUS or DIGEST-MD5
+# authentication. The DIGEST-MD5 authentication is done on a
# username+password, which is stored in the sasldb_path
# file. Usernames and passwords can be added to the file using the
# command:
@@ -39,6 +39,7 @@
pwcheck_method: auxprop
auxprop_plugin: sasldb
sasldb_path: /var/lib/qpidd/qpidd.sasldb
+mech_list: DIGEST-MD5 ANONYMOUS
#following line stops spurious 'sql_select option missing' errors when
#cyrus-sql-sasl plugin is installed
diff --git a/cpp/examples/CMakeLists.txt b/cpp/examples/CMakeLists.txt
index da8e39e944..1b28cfd031 100644
--- a/cpp/examples/CMakeLists.txt
+++ b/cpp/examples/CMakeLists.txt
@@ -77,25 +77,14 @@ macro(add_example subdir example)
endmacro(add_example)
install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/README.txt
- ${CMAKE_CURRENT_SOURCE_DIR}/README.verify
- ${CMAKE_CURRENT_SOURCE_DIR}/verify
- ${CMAKE_CURRENT_SOURCE_DIR}/verify_all
DESTINATION ${QPID_INSTALL_EXAMPLESDIR}
COMPONENT ${QPID_COMPONENT_EXAMPLES})
if (MSVC)
install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/examples.sln
- ${CMAKE_CURRENT_SOURCE_DIR}/old-examples.sln
DESTINATION ${QPID_INSTALL_EXAMPLESDIR}
COMPONENT ${QPID_COMPONENT_EXAMPLES})
endif (MSVC)
-add_subdirectory(direct)
-add_subdirectory(failover)
-add_subdirectory(fanout)
-add_subdirectory(pub-sub)
-#add_subdirectory(qmf-agent)
add_subdirectory(qmf-console)
-add_subdirectory(request-response)
-add_subdirectory(tradedemo)
-add_subdirectory(xml-exchange)
add_subdirectory(messaging)
+add_subdirectory(old_api)
diff --git a/cpp/examples/Makefile.am b/cpp/examples/Makefile.am
index c6cc308d98..6b2bb73587 100644
--- a/cpp/examples/Makefile.am
+++ b/cpp/examples/Makefile.am
@@ -16,15 +16,7 @@
# specific language governing permissions and limitations
# under the License.
#
-SUBDIRS = direct fanout pub-sub request-response failover qmf-console tradedemo messaging
-if HAVE_XML
- SUBDIRS += xml-exchange
- broker_args = "--no-module-dir --data-dir \"\" --auth no --load-module $(top_builddir)/src/.libs/xml.so"
-endif
-if !HAVE_XML
- exclude_examples_regexp="xml" # Exclude XML examples.
- broker_args = "--no-module-dir --data-dir \"\" --auth no"
-endif
+SUBDIRS = qmf-console messaging old_api
MAKEDIST=.libs/Makefile
@@ -37,13 +29,9 @@ $(MAKEDIST): Makefile
examplesdir=$(pkgdatadir)/examples
dist_examples_DATA = README.txt $(MAKEDIST)
-EXTRA_DIST = README.verify verify verify_all examples.sln CMakeLists.txt
+EXTRA_DIST = examples.sln CMakeLists.txt
# For older versions of automake
abs_top_srcdir = @abs_top_srcdir@
abs_top_builddir = @abs_top_builddir@
-# Verify the examples in the buid tree.
-check-local:
- $(srcdir)/verify_all $(abs_top_srcdir)/.. $(abs_top_builddir) $(broker_args) $(exclude_examples_regexp)
-
diff --git a/cpp/examples/examples.sln b/cpp/examples/examples.sln
index 8511fe3cce..6f96105d97 100644
--- a/cpp/examples/examples.sln
+++ b/cpp/examples/examples.sln
@@ -32,6 +32,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "messaging_drain", "messagin
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "messaging_spout", "messaging\messaging_spout.vcproj", "{D3115AC9-91C4-4D79-BCAC-DE837C70F1EA}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_console", "qmf-console\qmf-console_console.vcproj", "{490473E1-FECA-1BAD-2E13-3FFA2B8669C3}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_ping", "qmf-console\qmf-console_ping.vcproj", "{C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_printevents", "qmf-console\qmf-console_printevents.vcproj", "{72C74624-FECA-1BAD-2E13-3FFA2B8669C3}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_queuestats", "qmf-console\qmf-console_queuestats.vcproj", "{B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -88,6 +96,22 @@ Global
{D3115AC9-91C4-4D79-BCAC-DE837C70F1EA}.Release|Win32.Build.0 = Release|Win32
{D3115AC9-91C4-4D79-BCAC-DE837C70F1EA}.Release|x64.ActiveCfg = Release|x64
{D3115AC9-91C4-4D79-BCAC-DE837C70F1EA}.Release|x64.Build.0 = Release|x64
+ {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.Build.0 = Debug|Win32
+ {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.ActiveCfg = Release|Win32
+ {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.Build.0 = Release|Win32
+ {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Debug|Win32.Build.0 = Debug|Win32
+ {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Release|Win32.ActiveCfg = Release|Win32
+ {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Release|Win32.Build.0 = Release|Win32
+ {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.Build.0 = Debug|Win32
+ {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.ActiveCfg = Release|Win32
+ {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.Build.0 = Release|Win32
+ {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.Build.0 = Debug|Win32
+ {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.ActiveCfg = Release|Win32
+ {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/cpp/examples/messaging/drain.cpp b/cpp/examples/messaging/drain.cpp
index 5c938e9742..563e5e5060 100644
--- a/cpp/examples/messaging/drain.cpp
+++ b/cpp/examples/messaging/drain.cpp
@@ -45,12 +45,12 @@ struct Options : OptionParser
url("127.0.0.1"),
timeout(0),
forever(false),
- count(1)
+ count(0)
{
add("broker,b", url, "url of broker to connect to");
add("timeout,t", timeout, "timeout in seconds to wait before exiting");
add("forever,f", forever, "ignore timeout and wait forever");
- add("connection-options", connectionOptions, "connection options string in the form {name1=value1, name2=value2}");
+ add("connection-options", connectionOptions, "connection options string in the form {name1:value1, name2:value2}");
add("count,c", count, "number of messages to read before exiting");
}
diff --git a/cpp/examples/messaging/server.cpp b/cpp/examples/messaging/server.cpp
index ab72694c61..aa271d91f9 100644
--- a/cpp/examples/messaging/server.cpp
+++ b/cpp/examples/messaging/server.cpp
@@ -39,8 +39,8 @@ using std::string;
int main(int argc, char** argv) {
const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672";
- std::string connectionOptions = argc > 3 ? argv[3] : "";
-
+ std::string connectionOptions = argc > 2 ? argv[2] : "";
+
Connection connection(url, connectionOptions);
try {
connection.open();
diff --git a/cpp/examples/messaging/spout.cpp b/cpp/examples/messaging/spout.cpp
index 57b955c1de..cd11a7ad81 100644
--- a/cpp/examples/messaging/spout.cpp
+++ b/cpp/examples/messaging/spout.cpp
@@ -65,7 +65,7 @@ struct Options : OptionParser
add("property,P", properties, "specify message property");
add("map,M", entries, "specify entry for map content");
add("content", content, "specify textual content");
- add("connection-options", connectionOptions, "connection options string in the form {name1=value1, name2=value2}");
+ add("connection-options", connectionOptions, "connection options string in the form {name1:value1, name2:value2}");
}
static bool nameval(const std::string& in, std::string& name, std::string& value)
diff --git a/cpp/examples/old_api/CMakeLists.txt b/cpp/examples/old_api/CMakeLists.txt
new file mode 100644
index 0000000000..701f9be860
--- /dev/null
+++ b/cpp/examples/old_api/CMakeLists.txt
@@ -0,0 +1,25 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+add_subdirectory(direct)
+add_subdirectory(failover)
+add_subdirectory(fanout)
+add_subdirectory(pub-sub)
+add_subdirectory(request-response)
+add_subdirectory(tradedemo)
+add_subdirectory(xml-exchange)
diff --git a/cpp/examples/old_api/Makefile.am b/cpp/examples/old_api/Makefile.am
new file mode 100644
index 0000000000..04216ffa97
--- /dev/null
+++ b/cpp/examples/old_api/Makefile.am
@@ -0,0 +1,48 @@
+#
+# 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.
+#
+SUBDIRS = direct pub-sub fanout request-response failover tradedemo
+if HAVE_XML
+ SUBDIRS += xml-exchange
+ broker_args = "--no-module-dir --data-dir \"\" --auth no --load-module $(top_builddir)/src/.libs/xml.so"
+endif
+if !HAVE_XML
+ exclude_examples_regexp="xml" # Exclude XML examples.
+ broker_args = "--no-module-dir --data-dir \"\" --auth no"
+endif
+
+MAKEDIST=.libs/Makefile
+
+$(MAKEDIST): Makefile
+ mkdir -p .libs
+ @(echo 'all clean:' ; \
+ echo ' for d in $(SUBDIRS) ; do $$(MAKE) -C $$$$d $$@ ; done' ; \
+ ) > $(MAKEDIST)
+
+examplesdir=$(pkgdatadir)/examples/old_api
+dist_examples_DATA = $(MAKEDIST)
+EXTRA_DIST = README.verify verify verify_all CMakeLists.txt
+
+# For older versions of automake
+abs_top_srcdir = @abs_top_srcdir@
+abs_top_builddir = @abs_top_builddir@
+
+# Verify the examples in the buid tree.
+check-local:
+ $(srcdir)/verify_all $(abs_top_srcdir)/.. $(abs_top_builddir) $(broker_args) $(exclude_examples_regexp)
+
diff --git a/cpp/examples/README.verify b/cpp/examples/old_api/README.verify
index e1370764c9..e1370764c9 100644
--- a/cpp/examples/README.verify
+++ b/cpp/examples/old_api/README.verify
diff --git a/cpp/examples/direct/CMakeLists.txt b/cpp/examples/old_api/direct/CMakeLists.txt
index 2ec1b2b813..2ec1b2b813 100644
--- a/cpp/examples/direct/CMakeLists.txt
+++ b/cpp/examples/old_api/direct/CMakeLists.txt
diff --git a/cpp/examples/direct/Makefile.am b/cpp/examples/old_api/direct/Makefile.am
index b07db2cfd6..24f783fcc7 100644
--- a/cpp/examples/direct/Makefile.am
+++ b/cpp/examples/old_api/direct/Makefile.am
@@ -16,7 +16,7 @@
# specific language governing permissions and limitations
# under the License.
#
-examplesdir=$(pkgdatadir)/examples/direct
+examplesdir=$(pkgdatadir)/examples/old_api/direct
MAKELDFLAGS=$(CLIENTFLAGS)
include $(top_srcdir)/examples/makedist.mk
diff --git a/cpp/examples/direct/declare_queues.cpp b/cpp/examples/old_api/direct/declare_queues.cpp
index 9a51d1982b..9a51d1982b 100644
--- a/cpp/examples/direct/declare_queues.cpp
+++ b/cpp/examples/old_api/direct/declare_queues.cpp
diff --git a/cpp/examples/direct/direct_declare_queues.vcproj b/cpp/examples/old_api/direct/direct_declare_queues.vcproj
index 083474b9ef..083474b9ef 100644
--- a/cpp/examples/direct/direct_declare_queues.vcproj
+++ b/cpp/examples/old_api/direct/direct_declare_queues.vcproj
diff --git a/cpp/examples/direct/direct_direct_producer.vcproj b/cpp/examples/old_api/direct/direct_direct_producer.vcproj
index f091fbf291..f091fbf291 100644
--- a/cpp/examples/direct/direct_direct_producer.vcproj
+++ b/cpp/examples/old_api/direct/direct_direct_producer.vcproj
diff --git a/cpp/examples/direct/direct_listener.vcproj b/cpp/examples/old_api/direct/direct_listener.vcproj
index dce1d3ec28..dce1d3ec28 100644
--- a/cpp/examples/direct/direct_listener.vcproj
+++ b/cpp/examples/old_api/direct/direct_listener.vcproj
diff --git a/cpp/examples/direct/direct_producer.cpp b/cpp/examples/old_api/direct/direct_producer.cpp
index ecc9675189..ecc9675189 100644
--- a/cpp/examples/direct/direct_producer.cpp
+++ b/cpp/examples/old_api/direct/direct_producer.cpp
diff --git a/cpp/examples/direct/listener.cpp b/cpp/examples/old_api/direct/listener.cpp
index 38bf24ec41..38bf24ec41 100644
--- a/cpp/examples/direct/listener.cpp
+++ b/cpp/examples/old_api/direct/listener.cpp
diff --git a/cpp/examples/direct/verify b/cpp/examples/old_api/direct/verify
index f598bacc1f..f598bacc1f 100644
--- a/cpp/examples/direct/verify
+++ b/cpp/examples/old_api/direct/verify
diff --git a/cpp/examples/direct/verify.in b/cpp/examples/old_api/direct/verify.in
index d1e95f1151..d1e95f1151 100644
--- a/cpp/examples/direct/verify.in
+++ b/cpp/examples/old_api/direct/verify.in
diff --git a/cpp/examples/failover/CMakeLists.txt b/cpp/examples/old_api/failover/CMakeLists.txt
index 05db8fad51..05db8fad51 100644
--- a/cpp/examples/failover/CMakeLists.txt
+++ b/cpp/examples/old_api/failover/CMakeLists.txt
diff --git a/cpp/examples/failover/Makefile.am b/cpp/examples/old_api/failover/Makefile.am
index 48846fdf79..8b1da80f2c 100644
--- a/cpp/examples/failover/Makefile.am
+++ b/cpp/examples/old_api/failover/Makefile.am
@@ -16,7 +16,7 @@
# specific language governing permissions and limitations
# under the License.
#
-examplesdir=$(pkgdatadir)/examples/failover
+examplesdir=$(pkgdatadir)/examples/old_api/failover
MAKELDFLAGS=$(CLIENTFLAGS)
include $(top_srcdir)/examples/makedist.mk
diff --git a/cpp/examples/failover/declare_queues.cpp b/cpp/examples/old_api/failover/declare_queues.cpp
index a677870c53..a677870c53 100644
--- a/cpp/examples/failover/declare_queues.cpp
+++ b/cpp/examples/old_api/failover/declare_queues.cpp
diff --git a/cpp/examples/failover/failover_declare_queues.vcproj b/cpp/examples/old_api/failover/failover_declare_queues.vcproj
index c87c72affd..c87c72affd 100644
--- a/cpp/examples/failover/failover_declare_queues.vcproj
+++ b/cpp/examples/old_api/failover/failover_declare_queues.vcproj
diff --git a/cpp/examples/failover/failover_replaying_sender.vcproj b/cpp/examples/old_api/failover/failover_replaying_sender.vcproj
index 6d22fa6770..6d22fa6770 100644
--- a/cpp/examples/failover/failover_replaying_sender.vcproj
+++ b/cpp/examples/old_api/failover/failover_replaying_sender.vcproj
diff --git a/cpp/examples/failover/failover_resuming_receiver.vcproj b/cpp/examples/old_api/failover/failover_resuming_receiver.vcproj
index ba5061e248..ba5061e248 100644
--- a/cpp/examples/failover/failover_resuming_receiver.vcproj
+++ b/cpp/examples/old_api/failover/failover_resuming_receiver.vcproj
diff --git a/cpp/examples/failover/replaying_sender.cpp b/cpp/examples/old_api/failover/replaying_sender.cpp
index 22a7e1ebd3..22a7e1ebd3 100644
--- a/cpp/examples/failover/replaying_sender.cpp
+++ b/cpp/examples/old_api/failover/replaying_sender.cpp
diff --git a/cpp/examples/failover/resuming_receiver.cpp b/cpp/examples/old_api/failover/resuming_receiver.cpp
index d1886ce861..d1886ce861 100644
--- a/cpp/examples/failover/resuming_receiver.cpp
+++ b/cpp/examples/old_api/failover/resuming_receiver.cpp
diff --git a/cpp/examples/fanout/CMakeLists.txt b/cpp/examples/old_api/fanout/CMakeLists.txt
index 3f89d67650..3f89d67650 100644
--- a/cpp/examples/fanout/CMakeLists.txt
+++ b/cpp/examples/old_api/fanout/CMakeLists.txt
diff --git a/cpp/examples/fanout/Makefile.am b/cpp/examples/old_api/fanout/Makefile.am
index 6e2e821eae..3ab43b0279 100644
--- a/cpp/examples/fanout/Makefile.am
+++ b/cpp/examples/old_api/fanout/Makefile.am
@@ -16,7 +16,7 @@
# specific language governing permissions and limitations
# under the License.
#
-examplesdir=$(pkgdatadir)/examples/fanout
+examplesdir=$(pkgdatadir)/examples/old_api/fanout
MAKELDFLAGS=$(CLIENTFLAGS)
include $(top_srcdir)/examples/makedist.mk
diff --git a/cpp/examples/fanout/fanout_fanout_producer.vcproj b/cpp/examples/old_api/fanout/fanout_fanout_producer.vcproj
index daff5f3cf0..daff5f3cf0 100644
--- a/cpp/examples/fanout/fanout_fanout_producer.vcproj
+++ b/cpp/examples/old_api/fanout/fanout_fanout_producer.vcproj
diff --git a/cpp/examples/fanout/fanout_listener.vcproj b/cpp/examples/old_api/fanout/fanout_listener.vcproj
index f0e91b7dc6..f0e91b7dc6 100644
--- a/cpp/examples/fanout/fanout_listener.vcproj
+++ b/cpp/examples/old_api/fanout/fanout_listener.vcproj
diff --git a/cpp/examples/fanout/fanout_producer.cpp b/cpp/examples/old_api/fanout/fanout_producer.cpp
index decd4d314d..decd4d314d 100644
--- a/cpp/examples/fanout/fanout_producer.cpp
+++ b/cpp/examples/old_api/fanout/fanout_producer.cpp
diff --git a/cpp/examples/fanout/listener.cpp b/cpp/examples/old_api/fanout/listener.cpp
index cd3071c29a..cd3071c29a 100644
--- a/cpp/examples/fanout/listener.cpp
+++ b/cpp/examples/old_api/fanout/listener.cpp
diff --git a/cpp/examples/fanout/verify b/cpp/examples/old_api/fanout/verify
index 2eaadff56b..2eaadff56b 100644
--- a/cpp/examples/fanout/verify
+++ b/cpp/examples/old_api/fanout/verify
diff --git a/cpp/examples/fanout/verify.in b/cpp/examples/old_api/fanout/verify.in
index 8f8612ce67..8f8612ce67 100644
--- a/cpp/examples/fanout/verify.in
+++ b/cpp/examples/old_api/fanout/verify.in
diff --git a/cpp/examples/old-examples.sln b/cpp/examples/old_api/old-examples.sln
index 7f2fa3e8b0..e6ec9a0d66 100644
--- a/cpp/examples/old-examples.sln
+++ b/cpp/examples/old_api/old-examples.sln
@@ -40,14 +40,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pub_sub_topic_listener", "p
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pub_sub_topic_publisher", "pub-sub\pub-sub_topic_publisher.vcproj", "{05158653-FECA-1BAD-A430-FD5330E23A2D}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_console", "qmf-console\qmf-console_console.vcproj", "{490473E1-FECA-1BAD-2E13-3FFA2B8669C3}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_ping", "qmf-console\qmf-console_ping.vcproj", "{C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_printevents", "qmf-console\qmf-console_printevents.vcproj", "{72C74624-FECA-1BAD-2E13-3FFA2B8669C3}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_queuestats", "qmf-console\qmf-console_queuestats.vcproj", "{B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}"
-EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "request_response_client", "request-response\request-response_client.vcproj", "{2691FE1E-FECA-1BAD-BD3A-8A467D0C5CCC}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "request_response_server", "request-response\request-response_server.vcproj", "{46817425-FECA-1BAD-BD3A-8A467D0C5CCC}"
@@ -104,22 +96,6 @@ Global
{05158653-FECA-1BAD-A430-FD5330E23A2D}.Debug|Win32.Build.0 = Debug|Win32
{05158653-FECA-1BAD-A430-FD5330E23A2D}.Release|Win32.ActiveCfg = Release|Win32
{05158653-FECA-1BAD-A430-FD5330E23A2D}.Release|Win32.Build.0 = Release|Win32
- {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.ActiveCfg = Debug|Win32
- {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.Build.0 = Debug|Win32
- {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.ActiveCfg = Release|Win32
- {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.Build.0 = Release|Win32
- {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Debug|Win32.ActiveCfg = Debug|Win32
- {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Debug|Win32.Build.0 = Debug|Win32
- {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Release|Win32.ActiveCfg = Release|Win32
- {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Release|Win32.Build.0 = Release|Win32
- {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.ActiveCfg = Debug|Win32
- {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.Build.0 = Debug|Win32
- {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.ActiveCfg = Release|Win32
- {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.Build.0 = Release|Win32
- {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.ActiveCfg = Debug|Win32
- {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.Build.0 = Debug|Win32
- {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.ActiveCfg = Release|Win32
- {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.Build.0 = Release|Win32
{2691FE1E-FECA-1BAD-BD3A-8A467D0C5CCC}.Debug|Win32.ActiveCfg = Debug|Win32
{2691FE1E-FECA-1BAD-BD3A-8A467D0C5CCC}.Debug|Win32.Build.0 = Debug|Win32
{2691FE1E-FECA-1BAD-BD3A-8A467D0C5CCC}.Release|Win32.ActiveCfg = Release|Win32
diff --git a/cpp/examples/pub-sub/CMakeLists.txt b/cpp/examples/old_api/pub-sub/CMakeLists.txt
index 961de06d5a..961de06d5a 100644
--- a/cpp/examples/pub-sub/CMakeLists.txt
+++ b/cpp/examples/old_api/pub-sub/CMakeLists.txt
diff --git a/cpp/examples/pub-sub/Makefile.am b/cpp/examples/old_api/pub-sub/Makefile.am
index 62658ebe94..8f42ee0211 100644
--- a/cpp/examples/pub-sub/Makefile.am
+++ b/cpp/examples/old_api/pub-sub/Makefile.am
@@ -16,7 +16,7 @@
# specific language governing permissions and limitations
# under the License.
#
-examplesdir=$(pkgdatadir)/examples/pub-sub
+examplesdir=$(pkgdatadir)/examples/old_api/pub-sub
MAKELDFLAGS=$(CLIENTFLAGS)
include $(top_srcdir)/examples/makedist.mk
diff --git a/cpp/examples/pub-sub/pub-sub_topic_listener.vcproj b/cpp/examples/old_api/pub-sub/pub-sub_topic_listener.vcproj
index aa0b3bcaa3..aa0b3bcaa3 100644
--- a/cpp/examples/pub-sub/pub-sub_topic_listener.vcproj
+++ b/cpp/examples/old_api/pub-sub/pub-sub_topic_listener.vcproj
diff --git a/cpp/examples/pub-sub/pub-sub_topic_publisher.vcproj b/cpp/examples/old_api/pub-sub/pub-sub_topic_publisher.vcproj
index 76e51df4df..76e51df4df 100644
--- a/cpp/examples/pub-sub/pub-sub_topic_publisher.vcproj
+++ b/cpp/examples/old_api/pub-sub/pub-sub_topic_publisher.vcproj
diff --git a/cpp/examples/pub-sub/topic_listener.cpp b/cpp/examples/old_api/pub-sub/topic_listener.cpp
index d38a806303..d38a806303 100644
--- a/cpp/examples/pub-sub/topic_listener.cpp
+++ b/cpp/examples/old_api/pub-sub/topic_listener.cpp
diff --git a/cpp/examples/pub-sub/topic_publisher.cpp b/cpp/examples/old_api/pub-sub/topic_publisher.cpp
index aed5f8f033..aed5f8f033 100644
--- a/cpp/examples/pub-sub/topic_publisher.cpp
+++ b/cpp/examples/old_api/pub-sub/topic_publisher.cpp
diff --git a/cpp/examples/pub-sub/verify b/cpp/examples/old_api/pub-sub/verify
index 528d2f401e..528d2f401e 100644
--- a/cpp/examples/pub-sub/verify
+++ b/cpp/examples/old_api/pub-sub/verify
diff --git a/cpp/examples/pub-sub/verify.in b/cpp/examples/old_api/pub-sub/verify.in
index 6413c5c788..6413c5c788 100644
--- a/cpp/examples/pub-sub/verify.in
+++ b/cpp/examples/old_api/pub-sub/verify.in
diff --git a/cpp/examples/request-response/CMakeLists.txt b/cpp/examples/old_api/request-response/CMakeLists.txt
index 873a0cfa86..873a0cfa86 100644
--- a/cpp/examples/request-response/CMakeLists.txt
+++ b/cpp/examples/old_api/request-response/CMakeLists.txt
diff --git a/cpp/examples/request-response/Makefile.am b/cpp/examples/old_api/request-response/Makefile.am
index 48b3d989f0..f48762da51 100644
--- a/cpp/examples/request-response/Makefile.am
+++ b/cpp/examples/old_api/request-response/Makefile.am
@@ -16,7 +16,7 @@
# specific language governing permissions and limitations
# under the License.
#
-examplesdir=$(pkgdatadir)/examples/request-response
+examplesdir=$(pkgdatadir)/examples/old_api/request-response
MAKELDFLAGS=$(CLIENTFLAGS)
include $(top_srcdir)/examples/makedist.mk
diff --git a/cpp/examples/request-response/client.cpp b/cpp/examples/old_api/request-response/client.cpp
index 679d1c5fc2..679d1c5fc2 100644
--- a/cpp/examples/request-response/client.cpp
+++ b/cpp/examples/old_api/request-response/client.cpp
diff --git a/cpp/examples/request-response/request-response_client.vcproj b/cpp/examples/old_api/request-response/request-response_client.vcproj
index 5f9eadde36..5f9eadde36 100644
--- a/cpp/examples/request-response/request-response_client.vcproj
+++ b/cpp/examples/old_api/request-response/request-response_client.vcproj
diff --git a/cpp/examples/request-response/request-response_server.vcproj b/cpp/examples/old_api/request-response/request-response_server.vcproj
index 54352b9f46..54352b9f46 100644
--- a/cpp/examples/request-response/request-response_server.vcproj
+++ b/cpp/examples/old_api/request-response/request-response_server.vcproj
diff --git a/cpp/examples/request-response/server.cpp b/cpp/examples/old_api/request-response/server.cpp
index 65a4717b35..65a4717b35 100644
--- a/cpp/examples/request-response/server.cpp
+++ b/cpp/examples/old_api/request-response/server.cpp
diff --git a/cpp/examples/request-response/verify b/cpp/examples/old_api/request-response/verify
index dee82413e7..dee82413e7 100644
--- a/cpp/examples/request-response/verify
+++ b/cpp/examples/old_api/request-response/verify
diff --git a/cpp/examples/request-response/verify.in b/cpp/examples/old_api/request-response/verify.in
index 7925dc5671..7925dc5671 100644
--- a/cpp/examples/request-response/verify.in
+++ b/cpp/examples/old_api/request-response/verify.in
diff --git a/cpp/examples/tradedemo/CMakeLists.txt b/cpp/examples/old_api/tradedemo/CMakeLists.txt
index e61fc1467d..e61fc1467d 100644
--- a/cpp/examples/tradedemo/CMakeLists.txt
+++ b/cpp/examples/old_api/tradedemo/CMakeLists.txt
diff --git a/cpp/examples/tradedemo/Makefile.am b/cpp/examples/old_api/tradedemo/Makefile.am
index f4d8686d05..445b15b367 100644
--- a/cpp/examples/tradedemo/Makefile.am
+++ b/cpp/examples/old_api/tradedemo/Makefile.am
@@ -16,7 +16,7 @@
# specific language governing permissions and limitations
# under the License.
#
-examplesdir=$(pkgdatadir)/examples/tradedemo
+examplesdir=$(pkgdatadir)/examples/old_api/tradedemo
MAKELDFLAGS=$(CLIENTFLAGS)
include $(top_srcdir)/examples/makedist.mk
diff --git a/cpp/examples/tradedemo/declare_queues.cpp b/cpp/examples/old_api/tradedemo/declare_queues.cpp
index b1f2cc3510..b1f2cc3510 100644
--- a/cpp/examples/tradedemo/declare_queues.cpp
+++ b/cpp/examples/old_api/tradedemo/declare_queues.cpp
diff --git a/cpp/examples/tradedemo/topic_listener.cpp b/cpp/examples/old_api/tradedemo/topic_listener.cpp
index c488e7fb69..c488e7fb69 100644
--- a/cpp/examples/tradedemo/topic_listener.cpp
+++ b/cpp/examples/old_api/tradedemo/topic_listener.cpp
diff --git a/cpp/examples/tradedemo/topic_publisher.cpp b/cpp/examples/old_api/tradedemo/topic_publisher.cpp
index e22c185bc7..e22c185bc7 100644
--- a/cpp/examples/tradedemo/topic_publisher.cpp
+++ b/cpp/examples/old_api/tradedemo/topic_publisher.cpp
diff --git a/cpp/examples/tradedemo/tradedemo_declare_queues.vcproj b/cpp/examples/old_api/tradedemo/tradedemo_declare_queues.vcproj
index 34b5cb3b2b..34b5cb3b2b 100644
--- a/cpp/examples/tradedemo/tradedemo_declare_queues.vcproj
+++ b/cpp/examples/old_api/tradedemo/tradedemo_declare_queues.vcproj
diff --git a/cpp/examples/tradedemo/tradedemo_topic_listener.vcproj b/cpp/examples/old_api/tradedemo/tradedemo_topic_listener.vcproj
index 965be2e88b..965be2e88b 100644
--- a/cpp/examples/tradedemo/tradedemo_topic_listener.vcproj
+++ b/cpp/examples/old_api/tradedemo/tradedemo_topic_listener.vcproj
diff --git a/cpp/examples/tradedemo/tradedemo_topic_publisher.vcproj b/cpp/examples/old_api/tradedemo/tradedemo_topic_publisher.vcproj
index 77fd511e15..77fd511e15 100644
--- a/cpp/examples/tradedemo/tradedemo_topic_publisher.vcproj
+++ b/cpp/examples/old_api/tradedemo/tradedemo_topic_publisher.vcproj
diff --git a/cpp/examples/verify b/cpp/examples/old_api/verify
index 9a1ed078d6..9a1ed078d6 100755
--- a/cpp/examples/verify
+++ b/cpp/examples/old_api/verify
diff --git a/cpp/examples/verify_all b/cpp/examples/old_api/verify_all
index cb4c5283fa..fbe51377b6 100755
--- a/cpp/examples/verify_all
+++ b/cpp/examples/old_api/verify_all
@@ -32,7 +32,7 @@ trap "$qpidd -q" exit
QPID_PORT=`$qpidd -dp0 $broker_args` || { echo "Can't run qpidd" ; exit 1; }
export QPID_PORT
-find="find $topsrcdir/cpp/examples"
+find="find $topsrcdir/cpp/examples/old_api"
find="$find -mindepth 2 -name verify"
all_examples=`$find`
diff --git a/cpp/examples/xml-exchange/CMakeLists.txt b/cpp/examples/old_api/xml-exchange/CMakeLists.txt
index 3fea47a208..3fea47a208 100644
--- a/cpp/examples/xml-exchange/CMakeLists.txt
+++ b/cpp/examples/old_api/xml-exchange/CMakeLists.txt
diff --git a/cpp/examples/xml-exchange/Makefile.am b/cpp/examples/old_api/xml-exchange/Makefile.am
index 9001e3fa61..3e1082cdb2 100644
--- a/cpp/examples/xml-exchange/Makefile.am
+++ b/cpp/examples/old_api/xml-exchange/Makefile.am
@@ -16,7 +16,7 @@
# specific language governing permissions and limitations
# under the License.
#
-examplesdir=$(pkgdatadir)/examples/xml-exchange
+examplesdir=$(pkgdatadir)/examples/old_api/xml-exchange
MAKELDFLAGS=$(CLIENTFLAGS)
include $(top_srcdir)/examples/makedist.mk
diff --git a/cpp/examples/xml-exchange/README.txt b/cpp/examples/old_api/xml-exchange/README.txt
index 85caebe352..85caebe352 100644
--- a/cpp/examples/xml-exchange/README.txt
+++ b/cpp/examples/old_api/xml-exchange/README.txt
diff --git a/cpp/examples/xml-exchange/declare_queues.cpp b/cpp/examples/old_api/xml-exchange/declare_queues.cpp
index ad08642019..ad08642019 100644
--- a/cpp/examples/xml-exchange/declare_queues.cpp
+++ b/cpp/examples/old_api/xml-exchange/declare_queues.cpp
diff --git a/cpp/examples/xml-exchange/listener.cpp b/cpp/examples/old_api/xml-exchange/listener.cpp
index 11bcb9f669..11bcb9f669 100644
--- a/cpp/examples/xml-exchange/listener.cpp
+++ b/cpp/examples/old_api/xml-exchange/listener.cpp
diff --git a/cpp/examples/xml-exchange/xml_producer.cpp b/cpp/examples/old_api/xml-exchange/xml_producer.cpp
index af1a7e60c7..af1a7e60c7 100644
--- a/cpp/examples/xml-exchange/xml_producer.cpp
+++ b/cpp/examples/old_api/xml-exchange/xml_producer.cpp
diff --git a/cpp/examples/qmf-console/ping.cpp b/cpp/examples/qmf-console/ping.cpp
index fe537d48d2..e6d6d138d5 100644
--- a/cpp/examples/qmf-console/ping.cpp
+++ b/cpp/examples/qmf-console/ping.cpp
@@ -31,9 +31,7 @@ using namespace qpid::console;
int main_int(int /*argc*/, char** /*argv*/)
{
//
- // Declare connection settings for the messaging broker. The settings default to
- // localhost:5672 with user guest (password guest). Refer to the header file
- // <qpid/client/ConnectionSettings.h> for full details.
+ // Declare connection settings for the messaging broker.
//
qpid::client::ConnectionSettings connSettings;
diff --git a/cpp/examples/qmf-console/printevents.cpp b/cpp/examples/qmf-console/printevents.cpp
index 3a0a2ab68b..ac3e449a2c 100644
--- a/cpp/examples/qmf-console/printevents.cpp
+++ b/cpp/examples/qmf-console/printevents.cpp
@@ -64,9 +64,7 @@ struct Main {
Listener listener;
//
- // Declare connection settings for the messaging broker. The settings default to
- // localhost:5672 with user guest (password guest). Refer to the header file
- // <qpid/client/ConnectionSettings.h> for full details.
+ // Declare connection settings for the messaging broker.
//
qpid::client::ConnectionSettings connSettings;
diff --git a/cpp/include/qmf/Agent.h b/cpp/include/qmf/Agent.h
index 8c0f48b8b1..94083be4f3 100644
--- a/cpp/include/qmf/Agent.h
+++ b/cpp/include/qmf/Agent.h
@@ -42,7 +42,7 @@ namespace qmf {
class SchemaId;
class Schema;
- class Agent : public qmf::Handle<AgentImpl> {
+ class QMF_CLASS_EXTERN Agent : public qmf::Handle<AgentImpl> {
public:
QMF_EXTERN Agent(AgentImpl* impl = 0);
QMF_EXTERN Agent(const Agent&);
diff --git a/cpp/include/qmf/AgentEvent.h b/cpp/include/qmf/AgentEvent.h
index 59a41c3267..0f93a9bb0a 100644
--- a/cpp/include/qmf/AgentEvent.h
+++ b/cpp/include/qmf/AgentEvent.h
@@ -46,7 +46,7 @@ namespace qmf {
AGENT_THREAD_FAILED = 8
};
- class AgentEvent : public qmf::Handle<AgentEventImpl> {
+ class QMF_CLASS_EXTERN AgentEvent : public qmf::Handle<AgentEventImpl> {
public:
QMF_EXTERN AgentEvent(AgentEventImpl* impl = 0);
QMF_EXTERN AgentEvent(const AgentEvent&);
diff --git a/cpp/include/qmf/AgentSession.h b/cpp/include/qmf/AgentSession.h
index 9e29d6b54b..589d364bcc 100644
--- a/cpp/include/qmf/AgentSession.h
+++ b/cpp/include/qmf/AgentSession.h
@@ -40,7 +40,7 @@ namespace qmf {
class Data;
class DataAddr;
- class AgentSession : public qmf::Handle<AgentSessionImpl> {
+ class QMF_CLASS_EXTERN AgentSession : public qmf::Handle<AgentSessionImpl> {
public:
QMF_EXTERN AgentSession(AgentSessionImpl* impl = 0);
QMF_EXTERN AgentSession(const AgentSession&);
@@ -71,15 +71,20 @@ namespace qmf {
* If False: Listen only on the routable direct address
* strict-security:{True,False} - If True: Cooperate with the broker to enforce strict access control to the network
* - If False: Operate more flexibly with regard to use of messaging facilities [default]
+ * max-thread-wait-time:N - Time (in seconds) the session thread will wait for messages from the network between
+ * periodic background processing passes. [default: 5]
+ * Must not be greater than 'interval'. Larger numbers will cause fewer wake-ups but will
+ * increase the time it takes to shut down the process. This setting will not affect the
+ * agent's response time for queries or method invocation.
*/
- QMF_EXTERN AgentSession(qpid::messaging::Connection&, const std::string& options="");
+ QMF_EXTERN AgentSession(qpid::messaging::Connection& conn, const std::string& options="");
/**
* setDomain - Change the QMF domain that this agent will operate in. If this is not called,
* the domain will be "default". Agents in a domain can be seen only by consoles in the same domain.
* This must be called prior to opening the agent session.
*/
- QMF_EXTERN void setDomain(const std::string&);
+ QMF_EXTERN void setDomain(const std::string& domain);
/**
* Set identifying attributes of this agent.
@@ -88,16 +93,16 @@ namespace qmf {
* setInstance - Set the unique instance name (if not set, a UUID will be assigned)
* These must be called prior to opening the agent session.
*/
- QMF_EXTERN void setVendor(const std::string&);
- QMF_EXTERN void setProduct(const std::string&);
- QMF_EXTERN void setInstance(const std::string&);
+ QMF_EXTERN void setVendor(const std::string& vendor);
+ QMF_EXTERN void setProduct(const std::string& product);
+ QMF_EXTERN void setInstance(const std::string& instance);
/**
* setAttribute - Set an arbitrary attribute for this agent. The attributes are not used
* to uniquely identify the agent but can be used as a search criteria when looking for agents.
* This must be called prior to opening the agent session.
*/
- QMF_EXTERN void setAttribute(const std::string&, const qpid::types::Variant&);
+ QMF_EXTERN void setAttribute(const std::string& key, const qpid::types::Variant& value);
/**
* Get the identifying name of the agent.
@@ -119,13 +124,19 @@ namespace qmf {
* Get the next event from the agent session. Events represent actions that must be acted upon by the
* agent application. This method blocks for up to the timeout if there are no events to be handled.
* This method will typically be the focus of the agent application's main execution loop.
+ * If the timeout is set to Duration::IMMEDIATE, the call will not block.
*/
- QMF_EXTERN bool nextEvent(AgentEvent&, qpid::messaging::Duration timeout=qpid::messaging::Duration::FOREVER);
+ QMF_EXTERN bool nextEvent(AgentEvent& outEvent, qpid::messaging::Duration timeout=qpid::messaging::Duration::FOREVER);
+
+ /**
+ * Return the number of events pending for nextEvent. This method will never block.
+ */
+ QMF_EXTERN int pendingEvents() const;
/**
* Register a schema to be exposed by this agent.
*/
- QMF_EXTERN void registerSchema(Schema&);
+ QMF_EXTERN void registerSchema(Schema& schema);
/**
* Add data to be managed internally by the agent. If the option external:True is selected, this call
@@ -138,12 +149,12 @@ namespace qmf {
* across different sessions. If persistent, it is the agent application's
* responsibility to ensure the name is the same each time it is added.
*/
- QMF_EXTERN DataAddr addData(Data&, const std::string& name="", bool persistent=false);
+ QMF_EXTERN DataAddr addData(Data& data, const std::string& name="", bool persistent=false);
/**
* Delete data from internal agent management.
*/
- QMF_EXTERN void delData(const DataAddr&);
+ QMF_EXTERN void delData(const DataAddr& dataAddr);
/**
* The following methods are used to respond to events received in nextEvent.
@@ -155,13 +166,13 @@ namespace qmf {
* complete - Indicate that the response to a query is complete (external:True only)
* methodSuccess - Indicate the successful completion of a method call.
*/
- QMF_EXTERN void authAccept(AgentEvent&);
- QMF_EXTERN void authReject(AgentEvent&, const std::string& diag="");
- QMF_EXTERN void raiseException(AgentEvent&, const std::string&);
- QMF_EXTERN void raiseException(AgentEvent&, const Data&);
- QMF_EXTERN void response(AgentEvent&, const Data&);
- QMF_EXTERN void complete(AgentEvent&);
- QMF_EXTERN void methodSuccess(AgentEvent&);
+ QMF_EXTERN void authAccept(AgentEvent& event);
+ QMF_EXTERN void authReject(AgentEvent& event, const std::string& diag="");
+ QMF_EXTERN void raiseException(AgentEvent& event, const std::string& errorText);
+ QMF_EXTERN void raiseException(AgentEvent& event, const Data& errorData);
+ QMF_EXTERN void response(AgentEvent& event, const Data& responseData);
+ QMF_EXTERN void complete(AgentEvent& event);
+ QMF_EXTERN void methodSuccess(AgentEvent& event);
/**
* Raise an event to be sent into the QMF network.
@@ -177,6 +188,7 @@ namespace qmf {
#ifndef SWIG
private:
friend class qmf::PrivateImplRef<AgentSession>;
+ friend struct AgentSessionImplAccess;
#endif
};
diff --git a/cpp/include/qmf/ConsoleEvent.h b/cpp/include/qmf/ConsoleEvent.h
index b836b629af..94600f9357 100644
--- a/cpp/include/qmf/ConsoleEvent.h
+++ b/cpp/include/qmf/ConsoleEvent.h
@@ -57,7 +57,7 @@ namespace qmf {
AGENT_DEL_FILTER = 2
};
- class ConsoleEvent : public qmf::Handle<ConsoleEventImpl> {
+ class QMF_CLASS_EXTERN ConsoleEvent : public qmf::Handle<ConsoleEventImpl> {
public:
QMF_EXTERN ConsoleEvent(ConsoleEventImpl* impl = 0);
QMF_EXTERN ConsoleEvent(const ConsoleEvent&);
diff --git a/cpp/include/qmf/ConsoleSession.h b/cpp/include/qmf/ConsoleSession.h
index 0c73e7a6db..022485cfa7 100644
--- a/cpp/include/qmf/ConsoleSession.h
+++ b/cpp/include/qmf/ConsoleSession.h
@@ -38,7 +38,7 @@ namespace qmf {
class ConsoleSessionImpl;
class ConsoleEvent;
- class ConsoleSession : public qmf::Handle<ConsoleSessionImpl> {
+ class QMF_CLASS_EXTERN ConsoleSession : public qmf::Handle<ConsoleSessionImpl> {
public:
QMF_EXTERN ConsoleSession(ConsoleSessionImpl* impl = 0);
QMF_EXTERN ConsoleSession(const ConsoleSession&);
@@ -61,15 +61,53 @@ namespace qmf {
* If False: Listen only on the routable direct address
* strict-security:{True,False} - If True: Cooperate with the broker to enforce strict access control to the network
* - If False: Operate more flexibly with regard to use of messaging facilities [default]
+ * max-thread-wait-time:N - Time (in seconds) the session thread will wait for messages from the network between
+ * periodic background processing passes.
+ * Must not be greater than 60. Larger numbers will cause fewer wake-ups but will
+ * increase the time it takes to shut down the process. [default: 5]
+ */
+ QMF_EXTERN ConsoleSession(qpid::messaging::Connection& conn, const std::string& options="");
+
+ /**
+ * setDomain - Change the QMF domain that this console will operate in. If this is not called,
+ * the domain will be "default". Agents in a domain can be seen only by consoles in the same domain.
+ * This must be called prior to opening the console session.
+ */
+ QMF_EXTERN void setDomain(const std::string& domain);
+ QMF_EXTERN void setAgentFilter(const std::string& filter);
+
+ /**
+ * Open the console session. After opening the session, the domain cannot be changed.
*/
- QMF_EXTERN ConsoleSession(qpid::messaging::Connection&, const std::string& options="");
- QMF_EXTERN void setDomain(const std::string&);
- QMF_EXTERN void setAgentFilter(const std::string&);
QMF_EXTERN void open();
+
+ /**
+ * Close the session. Once closed, the session no longer communicates on the messaging network.
+ */
QMF_EXTERN void close();
- QMF_EXTERN bool nextEvent(ConsoleEvent&, qpid::messaging::Duration timeout=qpid::messaging::Duration::FOREVER);
+
+ /**
+ * Get the next event from the console session. Events represent actions that must be acted upon by the
+ * console application. This method blocks for up to the timeout if there are no events to be handled.
+ * This method will typically be the focus of the console application's main execution loop.
+ * If the timeout is set to Duration::IMMEDIATE, the call will not block.
+ */
+ QMF_EXTERN bool nextEvent(ConsoleEvent& outEvent, qpid::messaging::Duration timeout=qpid::messaging::Duration::FOREVER);
+
+ /**
+ * Return the number of events pending for nextEvent. This method will never block.
+ */
+ QMF_EXTERN int pendingEvents() const;
+
+ /**
+ * getAgentCount, getAgent - Retrieve the set of agents that match the console session's agent filter.
+ */
QMF_EXTERN uint32_t getAgentCount() const;
- QMF_EXTERN Agent getAgent(uint32_t) const;
+ QMF_EXTERN Agent getAgent(uint32_t agentIndex) const;
+
+ /**
+ * Get the agent for the connected broker (i.e. the agent embedded in the broker to which we have a connection).
+ */
QMF_EXTERN Agent getConnectedBrokerAgent() const;
/**
@@ -79,12 +117,13 @@ namespace qmf {
* will involve all known agents. If agentFilter is non-empty, it will be applied only to the set of known
* agents. A subscription cannot be created that involves an agent not known by the session.
*/
- QMF_EXTERN Subscription subscribe(const Query&, const std::string& agentFilter = "", const std::string& options = "");
- QMF_EXTERN Subscription subscribe(const std::string&, const std::string& agentFilter = "", const std::string& options = "");
+ QMF_EXTERN Subscription subscribe(const Query& query, const std::string& agentFilter = "", const std::string& options = "");
+ QMF_EXTERN Subscription subscribe(const std::string& query, const std::string& agentFilter = "", const std::string& options = "");
#ifndef SWIG
private:
friend class qmf::PrivateImplRef<ConsoleSession>;
+ friend struct ConsoleSessionImplAccess;
#endif
};
diff --git a/cpp/include/qmf/Data.h b/cpp/include/qmf/Data.h
index 82f1569a0b..487a02fe95 100644
--- a/cpp/include/qmf/Data.h
+++ b/cpp/include/qmf/Data.h
@@ -39,7 +39,7 @@ namespace qmf {
class DataAddr;
class Agent;
- class Data : public qmf::Handle<DataImpl> {
+ class QMF_CLASS_EXTERN Data : public qmf::Handle<DataImpl> {
public:
QMF_EXTERN Data(DataImpl* impl = 0);
QMF_EXTERN Data(const Data&);
diff --git a/cpp/include/qmf/DataAddr.h b/cpp/include/qmf/DataAddr.h
index 72de0c986a..20c469081e 100644
--- a/cpp/include/qmf/DataAddr.h
+++ b/cpp/include/qmf/DataAddr.h
@@ -34,7 +34,7 @@ namespace qmf {
class DataAddrImpl;
- class DataAddr : public qmf::Handle<DataAddrImpl> {
+ class QMF_CLASS_EXTERN DataAddr : public qmf::Handle<DataAddrImpl> {
public:
QMF_EXTERN DataAddr(DataAddrImpl* impl = 0);
QMF_EXTERN DataAddr(const DataAddr&);
@@ -51,6 +51,9 @@ namespace qmf {
QMF_EXTERN uint32_t getAgentEpoch() const;
QMF_EXTERN qpid::types::Variant::Map asMap() const;
+ QMF_EXTERN bool operator==(const DataAddr&) const;
+ QMF_EXTERN bool operator<(const DataAddr&) const;
+
#ifndef SWIG
private:
friend class qmf::PrivateImplRef<DataAddr>;
diff --git a/cpp/include/qmf/Handle.h b/cpp/include/qmf/Handle.h
index 510e2993aa..50971ea626 100644
--- a/cpp/include/qmf/Handle.h
+++ b/cpp/include/qmf/Handle.h
@@ -39,22 +39,22 @@ template <class T> class Handle {
public:
/**@return true if handle is valid, i.e. not null. */
- QMF_EXTERN bool isValid() const { return impl; }
+ QMF_INLINE_EXTERN bool isValid() const { return impl; }
/**@return true if handle is null. It is an error to call any function on a null handle. */
- QMF_EXTERN bool isNull() const { return !impl; }
+ QMF_INLINE_EXTERN bool isNull() const { return !impl; }
/** Conversion to bool supports idiom if (handle) { handle->... } */
- QMF_EXTERN operator bool() const { return impl; }
+ QMF_INLINE_EXTERN operator bool() const { return impl; }
/** Operator ! supports idiom if (!handle) { do_if_handle_is_null(); } */
- QMF_EXTERN bool operator !() const { return !impl; }
+ QMF_INLINE_EXTERN bool operator !() const { return !impl; }
void swap(Handle<T>& h) { T* t = h.impl; h.impl = impl; impl = t; }
protected:
typedef T Impl;
- QMF_EXTERN Handle() :impl() {}
+ QMF_INLINE_EXTERN Handle() :impl() {}
// Not implemented,subclasses must implement.
QMF_EXTERN Handle(const Handle&);
diff --git a/cpp/include/qmf/ImportExport.h b/cpp/include/qmf/ImportExport.h
index f5e1d9127c..7405c15259 100644
--- a/cpp/include/qmf/ImportExport.h
+++ b/cpp/include/qmf/ImportExport.h
@@ -20,14 +20,16 @@
* under the License.
*/
-#if defined(WIN32) && !defined(QPID_DECLARE_STATIC)
-# if defined(QMF_EXPORT) || defined (qmfcommon_EXPORTS)
-# define QMF_EXTERN __declspec(dllexport)
-# else
-# define QMF_EXTERN __declspec(dllimport)
-# endif
+#include "qpid/ImportExport.h"
+
+#if defined(QMF_EXPORT) || defined (qmf2_EXPORTS)
+# define QMF_EXTERN QPID_EXPORT
+# define QMF_CLASS_EXTERN QPID_CLASS_EXPORT
+# define QMF_INLINE_EXTERN QPID_INLINE_EXPORT
#else
-# define QMF_EXTERN
+# define QMF_EXTERN QPID_IMPORT
+# define QMF_CLASS_EXTERN QPID_CLASS_IMPORT
+# define QMF_INLINE_EXTERN QPID_INLINE_IMPORT
#endif
#endif
diff --git a/cpp/include/qmf/Query.h b/cpp/include/qmf/Query.h
index fec4660bd7..c1264f8901 100644
--- a/cpp/include/qmf/Query.h
+++ b/cpp/include/qmf/Query.h
@@ -43,7 +43,7 @@ namespace qmf {
QUERY_SCHEMA_ID = 4
};
- class Query : public qmf::Handle<QueryImpl> {
+ class QMF_CLASS_EXTERN Query : public qmf::Handle<QueryImpl> {
public:
QMF_EXTERN Query(QueryImpl* impl = 0);
QMF_EXTERN Query(const Query&);
@@ -65,7 +65,7 @@ namespace qmf {
#ifndef SWIG
private:
friend class qmf::PrivateImplRef<Query>;
- friend class QueryImplAccess;
+ friend struct QueryImplAccess;
#endif
};
diff --git a/cpp/include/qmf/Schema.h b/cpp/include/qmf/Schema.h
index cf316138c1..6cfd2e2a56 100644
--- a/cpp/include/qmf/Schema.h
+++ b/cpp/include/qmf/Schema.h
@@ -38,7 +38,7 @@ namespace qmf {
class SchemaProperty;
class SchemaMethod;
- class Schema : public qmf::Handle<SchemaImpl> {
+ class QMF_CLASS_EXTERN Schema : public qmf::Handle<SchemaImpl> {
public:
QMF_EXTERN Schema(SchemaImpl* impl = 0);
QMF_EXTERN Schema(const Schema&);
diff --git a/cpp/include/qmf/SchemaId.h b/cpp/include/qmf/SchemaId.h
index 13fb1cb902..2dafc1c091 100644
--- a/cpp/include/qmf/SchemaId.h
+++ b/cpp/include/qmf/SchemaId.h
@@ -35,7 +35,7 @@ namespace qmf {
class SchemaIdImpl;
- class SchemaId : public qmf::Handle<SchemaIdImpl> {
+ class QMF_CLASS_EXTERN SchemaId : public qmf::Handle<SchemaIdImpl> {
public:
QMF_EXTERN SchemaId(SchemaIdImpl* impl = 0);
QMF_EXTERN SchemaId(const SchemaId&);
diff --git a/cpp/include/qmf/SchemaMethod.h b/cpp/include/qmf/SchemaMethod.h
index 47302b62b9..b5944dc29e 100644
--- a/cpp/include/qmf/SchemaMethod.h
+++ b/cpp/include/qmf/SchemaMethod.h
@@ -36,7 +36,7 @@ namespace qmf {
class SchemaMethodImpl;
class SchemaProperty;
- class SchemaMethod : public qmf::Handle<SchemaMethodImpl> {
+ class QMF_CLASS_EXTERN SchemaMethod : public qmf::Handle<SchemaMethodImpl> {
public:
QMF_EXTERN SchemaMethod(SchemaMethodImpl* impl = 0);
QMF_EXTERN SchemaMethod(const SchemaMethod&);
diff --git a/cpp/include/qmf/SchemaProperty.h b/cpp/include/qmf/SchemaProperty.h
index a3a328b60b..bbb603fa50 100644
--- a/cpp/include/qmf/SchemaProperty.h
+++ b/cpp/include/qmf/SchemaProperty.h
@@ -36,7 +36,7 @@ namespace qmf {
class SchemaPropertyImpl;
- class SchemaProperty : public Handle<SchemaPropertyImpl> {
+ class QMF_CLASS_EXTERN SchemaProperty : public Handle<SchemaPropertyImpl> {
public:
QMF_EXTERN SchemaProperty(SchemaPropertyImpl* impl = 0);
QMF_EXTERN SchemaProperty(const SchemaProperty&);
diff --git a/cpp/include/qmf/Subscription.h b/cpp/include/qmf/Subscription.h
index 4e60eb984e..398a45b922 100644
--- a/cpp/include/qmf/Subscription.h
+++ b/cpp/include/qmf/Subscription.h
@@ -35,7 +35,7 @@ namespace qmf {
class SubscriptionImpl;
class Data;
- class Subscription : public qmf::Handle<SubscriptionImpl> {
+ class QMF_CLASS_EXTERN Subscription : public qmf::Handle<SubscriptionImpl> {
public:
QMF_EXTERN Subscription(SubscriptionImpl* impl = 0);
QMF_EXTERN Subscription(const Subscription&);
@@ -73,7 +73,7 @@ namespace qmf {
#ifndef SWIG
private:
friend class qmf::PrivateImplRef<Subscription>;
- friend class SubscriptionImplAccess;
+ friend struct SubscriptionImplAccess;
#endif
};
diff --git a/cpp/include/qmf/engine/QmfEngineImportExport.h b/cpp/include/qmf/engine/QmfEngineImportExport.h
index 373617e046..cf8fffdb17 100644
--- a/cpp/include/qmf/engine/QmfEngineImportExport.h
+++ b/cpp/include/qmf/engine/QmfEngineImportExport.h
@@ -26,8 +26,17 @@
# else
# define QMFE_EXTERN __declspec(dllimport)
# endif
+# ifdef _MSC_VER
+# define QMFE_CLASS_EXTERN
+# define QMFE_INLINE_EXTERN QMFE_EXTERN
+# else
+# define QMFE_CLASS_EXTERN QMFE_EXTERN
+# define QMFE_INLINE_EXTERN
+# endif
#else
# define QMFE_EXTERN
+# define QMFE_CLASS_EXTERN
+# define QMFE_INLINE_EXTERN
#endif
#endif
diff --git a/cpp/include/qmf/exceptions.h b/cpp/include/qmf/exceptions.h
index 7959499d63..c7ffa68ce2 100644
--- a/cpp/include/qmf/exceptions.h
+++ b/cpp/include/qmf/exceptions.h
@@ -31,24 +31,24 @@ namespace qmf {
/** \ingroup qmf
*/
- struct QmfException : public qpid::types::Exception {
+ struct QMF_CLASS_EXTERN QmfException : public qpid::types::Exception {
QMF_EXTERN QmfException(const std::string& msg);
QMF_EXTERN virtual ~QmfException() throw();
qpid::types::Variant::Map detail;
};
- struct KeyNotFound : public QmfException {
+ struct QMF_CLASS_EXTERN KeyNotFound : public QmfException {
QMF_EXTERN KeyNotFound(const std::string& msg);
QMF_EXTERN virtual ~KeyNotFound() throw();
};
- struct IndexOutOfRange : public QmfException {
+ struct QMF_CLASS_EXTERN IndexOutOfRange : public QmfException {
QMF_EXTERN IndexOutOfRange();
QMF_EXTERN virtual ~IndexOutOfRange() throw();
};
- struct OperationTimedOut : public QmfException {
+ struct QMF_CLASS_EXTERN OperationTimedOut : public QmfException {
QMF_EXTERN OperationTimedOut();
QMF_EXTERN virtual ~OperationTimedOut() throw();
};
diff --git a/cpp/include/qmf/posix/EventNotifier.h b/cpp/include/qmf/posix/EventNotifier.h
new file mode 100644
index 0000000000..ebc1cb5364
--- /dev/null
+++ b/cpp/include/qmf/posix/EventNotifier.h
@@ -0,0 +1,63 @@
+#ifndef __QMF_POSIX_EVENT_NOTIFIER_H
+#define __QMF_POSIX_EVENT_NOTIFIER_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 <qmf/ImportExport.h>
+#include "qmf/Handle.h"
+#include "qmf/AgentSession.h"
+#include "qmf/ConsoleSession.h"
+
+namespace qmf {
+
+ class PosixEventNotifierImpl;
+ class PosixEventNotifierImplAccess;
+
+namespace posix {
+
+#ifndef SWIG
+ template <class> class PrivateImplRef;
+#endif
+
+ class QMF_CLASS_EXTERN EventNotifier : public qmf::Handle<qmf::PosixEventNotifierImpl> {
+ public:
+ QMF_EXTERN EventNotifier(PosixEventNotifierImpl* impl = 0);
+ QMF_EXTERN EventNotifier(::qmf::AgentSession& agentSession);
+ QMF_EXTERN EventNotifier(::qmf::ConsoleSession& consoleSession);
+ QMF_EXTERN EventNotifier(const EventNotifier& that);
+
+ QMF_EXTERN ~EventNotifier();
+
+ QMF_EXTERN EventNotifier& operator=(const EventNotifier& that);
+
+ QMF_EXTERN int getHandle() const;
+
+#ifndef SWIG
+ private:
+ friend class qmf::PrivateImplRef<EventNotifier>;
+ friend struct qmf::PosixEventNotifierImplAccess;
+#endif
+
+ };
+
+}}
+
+#endif
+
diff --git a/cpp/include/qpid/Address.h b/cpp/include/qpid/Address.h
index 57c9139f87..f5b19d0532 100755
--- a/cpp/include/qpid/Address.h
+++ b/cpp/include/qpid/Address.h
@@ -36,7 +36,7 @@ public:
static const std::string TCP; // Default TCP protocol tag.
static const uint16_t AMQP_PORT=5672; // Default AMQP port.
- QPID_COMMON_EXTERN explicit Address(
+ QPID_COMMON_INLINE_EXTERN explicit Address(
const std::string& protocol_=std::string(),
const std::string& host_=std::string(),
uint16_t port_=0
diff --git a/cpp/include/qpid/CommonImportExport.h b/cpp/include/qpid/CommonImportExport.h
index 02c06ed7af..dd2b900b73 100644
--- a/cpp/include/qpid/CommonImportExport.h
+++ b/cpp/include/qpid/CommonImportExport.h
@@ -20,14 +20,16 @@
* under the License.
*/
-#if defined(WIN32) && !defined(QPID_DECLARE_STATIC)
+#include "qpid/ImportExport.h"
+
#if defined(COMMON_EXPORT) || defined (qpidcommon_EXPORTS)
-#define QPID_COMMON_EXTERN __declspec(dllexport)
-#else
-#define QPID_COMMON_EXTERN __declspec(dllimport)
-#endif
+# define QPID_COMMON_EXTERN QPID_EXPORT
+# define QPID_COMMON_CLASS_EXTERN QPID_CLASS_EXPORT
+# define QPID_COMMON_INLINE_EXTERN QPID_INLINE_EXPORT
#else
-#define QPID_COMMON_EXTERN
+# define QPID_COMMON_EXTERN QPID_IMPORT
+# define QPID_COMMON_CLASS_EXTERN QPID_CLASS_IMPORT
+# define QPID_COMMON_INLINE_EXTERN QPID_INLINE_IMPORT
#endif
#endif
diff --git a/cpp/include/qpid/Exception.h b/cpp/include/qpid/Exception.h
index fa7111160c..cbd175214d 100644
--- a/cpp/include/qpid/Exception.h
+++ b/cpp/include/qpid/Exception.h
@@ -36,7 +36,7 @@ namespace qpid
/**
* Base class for Qpid runtime exceptions.
*/
-class Exception : public std::exception
+class QPID_COMMON_CLASS_EXTERN Exception : public std::exception
{
public:
QPID_COMMON_EXTERN explicit Exception(const std::string& message=std::string()) throw();
@@ -51,30 +51,30 @@ class Exception : public std::exception
};
/** Exception that includes an errno message. */
-struct ErrnoException : public Exception {
+struct QPID_COMMON_CLASS_EXTERN ErrnoException : public Exception {
ErrnoException(const std::string& msg, int err) : Exception(msg+": "+qpid::sys::strError(err)) {}
ErrnoException(const std::string& msg) : Exception(msg+": "+qpid::sys::strError(errno)) {}
};
-struct SessionException : public Exception {
+struct QPID_COMMON_CLASS_EXTERN SessionException : public Exception {
const framing::execution::ErrorCode code;
SessionException(framing::execution::ErrorCode code_, const std::string& message)
: Exception(message), code(code_) {}
};
-struct ChannelException : public Exception {
+struct QPID_COMMON_CLASS_EXTERN ChannelException : public Exception {
const framing::session::DetachCode code;
ChannelException(framing::session::DetachCode _code, const std::string& message)
: Exception(message), code(_code) {}
};
-struct ConnectionException : public Exception {
+struct QPID_COMMON_CLASS_EXTERN ConnectionException : public Exception {
const framing::connection::CloseCode code;
ConnectionException(framing::connection::CloseCode _code, const std::string& message)
: Exception(message), code(_code) {}
};
-struct ClosedException : public Exception {
+struct QPID_COMMON_CLASS_EXTERN ClosedException : public Exception {
QPID_COMMON_EXTERN ClosedException(const std::string& msg=std::string());
QPID_COMMON_EXTERN std::string getPrefix() const;
};
diff --git a/cpp/include/qpid/ImportExport.h b/cpp/include/qpid/ImportExport.h
new file mode 100644
index 0000000000..e62399faf7
--- /dev/null
+++ b/cpp/include/qpid/ImportExport.h
@@ -0,0 +1,71 @@
+#ifndef QPID_IMPORTEXPORT_H
+#define QPID_IMPORTEXPORT_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.
+ */
+
+//
+// This header file defines the following macros for the control of library/DLL
+// import and export:
+//
+// QPID_EXPORT - Export declaration for Methods
+// QPID_CLASS_EXPORT - Export declaration for Classes
+// QPID_INLINE_EXPORT - Export declaration for Inline methods
+//
+// QPID_IMPORT - Import declaration for Methods
+// QPID_CLASS_IMPORT - Import declaration for Classes
+// QPID_INLINE_IMPORT - Import declaration for Inline methods
+//
+
+#if defined(WIN32) && !defined(QPID_DECLARE_STATIC)
+ //
+ // Import and Export definitions for Windows:
+ //
+# define QPID_EXPORT __declspec(dllexport)
+# define QPID_IMPORT __declspec(dllimport)
+# ifdef _MSC_VER
+ //
+ // Specific to the Microsoft compiler:
+ //
+# define QPID_CLASS_EXPORT
+# define QPID_CLASS_IMPORT
+# define QPID_INLINE_EXPORT QPID_EXPORT
+# define QPID_INLINE_IMPORT QPID_IMPORT
+# else
+ //
+ // Specific to non-Microsoft compilers (mingw32):
+ //
+# define QPID_CLASS_EXPORT QPID_EXPORT
+# define QPID_CLASS_IMPORT QPID_IMPORT
+# define QPID_INLINE_EXPORT
+# define QPID_INLINE_IMPORT
+# endif
+#else
+ //
+ // Non-Windows (Linux, etc.) definitions:
+ //
+# define QPID_EXPORT
+# define QPID_IMPORT
+# define QPID_CLASS_EXPORT
+# define QPID_CLASS_IMPORT
+# define QPID_INLINE_EXPORT
+# define QPID_INLINE_IMPORT
+#endif
+
+#endif /*!QPID_IMPORTEXPORT_H*/
diff --git a/cpp/include/qpid/Msg.h b/cpp/include/qpid/Msg.h
index e1837c29e5..5f0b11bc60 100644
--- a/cpp/include/qpid/Msg.h
+++ b/cpp/include/qpid/Msg.h
@@ -24,6 +24,7 @@
#include <sstream>
#include <iostream>
+#include "qpid/types/ImportExport.h"
namespace qpid {
diff --git a/cpp/include/qpid/Options.h b/cpp/include/qpid/Options.h
index 078a6b4d95..63d91c2d72 100644
--- a/cpp/include/qpid/Options.h
+++ b/cpp/include/qpid/Options.h
@@ -133,77 +133,6 @@ inline po::value_semantic* optValue(bool& value) { return po::bool_switch(&value
-/*
- * ---------------------------------------------
- * Explanation for Boost 103200 conditional code
- * ---------------------------------------------
- *
- * This boost version has an implementation of the program_options library
- * that has no provision for allowing unregistered options to pass by.
- *
- * But that means that, if you have a program that loads optional modules
- * after start-up, and those modules each have their own set of options,
- * then if you parse the command line too soon, you will get spurious
- * reports of unrecognized options -- and the program will exit!
- *
- * And we must process the command-line before module-loading, because we
- * need to look at the "bootstrap" options.
- *
- * This conditional code:
- *
- * 1. implements it's own functor class, derived from the Boost
- * "options_description_easy_init" class. This functor is used
- * to process added options and do the functor chaining, so that
- * I can snoop on the arguments before doing an explicit call
- * to its parent.
- *
- * 2. It implements two static vectors, one to hold long names, and
- * one for short names, so that options declared by modules are
- * not forgotten when their options_description goes out of scope.
- *
- * I will be thrilled to personally delete this code if we ever decide
- * that qpid doesn't really need to support this antique version of Boost.
- *
- */
-
-#if ( BOOST_VERSION == 103200 )
-struct Options;
-
-
-struct
-options_description_less_easy_init
- : public po::options_description_easy_init
-{
- options_description_less_easy_init ( Options * my_owner,
- po::options_description * my_parents_owner
- )
- : po::options_description_easy_init(my_parents_owner)
- {
- owner = my_owner;
- }
-
-
- options_description_less_easy_init&
- operator()(char const * name,
- char const * description);
-
-
- options_description_less_easy_init&
- operator()(char const * name,
- const po::value_semantic* s);
-
-
- options_description_less_easy_init&
- operator()(const char* name,
- const po::value_semantic* s,
- const char* description);
-
-
- Options * owner;
-};
-#endif
-
-
struct Options : public po::options_description {
struct Exception : public qpid::Exception {
@@ -222,26 +151,9 @@ struct Options : public po::options_description {
bool allowUnknown = false);
- #if ( BOOST_VERSION == 103200 )
- options_description_less_easy_init m_less_easy;
-
- options_description_less_easy_init addOptions() {
- return m_less_easy;
- }
-
- bool
- is_registered_option ( std::string s );
-
- void
- register_names ( std::string s );
-
- static std::vector<std::string> long_names;
- static std::vector<std::string> short_names;
- #else
boost::program_options::options_description_easy_init addOptions() {
return add_options();
}
- #endif
};
diff --git a/cpp/include/qpid/Url.h b/cpp/include/qpid/Url.h
index 353e9d5599..915b08ac5f 100644
--- a/cpp/include/qpid/Url.h
+++ b/cpp/include/qpid/Url.h
@@ -66,7 +66,7 @@ struct Url : public std::vector<Address> {
*@exception Invalid if the url is invalid.
*/
QPID_COMMON_EXTERN void parse(const char* url);
- QPID_COMMON_EXTERN void parse(const std::string& url) { parse(url.c_str()); }
+ QPID_COMMON_INLINE_EXTERN void parse(const std::string& url) { parse(url.c_str()); }
/** Replace contesnts with parsed URL. Replace with empty URL if invalid. */
QPID_COMMON_EXTERN void parseNoThrow(const char* url);
diff --git a/cpp/include/qpid/agent/ManagementAgent.h b/cpp/include/qpid/agent/ManagementAgent.h
index e2451244c1..10bc6527a9 100644
--- a/cpp/include/qpid/agent/ManagementAgent.h
+++ b/cpp/include/qpid/agent/ManagementAgent.h
@@ -110,8 +110,8 @@ class ManagementAgent
uint16_t intervalSeconds = 10,
bool useExternalThread = false,
const std::string& storeFile = "",
- const std::string& uid = "guest",
- const std::string& pwd = "guest",
+ const std::string& uid = "",
+ const std::string& pwd = "",
const std::string& mech = "PLAIN",
const std::string& proto = "tcp") = 0;
diff --git a/cpp/include/qpid/agent/QmfAgentImportExport.h b/cpp/include/qpid/agent/QmfAgentImportExport.h
index e41425a7ba..3f923ac4b2 100644
--- a/cpp/include/qpid/agent/QmfAgentImportExport.h
+++ b/cpp/include/qpid/agent/QmfAgentImportExport.h
@@ -20,14 +20,16 @@
* under the License.
*/
-#if defined(WIN32) && !defined(QPID_DECLARE_STATIC)
-#if defined (qmf_EXPORTS)
-#define QMF_AGENT_EXTERN __declspec(dllexport)
-#else
-#define QMF_AGENT_EXTERN __declspec(dllimport)
-#endif
+#include "qpid/ImportExport.h"
+
+#if defined(QMF_EXPORT) || defined (qmf_EXPORTS)
+# define QMF_AGENT_EXTERN QPID_EXPORT
+# define QMF_AGENT_CLASS_EXTERN QPID_CLASS_EXPORT
+# define QMF_AGENT_INLINE_EXTERN QPID_INLINE_EXPORT
#else
-#define QMF_AGENT_EXTERN
+# define QMF_AGENT_EXTERN QPID_IMPORT
+# define QMF_AGENT_CLASS_EXTERN QPID_CLASS_IMPORT
+# define QMF_AGENT_INLINE_EXTERN QPID_INLINE_IMPORT
#endif
#endif
diff --git a/cpp/include/qpid/amqp_0_10/Codecs.h b/cpp/include/qpid/amqp_0_10/Codecs.h
index 08275402fc..73846f33a8 100644
--- a/cpp/include/qpid/amqp_0_10/Codecs.h
+++ b/cpp/include/qpid/amqp_0_10/Codecs.h
@@ -34,14 +34,14 @@ namespace amqp_0_10 {
* Codec for encoding/decoding a map of Variants using the AMQP 0-10
* map encoding.
*/
-class QPID_COMMON_EXTERN MapCodec
+class QPID_COMMON_CLASS_EXTERN MapCodec
{
public:
typedef qpid::types::Variant::Map ObjectType;
- static void encode(const ObjectType&, std::string&);
- static void decode(const std::string&, ObjectType&);
- static size_t encodedSize(const ObjectType&);
- static const std::string contentType;
+ static void QPID_COMMON_EXTERN encode(const ObjectType&, std::string&);
+ static void QPID_COMMON_EXTERN decode(const std::string&, ObjectType&);
+ static size_t QPID_COMMON_EXTERN encodedSize(const ObjectType&);
+ static const QPID_COMMON_EXTERN std::string contentType;
private:
};
@@ -49,14 +49,14 @@ class QPID_COMMON_EXTERN MapCodec
* Codec for encoding/decoding a list of Variants using the AMQP 0-10
* list encoding.
*/
-class QPID_COMMON_EXTERN ListCodec
+class QPID_COMMON_CLASS_EXTERN ListCodec
{
public:
typedef qpid::types::Variant::List ObjectType;
- static void encode(const ObjectType&, std::string&);
- static void decode(const std::string&, ObjectType&);
- static size_t encodedSize(const ObjectType&);
- static const std::string contentType;
+ static void QPID_COMMON_EXTERN encode(const ObjectType&, std::string&);
+ static void QPID_COMMON_EXTERN decode(const std::string&, ObjectType&);
+ static size_t QPID_COMMON_EXTERN encodedSize(const ObjectType&);
+ static const QPID_COMMON_EXTERN std::string contentType;
private:
};
diff --git a/cpp/include/qpid/client/ClientImportExport.h b/cpp/include/qpid/client/ClientImportExport.h
index 42b02e33c3..2a3a5a52e9 100644
--- a/cpp/include/qpid/client/ClientImportExport.h
+++ b/cpp/include/qpid/client/ClientImportExport.h
@@ -20,14 +20,16 @@
* under the License.
*/
-#if defined(WIN32) && !defined(QPID_DECLARE_STATIC)
+#include "qpid/ImportExport.h"
+
#if defined(CLIENT_EXPORT) || defined (qpidclient_EXPORTS)
-#define QPID_CLIENT_EXTERN __declspec(dllexport)
-#else
-#define QPID_CLIENT_EXTERN __declspec(dllimport)
-#endif
+# define QPID_CLIENT_EXTERN QPID_EXPORT
+# define QPID_CLIENT_CLASS_EXTERN QPID_CLASS_EXPORT
+# define QPID_CLIENT_INLINE_EXTERN QPID_INLINE_EXPORT
#else
-#define QPID_CLIENT_EXTERN
+# define QPID_CLIENT_EXTERN QPID_IMPORT
+# define QPID_CLIENT_CLASS_EXTERN QPID_CLASS_IMPORT
+# define QPID_CLIENT_INLINE_EXTERN QPID_INLINE_IMPORT
#endif
#endif
diff --git a/cpp/include/qpid/client/Completion.h b/cpp/include/qpid/client/Completion.h
index 99d940f031..9546db9258 100644
--- a/cpp/include/qpid/client/Completion.h
+++ b/cpp/include/qpid/client/Completion.h
@@ -41,7 +41,7 @@ template <class T> class PrivateImplRef;
*
*\ingroup clientapi
*/
-class Completion : public Handle<CompletionImpl>
+class QPID_CLIENT_CLASS_EXTERN Completion : public Handle<CompletionImpl>
{
public:
QPID_CLIENT_EXTERN Completion(CompletionImpl* = 0);
diff --git a/cpp/include/qpid/client/Connection.h b/cpp/include/qpid/client/Connection.h
index 6ed0d98bc0..c0db0f301d 100644
--- a/cpp/include/qpid/client/Connection.h
+++ b/cpp/include/qpid/client/Connection.h
@@ -60,7 +60,7 @@ class ConnectionImpl;
*
*/
-class Connection
+class QPID_CLIENT_CLASS_EXTERN Connection
{
framing::ProtocolVersion version;
@@ -102,8 +102,8 @@ class Connection
* within a single broker).
*/
QPID_CLIENT_EXTERN void open(const std::string& host, int port = 5672,
- const std::string& uid = "guest",
- const std::string& pwd = "guest",
+ const std::string& uid = "",
+ const std::string& pwd = "",
const std::string& virtualhost = "/", uint16_t maxFrameSize=65535);
/**
@@ -124,8 +124,8 @@ class Connection
* within a single broker).
*/
QPID_CLIENT_EXTERN void open(const Url& url,
- const std::string& uid = "guest",
- const std::string& pwd = "guest",
+ const std::string& uid = "",
+ const std::string& pwd = "",
const std::string& virtualhost = "/", uint16_t maxFrameSize=65535);
/**
diff --git a/cpp/include/qpid/client/ConnectionSettings.h b/cpp/include/qpid/client/ConnectionSettings.h
index 1c2ee28b1b..2b6b86f891 100644
--- a/cpp/include/qpid/client/ConnectionSettings.h
+++ b/cpp/include/qpid/client/ConnectionSettings.h
@@ -37,7 +37,7 @@ namespace client {
/**
* Settings for a Connection.
*/
-struct ConnectionSettings {
+struct QPID_CLIENT_CLASS_EXTERN ConnectionSettings {
QPID_CLIENT_EXTERN ConnectionSettings();
QPID_CLIENT_EXTERN virtual ~ConnectionSettings();
diff --git a/cpp/include/qpid/client/FailoverListener.h b/cpp/include/qpid/client/FailoverListener.h
index 59108eb7cb..53c7c26211 100644
--- a/cpp/include/qpid/client/FailoverListener.h
+++ b/cpp/include/qpid/client/FailoverListener.h
@@ -48,7 +48,7 @@ namespace client {
* FailoverListener::decode to extract a list of broker URLs from a
* failover exchange message.
*/
-class FailoverListener : private MessageListener, private qpid::sys::Runnable
+class QPID_CLIENT_CLASS_EXTERN FailoverListener : private MessageListener, private qpid::sys::Runnable
{
public:
/** The name of the standard failover exchange amq.failover */
diff --git a/cpp/include/qpid/client/FailoverManager.h b/cpp/include/qpid/client/FailoverManager.h
index 0d30e2ed60..d3a0dbc976 100644
--- a/cpp/include/qpid/client/FailoverManager.h
+++ b/cpp/include/qpid/client/FailoverManager.h
@@ -42,7 +42,7 @@ struct CannotConnectException : qpid::Exception
/**
* Utility to manage failover.
*/
-class FailoverManager
+class QPID_CLIENT_CLASS_EXTERN FailoverManager
{
public:
/**
diff --git a/cpp/include/qpid/client/Future.h b/cpp/include/qpid/client/Future.h
index 09088e68f6..630a7e03c0 100644
--- a/cpp/include/qpid/client/Future.h
+++ b/cpp/include/qpid/client/Future.h
@@ -34,7 +34,7 @@ namespace qpid {
namespace client {
/**@internal */
-class Future
+class QPID_CLIENT_CLASS_EXTERN Future
{
framing::SequenceNumber command;
boost::shared_ptr<FutureResult> result;
diff --git a/cpp/include/qpid/client/FutureResult.h b/cpp/include/qpid/client/FutureResult.h
index b2b663daa1..ead4929571 100644
--- a/cpp/include/qpid/client/FutureResult.h
+++ b/cpp/include/qpid/client/FutureResult.h
@@ -34,7 +34,7 @@ namespace client {
class SessionImpl;
///@internal
-class FutureResult : public FutureCompletion
+class QPID_CLIENT_CLASS_EXTERN FutureResult : public FutureCompletion
{
std::string result;
public:
diff --git a/cpp/include/qpid/client/Handle.h b/cpp/include/qpid/client/Handle.h
index 088e836fcf..b8315481a9 100644
--- a/cpp/include/qpid/client/Handle.h
+++ b/cpp/include/qpid/client/Handle.h
@@ -40,22 +40,22 @@ template <class T> class Handle {
public:
/**@return true if handle is valid, i.e. not null. */
- QPID_CLIENT_EXTERN bool isValid() const { return impl; }
+ QPID_CLIENT_INLINE_EXTERN bool isValid() const { return impl; }
/**@return true if handle is null. It is an error to call any function on a null handle. */
- QPID_CLIENT_EXTERN bool isNull() const { return !impl; }
+ QPID_CLIENT_INLINE_EXTERN bool isNull() const { return !impl; }
/** Conversion to bool supports idiom if (handle) { handle->... } */
- QPID_CLIENT_EXTERN operator bool() const { return impl; }
+ QPID_CLIENT_INLINE_EXTERN operator bool() const { return impl; }
/** Operator ! supports idiom if (!handle) { do_if_handle_is_null(); } */
- QPID_CLIENT_EXTERN bool operator !() const { return !impl; }
+ QPID_CLIENT_INLINE_EXTERN bool operator !() const { return !impl; }
void swap(Handle<T>& h) { T* t = h.impl; h.impl = impl; impl = t; }
protected:
typedef T Impl;
- QPID_CLIENT_EXTERN Handle() :impl() {}
+ QPID_CLIENT_INLINE_EXTERN Handle() :impl() {}
// Not implemented,subclasses must implement.
QPID_CLIENT_EXTERN Handle(const Handle&);
diff --git a/cpp/include/qpid/client/LocalQueue.h b/cpp/include/qpid/client/LocalQueue.h
index 70e4cebcf1..1a19a8499d 100644
--- a/cpp/include/qpid/client/LocalQueue.h
+++ b/cpp/include/qpid/client/LocalQueue.h
@@ -71,7 +71,7 @@ template <class T> class PrivateImplRef;
* </ul>
*/
-class LocalQueue : public Handle<LocalQueueImpl> {
+class QPID_CLIENT_CLASS_EXTERN LocalQueue : public Handle<LocalQueueImpl> {
public:
/** Create a local queue. Subscribe the local queue to a remote broker
* queue with a SubscriptionManager.
diff --git a/cpp/include/qpid/client/Message.h b/cpp/include/qpid/client/Message.h
index 2401cbdc92..ba50dda9ba 100644
--- a/cpp/include/qpid/client/Message.h
+++ b/cpp/include/qpid/client/Message.h
@@ -112,7 +112,7 @@ class MessageImpl;
*
*
*/
-class Message
+class QPID_CLIENT_CLASS_EXTERN Message
{
public:
/** Create a Message.
diff --git a/cpp/include/qpid/client/MessageListener.h b/cpp/include/qpid/client/MessageListener.h
index d200f8cf21..3ca2fa964a 100644
--- a/cpp/include/qpid/client/MessageListener.h
+++ b/cpp/include/qpid/client/MessageListener.h
@@ -84,7 +84,7 @@ namespace client {
*
*/
- class MessageListener{
+ class QPID_CLIENT_CLASS_EXTERN MessageListener{
public:
QPID_CLIENT_EXTERN virtual ~MessageListener();
diff --git a/cpp/include/qpid/client/MessageReplayTracker.h b/cpp/include/qpid/client/MessageReplayTracker.h
index 6f5a0f4ac3..06a3f29c7d 100644
--- a/cpp/include/qpid/client/MessageReplayTracker.h
+++ b/cpp/include/qpid/client/MessageReplayTracker.h
@@ -34,7 +34,7 @@ namespace client {
* Utility to track messages sent asynchronously, allowing those that
* are indoubt to be replayed over a new session.
*/
-class MessageReplayTracker
+class QPID_CLIENT_CLASS_EXTERN MessageReplayTracker
{
public:
QPID_CLIENT_EXTERN MessageReplayTracker(uint flushInterval);
diff --git a/cpp/include/qpid/client/QueueOptions.h b/cpp/include/qpid/client/QueueOptions.h
index f8a4963f06..3984b63fdd 100644
--- a/cpp/include/qpid/client/QueueOptions.h
+++ b/cpp/include/qpid/client/QueueOptions.h
@@ -35,7 +35,7 @@ enum QueueOrderingPolicy {FIFO, LVQ, LVQ_NO_BROWSE};
* A help class to set options on the Queue. Create a configured args while
* still allowing any custom configuration via the FieldTable base class
*/
-class QueueOptions: public framing::FieldTable
+class QPID_CLIENT_CLASS_EXTERN QueueOptions: public framing::FieldTable
{
public:
QPID_CLIENT_EXTERN QueueOptions();
diff --git a/cpp/include/qpid/client/SessionBase_0_10.h b/cpp/include/qpid/client/SessionBase_0_10.h
index 3b5c84e74b..ea50ab32f7 100644
--- a/cpp/include/qpid/client/SessionBase_0_10.h
+++ b/cpp/include/qpid/client/SessionBase_0_10.h
@@ -54,7 +54,7 @@ enum CreditUnit { MESSAGE_CREDIT=0, BYTE_CREDIT=1, UNLIMITED_CREDIT=0xFFFFFFFF }
* Subclasses provide the AMQP commands for a given
* version of the protocol.
*/
-class SessionBase_0_10 {
+class QPID_CLIENT_CLASS_EXTERN SessionBase_0_10 {
public:
///@internal
diff --git a/cpp/include/qpid/client/Subscription.h b/cpp/include/qpid/client/Subscription.h
index 425b6b92e2..bb9b98e8ff 100644
--- a/cpp/include/qpid/client/Subscription.h
+++ b/cpp/include/qpid/client/Subscription.h
@@ -39,7 +39,7 @@ class SubscriptionManager;
* A handle to an active subscription. Provides methods to query the subscription status
* and control acknowledgement (acquire and accept) of messages.
*/
-class Subscription : public Handle<SubscriptionImpl> {
+class QPID_CLIENT_CLASS_EXTERN Subscription : public Handle<SubscriptionImpl> {
public:
QPID_CLIENT_EXTERN Subscription(SubscriptionImpl* = 0);
QPID_CLIENT_EXTERN Subscription(const Subscription&);
@@ -91,13 +91,13 @@ class Subscription : public Handle<SubscriptionImpl> {
QPID_CLIENT_EXTERN void release(const SequenceSet& messageIds);
/* Acquire a single message */
- QPID_CLIENT_EXTERN void acquire(const Message& m) { acquire(SequenceSet(m.getId())); }
+ QPID_CLIENT_INLINE_EXTERN void acquire(const Message& m) { acquire(SequenceSet(m.getId())); }
/* Accept a single message */
- QPID_CLIENT_EXTERN void accept(const Message& m) { accept(SequenceSet(m.getId())); }
+ QPID_CLIENT_INLINE_EXTERN void accept(const Message& m) { accept(SequenceSet(m.getId())); }
/* Release a single message */
- QPID_CLIENT_EXTERN void release(const Message& m) { release(SequenceSet(m.getId())); }
+ QPID_CLIENT_INLINE_EXTERN void release(const Message& m) { release(SequenceSet(m.getId())); }
/** Get the session associated with this subscription */
QPID_CLIENT_EXTERN Session getSession() const;
diff --git a/cpp/include/qpid/client/SubscriptionManager.h b/cpp/include/qpid/client/SubscriptionManager.h
index e70e05f73a..b69819a8ff 100644
--- a/cpp/include/qpid/client/SubscriptionManager.h
+++ b/cpp/include/qpid/client/SubscriptionManager.h
@@ -94,7 +94,7 @@ class SubscriptionManagerImpl;
* </ul>
*
*/
-class SubscriptionManager : public sys::Runnable, public Handle<SubscriptionManagerImpl>
+class QPID_CLIENT_CLASS_EXTERN SubscriptionManager : public sys::Runnable, public Handle<SubscriptionManagerImpl>
{
public:
/** Create a new SubscriptionManager associated with a session */
diff --git a/cpp/include/qpid/console/Agent.h b/cpp/include/qpid/console/Agent.h
index 97d75da250..629dd71dee 100644
--- a/cpp/include/qpid/console/Agent.h
+++ b/cpp/include/qpid/console/Agent.h
@@ -31,17 +31,17 @@ namespace console {
*
* \ingroup qmfconsoleapi
*/
- class QPID_CONSOLE_EXTERN Agent {
+ class QPID_CONSOLE_CLASS_EXTERN Agent {
public:
typedef std::vector<Agent*> Vector;
- Agent(Broker* _broker, uint32_t _bank, const std::string& _label) :
+ QPID_CONSOLE_INLINE_EXTERN Agent(Broker* _broker, uint32_t _bank, const std::string& _label) :
broker(_broker), brokerBank(broker->getBrokerBank()),
agentBank(_bank), label(_label) {}
- Broker* getBroker() const { return broker; }
- uint32_t getBrokerBank() const { return brokerBank; }
- uint32_t getAgentBank() const { return agentBank; }
- const std::string& getLabel() const { return label; }
+ QPID_CONSOLE_INLINE_EXTERN Broker* getBroker() const { return broker; }
+ QPID_CONSOLE_INLINE_EXTERN uint32_t getBrokerBank() const { return brokerBank; }
+ QPID_CONSOLE_INLINE_EXTERN uint32_t getAgentBank() const { return agentBank; }
+ QPID_CONSOLE_INLINE_EXTERN const std::string& getLabel() const { return label; }
private:
Broker* broker;
@@ -50,7 +50,7 @@ namespace console {
const std::string label;
};
- QPID_CONSOLE_EXTERN std::ostream& operator<<(std::ostream& o, const Agent& agent);
+ std::ostream& operator<<(std::ostream& o, const Agent& agent);
}
}
diff --git a/cpp/include/qpid/console/Broker.h b/cpp/include/qpid/console/Broker.h
index 0b2d1bcb61..c2ba8ac81f 100644
--- a/cpp/include/qpid/console/Broker.h
+++ b/cpp/include/qpid/console/Broker.h
@@ -55,12 +55,12 @@ namespace console {
client::ConnectionSettings& settings);
QPID_CONSOLE_EXTERN ~Broker();
- QPID_CONSOLE_EXTERN bool isConnected() const { return connected; }
- QPID_CONSOLE_EXTERN const std::string& getError() const { return error; }
- QPID_CONSOLE_EXTERN const std::string& getSessionId() const { return amqpSessionId; }
- QPID_CONSOLE_EXTERN const framing::Uuid& getBrokerId() const { return brokerId; }
- QPID_CONSOLE_EXTERN uint32_t getBrokerBank() const { return 1; }
- QPID_CONSOLE_EXTERN void addBinding(const std::string& key) {
+ QPID_CONSOLE_INLINE_EXTERN bool isConnected() const { return connected; }
+ QPID_CONSOLE_INLINE_EXTERN const std::string& getError() const { return error; }
+ QPID_CONSOLE_INLINE_EXTERN const std::string& getSessionId() const { return amqpSessionId; }
+ QPID_CONSOLE_INLINE_EXTERN const framing::Uuid& getBrokerId() const { return brokerId; }
+ QPID_CONSOLE_INLINE_EXTERN uint32_t getBrokerBank() const { return 1; }
+ QPID_CONSOLE_INLINE_EXTERN void addBinding(const std::string& key) {
connThreadBody.bindExchange("qpid.management", key);
}
QPID_CONSOLE_EXTERN std::string getUrl() const;
@@ -123,10 +123,10 @@ namespace console {
void setBrokerId(const framing::Uuid& id) { brokerId = id; }
void appendAgents(std::vector<Agent*>& agents) const;
- friend QPID_CONSOLE_EXTERN std::ostream& operator<<(std::ostream& o, const Broker& k);
+ friend std::ostream& operator<<(std::ostream& o, const Broker& k);
};
- QPID_CONSOLE_EXTERN std::ostream& operator<<(std::ostream& o, const Broker& k);
+ std::ostream& operator<<(std::ostream& o, const Broker& k);
}
}
diff --git a/cpp/include/qpid/console/ClassKey.h b/cpp/include/qpid/console/ClassKey.h
index 95cd2627f1..5f7c50351a 100644
--- a/cpp/include/qpid/console/ClassKey.h
+++ b/cpp/include/qpid/console/ClassKey.h
@@ -33,24 +33,24 @@ namespace console {
*
* \ingroup qmfconsoleapi
*/
- class QPID_CONSOLE_EXTERN ClassKey {
+ class QPID_CONSOLE_CLASS_EXTERN ClassKey {
public:
- static const int HASH_SIZE = 16;
+ QPID_CONSOLE_EXTERN static const int HASH_SIZE = 16;
- ClassKey(const std::string& package, const std::string& name, const uint8_t* hash);
+ QPID_CONSOLE_EXTERN ClassKey(const std::string& package, const std::string& name, const uint8_t* hash);
- const std::string& getPackageName() const { return package; }
- const std::string& getClassName() const { return name; }
- const uint8_t* getHash() const { return hash; }
- std::string getHashString() const;
- std::string str() const;
- bool operator==(const ClassKey& other) const;
- bool operator!=(const ClassKey& other) const;
- bool operator<(const ClassKey& other) const;
- bool operator>(const ClassKey& other) const;
- bool operator<=(const ClassKey& other) const;
- bool operator>=(const ClassKey& other) const;
- void encode(framing::Buffer& buffer) const;
+ const QPID_CONSOLE_EXTERN std::string& getPackageName() const { return package; }
+ const QPID_CONSOLE_EXTERN std::string& getClassName() const { return name; }
+ const QPID_CONSOLE_EXTERN uint8_t* getHash() const { return hash; }
+ QPID_CONSOLE_EXTERN std::string getHashString() const;
+ QPID_CONSOLE_EXTERN std::string str() const;
+ QPID_CONSOLE_EXTERN bool operator==(const ClassKey& other) const;
+ QPID_CONSOLE_EXTERN bool operator!=(const ClassKey& other) const;
+ QPID_CONSOLE_EXTERN bool operator<(const ClassKey& other) const;
+ QPID_CONSOLE_EXTERN bool operator>(const ClassKey& other) const;
+ QPID_CONSOLE_EXTERN bool operator<=(const ClassKey& other) const;
+ QPID_CONSOLE_EXTERN bool operator>=(const ClassKey& other) const;
+ QPID_CONSOLE_EXTERN void encode(framing::Buffer& buffer) const;
private:
std::string package;
diff --git a/cpp/include/qpid/console/ConsoleImportExport.h b/cpp/include/qpid/console/ConsoleImportExport.h
index c2d7cb3a14..aac30858f7 100644
--- a/cpp/include/qpid/console/ConsoleImportExport.h
+++ b/cpp/include/qpid/console/ConsoleImportExport.h
@@ -20,14 +20,16 @@
* under the License.
*/
-#if defined(WIN32) && !defined(QPID_DECLARE_STATIC)
+#include "qpid/ImportExport.h"
+
#if defined(CONSOLE_EXPORT) || defined (qmfconsole_EXPORTS)
-#define QPID_CONSOLE_EXTERN __declspec(dllexport)
-#else
-#define QPID_CONSOLE_EXTERN __declspec(dllimport)
-#endif
+# define QPID_CONSOLE_EXTERN QPID_EXPORT
+# define QPID_CONSOLE_CLASS_EXTERN QPID_CLASS_EXPORT
+# define QPID_CONSOLE_INLINE_EXTERN QPID_INLINE_EXPORT
#else
-#define QPID_CONSOLE_EXTERN
+# define QPID_CONSOLE_EXTERN QPID_IMPORT
+# define QPID_CONSOLE_CLASS_EXTERN QPID_CLASS_IMPORT
+# define QPID_CONSOLE_INLINE_EXTERN QPID_INLINE_IMPORT
#endif
#endif
diff --git a/cpp/include/qpid/console/ObjectId.h b/cpp/include/qpid/console/ObjectId.h
index 7904c85598..0722eaebeb 100644
--- a/cpp/include/qpid/console/ObjectId.h
+++ b/cpp/include/qpid/console/ObjectId.h
@@ -40,10 +40,10 @@ namespace console {
ObjectId() : first(0), second(0) {}
ObjectId(framing::Buffer& buffer);
- uint8_t getFlags() const { return (first & 0xF000000000000000LL) >> 60; }
- uint16_t getSequence() const { return (first & 0x0FFF000000000000LL) >> 48; }
- uint32_t getBrokerBank() const { return (first & 0x0000FFFFF0000000LL) >> 28; }
- uint32_t getAgentBank() const { return first & 0x000000000FFFFFFFLL; }
+ uint8_t getFlags() const { return (uint8_t)((first & 0xF000000000000000LL) >> 60); }
+ uint16_t getSequence() const { return (uint16_t)((first & 0x0FFF000000000000LL) >> 48); }
+ uint32_t getBrokerBank() const { return (uint32_t)((first & 0x0000FFFFF0000000LL) >> 28); }
+ uint32_t getAgentBank() const { return (uint32_t) (first & 0x000000000FFFFFFFLL); }
uint64_t getObject() const { return second; }
bool isDurable() const { return getSequence() == 0; }
void decode(framing::Buffer& buffer);
diff --git a/cpp/include/qpid/framing/Array.h b/cpp/include/qpid/framing/Array.h
index d3bdd36aa6..1e97be3bb4 100644
--- a/cpp/include/qpid/framing/Array.h
+++ b/cpp/include/qpid/framing/Array.h
@@ -34,7 +34,7 @@ namespace framing {
class Buffer;
-class Array
+class QPID_COMMON_CLASS_EXTERN Array
{
public:
typedef boost::shared_ptr<FieldValue> ValuePtr;
@@ -55,25 +55,25 @@ class Array
//creates a longstr array
QPID_COMMON_EXTERN Array(const std::vector<std::string>& in);
- QPID_COMMON_EXTERN TypeCode getType() const { return type; }
+ QPID_COMMON_INLINE_EXTERN TypeCode getType() const { return type; }
// std collection interface.
- QPID_COMMON_EXTERN const_iterator begin() const { return values.begin(); }
- QPID_COMMON_EXTERN const_iterator end() const { return values.end(); }
- QPID_COMMON_EXTERN iterator begin() { return values.begin(); }
- QPID_COMMON_EXTERN iterator end(){ return values.end(); }
+ QPID_COMMON_INLINE_EXTERN const_iterator begin() const { return values.begin(); }
+ QPID_COMMON_INLINE_EXTERN const_iterator end() const { return values.end(); }
+ QPID_COMMON_INLINE_EXTERN iterator begin() { return values.begin(); }
+ QPID_COMMON_INLINE_EXTERN iterator end(){ return values.end(); }
- QPID_COMMON_EXTERN ValuePtr front() const { return values.front(); }
- QPID_COMMON_EXTERN ValuePtr back() const { return values.back(); }
- QPID_COMMON_EXTERN size_t size() const { return values.size(); }
+ QPID_COMMON_INLINE_EXTERN ValuePtr front() const { return values.front(); }
+ QPID_COMMON_INLINE_EXTERN ValuePtr back() const { return values.back(); }
+ QPID_COMMON_INLINE_EXTERN size_t size() const { return values.size(); }
QPID_COMMON_EXTERN void insert(iterator i, ValuePtr value);
- QPID_COMMON_EXTERN void erase(iterator i) { values.erase(i); }
- QPID_COMMON_EXTERN void push_back(ValuePtr value) { values.insert(end(), value); }
- QPID_COMMON_EXTERN void pop_back() { values.pop_back(); }
+ QPID_COMMON_INLINE_EXTERN void erase(iterator i) { values.erase(i); }
+ QPID_COMMON_INLINE_EXTERN void push_back(ValuePtr value) { values.insert(end(), value); }
+ QPID_COMMON_INLINE_EXTERN void pop_back() { values.pop_back(); }
// Non-std interface
- QPID_COMMON_EXTERN void add(ValuePtr value) { push_back(value); }
+ QPID_COMMON_INLINE_EXTERN void add(ValuePtr value) { push_back(value); }
template <class T>
void collect(std::vector<T>& out) const
diff --git a/cpp/include/qpid/framing/Buffer.h b/cpp/include/qpid/framing/Buffer.h
index 04583433c5..8b08e60762 100644
--- a/cpp/include/qpid/framing/Buffer.h
+++ b/cpp/include/qpid/framing/Buffer.h
@@ -29,14 +29,14 @@
namespace qpid {
namespace framing {
-struct OutOfBounds : qpid::Exception {
+struct QPID_COMMON_CLASS_EXTERN OutOfBounds : qpid::Exception {
OutOfBounds() : qpid::Exception(std::string("Out of Bounds")) {}
};
class Content;
class FieldTable;
-class Buffer
+class QPID_COMMON_CLASS_EXTERN Buffer
{
uint32_t size;
char* data;
@@ -72,12 +72,12 @@ class Buffer
QPID_COMMON_EXTERN void restore(bool reRecord = false);
QPID_COMMON_EXTERN void reset();
- QPID_COMMON_EXTERN uint32_t available() { return size - position; }
- QPID_COMMON_EXTERN uint32_t getSize() { return size; }
- QPID_COMMON_EXTERN uint32_t getPosition() { return position; }
- QPID_COMMON_EXTERN void setPosition(uint32_t p) { position = p; }
- QPID_COMMON_EXTERN Iterator getIterator() { return Iterator(*this); }
- QPID_COMMON_EXTERN char* getPointer() { return data; }
+ 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);
QPID_COMMON_EXTERN void putShort(uint16_t i);
diff --git a/cpp/include/qpid/framing/FieldTable.h b/cpp/include/qpid/framing/FieldTable.h
index fdb1a28b9d..bdcef6d7fd 100644
--- a/cpp/include/qpid/framing/FieldTable.h
+++ b/cpp/include/qpid/framing/FieldTable.h
@@ -56,7 +56,7 @@ class FieldTable
typedef ValueMap::reference reference;
typedef ValueMap::value_type value_type;
- QPID_COMMON_EXTERN FieldTable() {};
+ QPID_COMMON_INLINE_EXTERN FieldTable() {};
QPID_COMMON_EXTERN FieldTable(const FieldTable& ft);
QPID_COMMON_EXTERN ~FieldTable();
QPID_COMMON_EXTERN FieldTable& operator=(const FieldTable& ft);
@@ -65,9 +65,11 @@ class FieldTable
QPID_COMMON_EXTERN void decode(Buffer& buffer);
QPID_COMMON_EXTERN int count() const;
+ QPID_COMMON_INLINE_EXTERN size_t size() const { return values.size(); }
+ QPID_COMMON_INLINE_EXTERN bool empty() { return size() == 0; }
QPID_COMMON_EXTERN void set(const std::string& name, const ValuePtr& value);
QPID_COMMON_EXTERN ValuePtr get(const std::string& name) const;
- QPID_COMMON_EXTERN bool isSet(const std::string& name) const { return get(name).get() != 0; }
+ QPID_COMMON_INLINE_EXTERN bool isSet(const std::string& name) const { return get(name).get() != 0; }
QPID_COMMON_EXTERN void setString(const std::string& name, const std::string& value);
QPID_COMMON_EXTERN void setInt(const std::string& name, const int value);
diff --git a/cpp/include/qpid/framing/FieldValue.h b/cpp/include/qpid/framing/FieldValue.h
index 19220e74d5..458de62fdf 100644
--- a/cpp/include/qpid/framing/FieldValue.h
+++ b/cpp/include/qpid/framing/FieldValue.h
@@ -41,14 +41,14 @@ namespace framing {
*
* \ingroup clientapi
*/
-class FieldValueException : public qpid::Exception {};
+class QPID_COMMON_CLASS_EXTERN FieldValueException : public qpid::Exception {};
/**
* Exception thrown when we can't perform requested conversion
*
* \ingroup clientapi
*/
-struct InvalidConversionException : public FieldValueException {
+struct QPID_COMMON_CLASS_EXTERN InvalidConversionException : public FieldValueException {
InvalidConversionException() {}
};
@@ -59,7 +59,7 @@ class List;
*
* \ingroup clientapi
*/
-class FieldValue {
+class QPID_COMMON_CLASS_EXTERN FieldValue {
public:
/*
* Abstract type for content of different types
@@ -90,7 +90,7 @@ class FieldValue {
void encode(Buffer& buffer);
void decode(Buffer& buffer);
QPID_COMMON_EXTERN bool operator==(const FieldValue&) const;
- QPID_COMMON_EXTERN bool operator!=(const FieldValue& v) const { return !(*this == v); }
+ QPID_COMMON_INLINE_EXTERN bool operator!=(const FieldValue& v) const { return !(*this == v); }
QPID_COMMON_EXTERN void print(std::ostream& out) const;
@@ -98,6 +98,7 @@ class FieldValue {
template <typename T> T get() const { throw InvalidConversionException(); }
template <class T, int W> T getIntegerValue() const;
+ template <class T> T getIntegerValue() const;
template <class T, int W> T getFloatingPointValue() const;
template <int W> void getFixedWidthValue(unsigned char*) const;
template <class T> bool get(T&) const;
@@ -196,6 +197,18 @@ inline T FieldValue::getIntegerValue() const
}
}
+template <class T>
+inline T FieldValue::getIntegerValue() const
+{
+ FixedWidthValue<1>* const fwv = dynamic_cast< FixedWidthValue<1>* const>(data.get());
+ if (fwv) {
+ uint8_t* octets = fwv->rawOctets();
+ return octets[0];
+ } else {
+ throw InvalidConversionException();
+ }
+}
+
template <class T, int W>
inline T FieldValue::getFloatingPointValue() const {
FixedWidthValue<W>* const fwv = dynamic_cast< FixedWidthValue<W>* const>(data.get());
diff --git a/cpp/include/qpid/framing/List.h b/cpp/include/qpid/framing/List.h
index 0f17c7884c..681445947c 100644
--- a/cpp/include/qpid/framing/List.h
+++ b/cpp/include/qpid/framing/List.h
@@ -36,10 +36,11 @@ class FieldValue;
/**
* Representation of an AMQP 0-10 list
*/
-class List
+class QPID_COMMON_CLASS_EXTERN List
{
public:
typedef boost::shared_ptr<FieldValue> ValuePtr;
+ typedef ValuePtr value_type;
typedef std::list<ValuePtr> Values;
typedef Values::const_iterator const_iterator;
typedef Values::iterator iterator;
@@ -53,19 +54,19 @@ class List
QPID_COMMON_EXTERN bool operator==(const List& other) const;
// std collection interface.
- QPID_COMMON_EXTERN const_iterator begin() const { return values.begin(); }
- QPID_COMMON_EXTERN const_iterator end() const { return values.end(); }
- QPID_COMMON_EXTERN iterator begin() { return values.begin(); }
- QPID_COMMON_EXTERN iterator end(){ return values.end(); }
+ QPID_COMMON_INLINE_EXTERN const_iterator begin() const { return values.begin(); }
+ QPID_COMMON_INLINE_EXTERN const_iterator end() const { return values.end(); }
+ QPID_COMMON_INLINE_EXTERN iterator begin() { return values.begin(); }
+ QPID_COMMON_INLINE_EXTERN iterator end(){ return values.end(); }
- QPID_COMMON_EXTERN ValuePtr front() const { return values.front(); }
- QPID_COMMON_EXTERN ValuePtr back() const { return values.back(); }
- QPID_COMMON_EXTERN size_t size() const { return values.size(); }
+ QPID_COMMON_INLINE_EXTERN ValuePtr front() const { return values.front(); }
+ QPID_COMMON_INLINE_EXTERN ValuePtr back() const { return values.back(); }
+ QPID_COMMON_INLINE_EXTERN size_t size() const { return values.size(); }
- QPID_COMMON_EXTERN iterator insert(iterator i, ValuePtr value) { return values.insert(i, value); }
- QPID_COMMON_EXTERN void erase(iterator i) { values.erase(i); }
- QPID_COMMON_EXTERN void push_back(ValuePtr value) { values.insert(end(), value); }
- QPID_COMMON_EXTERN void pop_back() { values.pop_back(); }
+ QPID_COMMON_INLINE_EXTERN iterator insert(iterator i, ValuePtr value) { return values.insert(i, value); }
+ QPID_COMMON_INLINE_EXTERN void erase(iterator i) { values.erase(i); }
+ QPID_COMMON_INLINE_EXTERN void push_back(ValuePtr value) { values.insert(end(), value); }
+ QPID_COMMON_INLINE_EXTERN void pop_back() { values.pop_back(); }
private:
Values values;
diff --git a/cpp/include/qpid/framing/ProtocolVersion.h b/cpp/include/qpid/framing/ProtocolVersion.h
index e7e75d75f6..30094c165d 100644
--- a/cpp/include/qpid/framing/ProtocolVersion.h
+++ b/cpp/include/qpid/framing/ProtocolVersion.h
@@ -29,7 +29,7 @@ namespace qpid
namespace framing
{
-class ProtocolVersion
+class QPID_COMMON_CLASS_EXTERN ProtocolVersion
{
private:
uint8_t major_;
@@ -39,16 +39,16 @@ public:
explicit ProtocolVersion(uint8_t _major=0, uint8_t _minor=0)
: major_(_major), minor_(_minor) {}
- QPID_COMMON_EXTERN uint8_t getMajor() const { return major_; }
- QPID_COMMON_EXTERN void setMajor(uint8_t major) { major_ = major; }
- QPID_COMMON_EXTERN uint8_t getMinor() const { return minor_; }
- QPID_COMMON_EXTERN void setMinor(uint8_t minor) { minor_ = minor; }
+ QPID_COMMON_INLINE_EXTERN uint8_t getMajor() const { return major_; }
+ QPID_COMMON_INLINE_EXTERN void setMajor(uint8_t major) { major_ = major; }
+ QPID_COMMON_INLINE_EXTERN uint8_t getMinor() const { return minor_; }
+ QPID_COMMON_INLINE_EXTERN void setMinor(uint8_t minor) { minor_ = minor; }
QPID_COMMON_EXTERN const std::string toString() const;
QPID_COMMON_EXTERN ProtocolVersion& operator=(ProtocolVersion p);
QPID_COMMON_EXTERN bool operator==(ProtocolVersion p) const;
- QPID_COMMON_EXTERN bool operator!=(ProtocolVersion p) const { return ! (*this == p); }
+ QPID_COMMON_INLINE_EXTERN bool operator!=(ProtocolVersion p) const { return ! (*this == p); }
};
} // namespace framing
diff --git a/cpp/include/qpid/framing/SequenceNumber.h b/cpp/include/qpid/framing/SequenceNumber.h
index 1e53058df8..eed15a4b75 100644
--- a/cpp/include/qpid/framing/SequenceNumber.h
+++ b/cpp/include/qpid/framing/SequenceNumber.h
@@ -34,7 +34,7 @@ class Buffer;
/**
* 4-byte sequence number that 'wraps around'.
*/
-class SequenceNumber : public
+class QPID_COMMON_CLASS_EXTERN SequenceNumber : public
boost::equality_comparable<
SequenceNumber, boost::less_than_comparable<
SequenceNumber, boost::incrementable<
diff --git a/cpp/include/qpid/framing/SequenceSet.h b/cpp/include/qpid/framing/SequenceSet.h
index 39395e9ad7..0a78e418ba 100644
--- a/cpp/include/qpid/framing/SequenceSet.h
+++ b/cpp/include/qpid/framing/SequenceSet.h
@@ -29,7 +29,7 @@ namespace qpid {
namespace framing {
class Buffer;
-class SequenceSet : public RangeSet<SequenceNumber> {
+class QPID_COMMON_CLASS_EXTERN SequenceSet : public RangeSet<SequenceNumber> {
public:
SequenceSet() {}
SequenceSet(const RangeSet<SequenceNumber>& r)
diff --git a/cpp/include/qpid/framing/StructHelper.h b/cpp/include/qpid/framing/StructHelper.h
index fc9a7909cc..21f9b91fa9 100644
--- a/cpp/include/qpid/framing/StructHelper.h
+++ b/cpp/include/qpid/framing/StructHelper.h
@@ -30,7 +30,7 @@
namespace qpid {
namespace framing {
-class StructHelper
+class QPID_COMMON_CLASS_EXTERN StructHelper
{
public:
diff --git a/cpp/include/qpid/framing/Uuid.h b/cpp/include/qpid/framing/Uuid.h
index d0a8d02411..ccfd7e9534 100644
--- a/cpp/include/qpid/framing/Uuid.h
+++ b/cpp/include/qpid/framing/Uuid.h
@@ -52,22 +52,22 @@ struct Uuid : public boost::array<uint8_t, 16> {
// boost::array gives us ==, < etc.
/** Copy from 16 bytes of data. */
- void assign(const uint8_t* data);
+ QPID_COMMON_EXTERN void assign(const uint8_t* data);
/** Set to a new unique identifier. */
QPID_COMMON_EXTERN void generate();
/** Set to all zeros. */
- void clear();
+ QPID_COMMON_EXTERN void clear();
/** Test for null (all zeros). */
QPID_COMMON_EXTERN bool isNull() const;
- operator bool() const { return !isNull(); }
- bool operator!() const { return isNull(); }
+ QPID_COMMON_INLINE_EXTERN operator bool() const { return !isNull(); }
+ QPID_COMMON_INLINE_EXTERN bool operator!() const { return isNull(); }
QPID_COMMON_EXTERN void encode(framing::Buffer& buf) const;
QPID_COMMON_EXTERN void decode(framing::Buffer& buf);
- QPID_COMMON_EXTERN uint32_t encodedSize() const
+ QPID_COMMON_INLINE_EXTERN uint32_t encodedSize() const
{ return static_cast<uint32_t>(size()); }
/** String value in format 1b4e28ba-2fa1-11d2-883f-b9a761bde3fb. */
diff --git a/cpp/include/qpid/log/Logger.h b/cpp/include/qpid/log/Logger.h
index 783ab7bdb9..d255b7e150 100644
--- a/cpp/include/qpid/log/Logger.h
+++ b/cpp/include/qpid/log/Logger.h
@@ -33,10 +33,10 @@ namespace log {
* is handled by Logger::Output-derived classes instantiated by the
* platform's sink-related options.
*/
-class Logger : private boost::noncopyable {
+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};
+ enum FormatFlag { FILE=1, LINE=2, FUNCTION=4, LEVEL=8, TIME=16, THREAD=32, HIRES=64};
/**
* Logging output sink.
@@ -93,7 +93,7 @@ class Logger : private boost::noncopyable {
QPID_COMMON_EXTERN void clear();
/** Get the options used to configure the logger. */
- QPID_COMMON_EXTERN const Options& getOptions() const { return options; }
+ QPID_COMMON_INLINE_EXTERN const Options& getOptions() const { return options; }
private:
diff --git a/cpp/include/qpid/log/Options.h b/cpp/include/qpid/log/Options.h
index bbc47b47d3..17cbfde9bc 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;
+ bool time, level, thread, source, function, hiresTs;
bool trace;
std::string prefix;
std::auto_ptr<SinkOptions> sinkOptions;
diff --git a/cpp/include/qpid/management/ManagementObject.h b/cpp/include/qpid/management/ManagementObject.h
index 747edda150..16bf21038c 100644
--- a/cpp/include/qpid/management/ManagementObject.h
+++ b/cpp/include/qpid/management/ManagementObject.h
@@ -58,14 +58,14 @@ protected:
std::string agentName;
void fromString(const std::string&);
public:
- QPID_COMMON_EXTERN ObjectId() : agent(0), first(0), second(0), agentEpoch(0) {}
- QPID_COMMON_EXTERN ObjectId(const types::Variant& map) :
+ QPID_COMMON_INLINE_EXTERN ObjectId() : agent(0), first(0), second(0), agentEpoch(0) {}
+ QPID_COMMON_INLINE_EXTERN ObjectId(const types::Variant& map) :
agent(0), first(0), second(0), agentEpoch(0) { mapDecode(map.asMap()); }
QPID_COMMON_EXTERN ObjectId(uint8_t flags, uint16_t seq, uint32_t broker);
QPID_COMMON_EXTERN ObjectId(AgentAttachment* _agent, uint8_t flags, uint16_t seq);
QPID_COMMON_EXTERN ObjectId(std::istream&);
QPID_COMMON_EXTERN ObjectId(const std::string&);
- QPID_COMMON_EXTERN ObjectId(const std::string& agentAddress, const std::string& key,
+ QPID_COMMON_INLINE_EXTERN ObjectId(const std::string& agentAddress, const std::string& key,
uint64_t epoch=0) : agent(0), first(0), second(0),
agentEpoch(epoch), v2Key(key), agentName(agentAddress) {}
@@ -76,15 +76,15 @@ public:
QPID_COMMON_EXTERN void mapEncode(types::Variant::Map& map) const;
QPID_COMMON_EXTERN void mapDecode(const types::Variant::Map& map);
QPID_COMMON_EXTERN operator types::Variant::Map() const;
- QPID_COMMON_EXTERN uint32_t encodedSize() const { return 16; };
+ QPID_COMMON_INLINE_EXTERN uint32_t encodedSize() const { return 16; };
QPID_COMMON_EXTERN void encode(std::string& buffer) const;
QPID_COMMON_EXTERN void decode(const std::string& buffer);
QPID_COMMON_EXTERN bool equalV1(const ObjectId &other) const;
- QPID_COMMON_EXTERN void setV2Key(const std::string& _key) { v2Key = _key; }
+ QPID_COMMON_INLINE_EXTERN void setV2Key(const std::string& _key) { v2Key = _key; }
QPID_COMMON_EXTERN void setV2Key(const ManagementObject& object);
- QPID_COMMON_EXTERN void setAgentName(const std::string& _name) { agentName = _name; }
- QPID_COMMON_EXTERN const std::string& getAgentName() const { return agentName; }
- QPID_COMMON_EXTERN const std::string& getV2Key() const { return v2Key; }
+ QPID_COMMON_INLINE_EXTERN void setAgentName(const std::string& _name) { agentName = _name; }
+ QPID_COMMON_INLINE_EXTERN const std::string& getAgentName() const { return agentName; }
+ QPID_COMMON_INLINE_EXTERN const std::string& getV2Key() const { return v2Key; }
friend QPID_COMMON_EXTERN std::ostream& operator<<(std::ostream&, const ObjectId&);
};
@@ -131,7 +131,7 @@ public:
virtual ~ManagementItem() {}
};
-class ManagementObject : public ManagementItem
+class QPID_COMMON_CLASS_EXTERN ManagementObject : public ManagementItem
{
protected:
diff --git a/cpp/include/qpid/messaging/Address.h b/cpp/include/qpid/messaging/Address.h
index bebbfc72f6..63dce0c49d 100644
--- a/cpp/include/qpid/messaging/Address.h
+++ b/cpp/include/qpid/messaging/Address.h
@@ -119,7 +119,7 @@ class AddressImpl;
*
* An address has value semantics.
*/
-class Address
+class QPID_MESSAGING_CLASS_EXTERN Address
{
public:
QPID_MESSAGING_EXTERN Address();
diff --git a/cpp/include/qpid/messaging/Connection.h b/cpp/include/qpid/messaging/Connection.h
index 1ad7a7242f..165573e2ef 100644
--- a/cpp/include/qpid/messaging/Connection.h
+++ b/cpp/include/qpid/messaging/Connection.h
@@ -42,7 +42,7 @@ class Session;
* A connection represents a network connection to a remote endpoint.
*/
-class Connection : public qpid::messaging::Handle<ConnectionImpl>
+class QPID_MESSAGING_CLASS_EXTERN Connection : public qpid::messaging::Handle<ConnectionImpl>
{
public:
QPID_MESSAGING_EXTERN Connection(ConnectionImpl* impl);
@@ -54,27 +54,27 @@ class Connection : public qpid::messaging::Handle<ConnectionImpl>
* username
* password
* heartbeat
- * tcp-nodelay
- * sasl-mechanism
- * sasl-service
- * sasl-min-ssf
- * sasl-max-ssf
+ * tcp_nodelay
+ * sasl_mechanisms
+ * sasl_service
+ * sasl_min_ssf
+ * sasl_max_ssf
* transport
*
* Reconnect behaviour can be controlled through the following options:
*
* reconnect: true/false (enables/disables reconnect entirely)
- * reconnect-timeout: number of seconds (give up and report failure after specified time)
- * reconnect-limit: n (give up and report failure after specified number of attempts)
- * reconnect-interval-min: number of seconds (initial delay between failed reconnection attempts)
- * reconnect-interval-max: number of seconds (maximum delay between failed reconnection attempts)
- * reconnect-interval: shorthand for setting the same reconnect_interval_min/max
- * reconnect-urls: list of alternate urls to try when connecting
+ * reconnect_timeout: number of seconds (give up and report failure after specified time)
+ * reconnect_limit: n (give up and report failure after specified number of attempts)
+ * reconnect_interval_min: number of seconds (initial delay between failed reconnection attempts)
+ * reconnect_interval_max: number of seconds (maximum delay between failed reconnection attempts)
+ * reconnect_interval: shorthand for setting the same reconnect_interval_min/max
+ * reconnect_urls: list of alternate urls to try when connecting
*
- * The reconnect-interval is the time that the client waits
+ * The reconnect_interval is the time that the client waits
* for after a failed attempt to reconnect before retrying. It
- * starts at the value of the min-retry-interval and is
- * doubled every failure until the value of max-retry-interval
+ * starts at the value of the min_retry_interval and is
+ * doubled every failure until the value of max_retry_interval
* is reached.
*/
QPID_MESSAGING_EXTERN Connection(const std::string& url, const qpid::types::Variant::Map& options = qpid::types::Variant::Map());
diff --git a/cpp/include/qpid/messaging/Duration.h b/cpp/include/qpid/messaging/Duration.h
index abcf169090..6b8f05c7c6 100644
--- a/cpp/include/qpid/messaging/Duration.h
+++ b/cpp/include/qpid/messaging/Duration.h
@@ -32,7 +32,7 @@ namespace messaging {
/** \ingroup messaging
* A duration is a time in milliseconds.
*/
-class Duration
+class QPID_MESSAGING_CLASS_EXTERN Duration
{
public:
QPID_MESSAGING_EXTERN explicit Duration(uint64_t milliseconds);
@@ -46,9 +46,11 @@ class Duration
};
QPID_MESSAGING_EXTERN Duration operator*(const Duration& duration,
- uint64_t multiplier);
+ uint64_t multiplier);
QPID_MESSAGING_EXTERN Duration operator*(uint64_t multiplier,
- const Duration& duration);
+ const Duration& duration);
+QPID_MESSAGING_EXTERN bool operator==(const Duration& a, const Duration& b);
+QPID_MESSAGING_EXTERN bool operator!=(const Duration& a, const Duration& b);
}} // namespace qpid::messaging
diff --git a/cpp/include/qpid/messaging/FailoverUpdates.h b/cpp/include/qpid/messaging/FailoverUpdates.h
index 14a1a31b63..6d7314620a 100644
--- a/cpp/include/qpid/messaging/FailoverUpdates.h
+++ b/cpp/include/qpid/messaging/FailoverUpdates.h
@@ -32,7 +32,7 @@ struct FailoverUpdatesImpl;
* A utility to listen for updates on cluster membership and update
* the list of known urls for a connection accordingly.
*/
-class FailoverUpdates
+class QPID_MESSAGING_CLASS_EXTERN FailoverUpdates
{
public:
QPID_MESSAGING_EXTERN FailoverUpdates(Connection& connection);
diff --git a/cpp/include/qpid/messaging/Handle.h b/cpp/include/qpid/messaging/Handle.h
index 1e634ef888..97a8f00b54 100644
--- a/cpp/include/qpid/messaging/Handle.h
+++ b/cpp/include/qpid/messaging/Handle.h
@@ -40,22 +40,22 @@ template <class T> class Handle {
public:
/**@return true if handle is valid, i.e. not null. */
- QPID_MESSAGING_EXTERN bool isValid() const { return impl; }
+ QPID_MESSAGING_INLINE_EXTERN bool isValid() const { return impl; }
/**@return true if handle is null. It is an error to call any function on a null handle. */
- QPID_MESSAGING_EXTERN bool isNull() const { return !impl; }
+ QPID_MESSAGING_INLINE_EXTERN bool isNull() const { return !impl; }
/** Conversion to bool supports idiom if (handle) { handle->... } */
- QPID_MESSAGING_EXTERN operator bool() const { return impl; }
+ QPID_MESSAGING_INLINE_EXTERN operator bool() const { return impl; }
/** Operator ! supports idiom if (!handle) { do_if_handle_is_null(); } */
- QPID_MESSAGING_EXTERN bool operator !() const { return !impl; }
+ QPID_MESSAGING_INLINE_EXTERN bool operator !() const { return !impl; }
void swap(Handle<T>& h) { T* t = h.impl; h.impl = impl; impl = t; }
protected:
typedef T Impl;
- QPID_MESSAGING_EXTERN Handle() :impl() {}
+ QPID_MESSAGING_INLINE_EXTERN Handle() :impl() {}
// Not implemented,subclasses must implement.
QPID_MESSAGING_EXTERN Handle(const Handle&);
diff --git a/cpp/include/qpid/messaging/ImportExport.h b/cpp/include/qpid/messaging/ImportExport.h
index 52f3eb8568..ab5f21f618 100644
--- a/cpp/include/qpid/messaging/ImportExport.h
+++ b/cpp/include/qpid/messaging/ImportExport.h
@@ -20,14 +20,16 @@
* under the License.
*/
-#if defined(WIN32) && !defined(QPID_DECLARE_STATIC)
+#include "qpid/ImportExport.h"
+
#if defined(CLIENT_EXPORT) || defined (qpidmessaging_EXPORTS)
-#define QPID_MESSAGING_EXTERN __declspec(dllexport)
-#else
-#define QPID_MESSAGING_EXTERN __declspec(dllimport)
-#endif
+# define QPID_MESSAGING_EXTERN QPID_EXPORT
+# define QPID_MESSAGING_CLASS_EXTERN QPID_CLASS_EXPORT
+# define QPID_MESSAGING_INLINE_EXTERN QPID_INLINE_EXPORT
#else
-#define QPID_MESSAGING_EXTERN
+# define QPID_MESSAGING_EXTERN QPID_IMPORT
+# define QPID_MESSAGING_CLASS_EXTERN QPID_CLASS_IMPORT
+# define QPID_MESSAGING_INLINE_EXTERN QPID_INLINE_IMPORT
#endif
#endif /*!QPID_MESSAGING_IMPORTEXPORT_H*/
diff --git a/cpp/include/qpid/messaging/Message.h b/cpp/include/qpid/messaging/Message.h
index d48af35cc0..e89a6ce02f 100644
--- a/cpp/include/qpid/messaging/Message.h
+++ b/cpp/include/qpid/messaging/Message.h
@@ -39,7 +39,7 @@ struct MessageImpl;
/** \ingroup messaging
* Representation of a message.
*/
-class Message
+class QPID_MESSAGING_CLASS_EXTERN Message
{
public:
QPID_MESSAGING_EXTERN Message(const std::string& bytes = std::string());
@@ -55,23 +55,58 @@ class Message
QPID_MESSAGING_EXTERN void setSubject(const std::string&);
QPID_MESSAGING_EXTERN const std::string& getSubject() const;
+ /**
+ * Set the content type (i.e. the MIME type) for the message. This
+ * should be set by the sending application and indicates to
+ * recipients of message how to interpret or decode the content.
+ */
QPID_MESSAGING_EXTERN void setContentType(const std::string&);
+ /**
+ * Returns the content type (i.e. the MIME type) for the
+ * message. This can be used to determine how to decode the
+ * message content.
+ */
QPID_MESSAGING_EXTERN const std::string& getContentType() const;
+ /**
+ * Set an application defined identifier for the message. At
+ * present this must be a stringfied UUID (support for less
+ * restrictive IDs is anticipated however).
+ */
QPID_MESSAGING_EXTERN void setMessageId(const std::string&);
QPID_MESSAGING_EXTERN const std::string& getMessageId() const;
+ /**
+ * Sets the user id of the message. This should in general be the
+ * user-id as which the sending connection authenticated itself as
+ * the messaging infrastructure will verify this. See
+ * Connection::getAuthenticatedUsername()
+ */
QPID_MESSAGING_EXTERN void setUserId(const std::string&);
QPID_MESSAGING_EXTERN const std::string& getUserId() const;
+ /**
+ * Can be used to set application specific correlation identifiers
+ * as part of a protocol for message exchange patterns. E.g. a
+ * request-reponse pattern might require the correlation-id of the
+ * request and response to match, or might use the message-id of
+ * the request as the correlation-id on the response etc.
+ */
QPID_MESSAGING_EXTERN void setCorrelationId(const std::string&);
QPID_MESSAGING_EXTERN const std::string& getCorrelationId() const;
+ /**
+ * Sets a priority level on the message. This may be used by the
+ * messaging infrastructure to prioritise delivery of higher
+ * priority messages.
+ */
QPID_MESSAGING_EXTERN void setPriority(uint8_t);
QPID_MESSAGING_EXTERN uint8_t getPriority() const;
/**
- * Set the time to live for this message in milliseconds.
+ * Set the time to live for this message in milliseconds. This can
+ * be used by the messaging infrastructure to discard messages
+ * that are no longer of relevance.
*/
QPID_MESSAGING_EXTERN void setTtl(Duration ttl);
/**
@@ -79,24 +114,62 @@ class Message
*/
QPID_MESSAGING_EXTERN Duration getTtl() const;
+ /**
+ * Mark the message as durable. This is a hint to the messaging
+ * infrastructure that the message should be persisted or
+ * otherwise stored such that failoures or shutdown do not cause
+ * it to be lost.
+ */
QPID_MESSAGING_EXTERN void setDurable(bool durable);
QPID_MESSAGING_EXTERN bool getDurable() const;
+ /**
+ * The redelivered flag if set implies that the message *may* have
+ * been previously delivered and thus is a hint to the application
+ * or messaging infrastructure that if de-duplication is required
+ * this message should be examined to determine if it is a
+ * duplicate.
+ */
QPID_MESSAGING_EXTERN bool getRedelivered() const;
+ /**
+ * Can be used to provide a hint to the application or messaging
+ * infrastructure that if de-duplication is required this message
+ * should be examined to determine if it is a duplicate.
+ */
QPID_MESSAGING_EXTERN void setRedelivered(bool);
+ /**
+ * In addition to a payload (i.e. the content), messages can
+ * include annotations describing aspectf of the message. In
+ * addition to the standard annotations such as TTL and content
+ * type, application- or context- specific properties can also be
+ * defined. Each message has a map of name values for such custom
+ * properties. The value is specified as a Variant.
+ */
QPID_MESSAGING_EXTERN const qpid::types::Variant::Map& getProperties() const;
QPID_MESSAGING_EXTERN qpid::types::Variant::Map& getProperties();
+ /**
+ * Set the content to the data held in the string parameter. Note:
+ * this is treated as raw bytes and need not be text. Consider
+ * setting the content-type to indicate how the data should be
+ * interpreted by recipients.
+ */
QPID_MESSAGING_EXTERN void setContent(const std::string&);
/**
- * Note that chars are copied.
+ * Copy count bytes from the region pointed to by chars as the
+ * message content.
*/
QPID_MESSAGING_EXTERN void setContent(const char* chars, size_t count);
/** Get the content as a std::string */
QPID_MESSAGING_EXTERN std::string getContent() const;
- /** Get a const pointer to the start of the content data. */
+ /**
+ * Get a const pointer to the start of the content data. The
+ * memory pointed to is owned by the message. The getContentSize()
+ * method indicates how much data there is (i.e. the extent of the
+ * memory region pointed to by the return value of this method).
+ */
QPID_MESSAGING_EXTERN const char* getContentPtr() const;
/** Get the size of content in bytes. */
QPID_MESSAGING_EXTERN size_t getContentSize() const;
@@ -107,9 +180,9 @@ class Message
friend struct MessageImplAccess;
};
-struct EncodingException : qpid::types::Exception
+struct QPID_MESSAGING_CLASS_EXTERN EncodingException : qpid::types::Exception
{
- EncodingException(const std::string& msg);
+ QPID_MESSAGING_EXTERN EncodingException(const std::string& msg);
};
/**
@@ -122,8 +195,8 @@ struct EncodingException : qpid::types::Exception
* @exception EncodingException
*/
QPID_MESSAGING_EXTERN void decode(const Message& message,
- qpid::types::Variant::Map& map,
- const std::string& encoding = std::string());
+ qpid::types::Variant::Map& map,
+ const std::string& encoding = std::string());
/**
* Decodes message content into a Variant::List.
*
@@ -134,8 +207,8 @@ QPID_MESSAGING_EXTERN void decode(const Message& message,
* @exception EncodingException
*/
QPID_MESSAGING_EXTERN void decode(const Message& message,
- qpid::types::Variant::List& list,
- const std::string& encoding = std::string());
+ qpid::types::Variant::List& list,
+ const std::string& encoding = std::string());
/**
* Encodes a Variant::Map into a message.
*
@@ -146,8 +219,8 @@ QPID_MESSAGING_EXTERN void decode(const Message& message,
* @exception EncodingException
*/
QPID_MESSAGING_EXTERN void encode(const qpid::types::Variant::Map& map,
- Message& message,
- const std::string& encoding = std::string());
+ Message& message,
+ const std::string& encoding = std::string());
/**
* Encodes a Variant::List into a message.
*
@@ -158,8 +231,8 @@ QPID_MESSAGING_EXTERN void encode(const qpid::types::Variant::Map& map,
* @exception EncodingException
*/
QPID_MESSAGING_EXTERN void encode(const qpid::types::Variant::List& list,
- Message& message,
- const std::string& encoding = std::string());
+ Message& message,
+ const std::string& encoding = std::string());
}} // namespace qpid::messaging
diff --git a/cpp/include/qpid/messaging/Receiver.h b/cpp/include/qpid/messaging/Receiver.h
index 6f3ae961db..13317dfcbd 100644
--- a/cpp/include/qpid/messaging/Receiver.h
+++ b/cpp/include/qpid/messaging/Receiver.h
@@ -41,7 +41,7 @@ class Session;
/** \ingroup messaging
* Interface through which messages are received.
*/
-class Receiver : public qpid::messaging::Handle<ReceiverImpl>
+class QPID_MESSAGING_CLASS_EXTERN Receiver : public qpid::messaging::Handle<ReceiverImpl>
{
public:
QPID_MESSAGING_EXTERN Receiver(ReceiverImpl* impl = 0);
diff --git a/cpp/include/qpid/messaging/Sender.h b/cpp/include/qpid/messaging/Sender.h
index 85658f37cc..8e1c5846e9 100644
--- a/cpp/include/qpid/messaging/Sender.h
+++ b/cpp/include/qpid/messaging/Sender.h
@@ -40,7 +40,7 @@ class Session;
/** \ingroup messaging
* Interface through which messages are sent.
*/
-class Sender : public qpid::messaging::Handle<SenderImpl>
+class QPID_MESSAGING_CLASS_EXTERN Sender : public qpid::messaging::Handle<SenderImpl>
{
public:
QPID_MESSAGING_EXTERN Sender(SenderImpl* impl = 0);
diff --git a/cpp/include/qpid/messaging/Session.h b/cpp/include/qpid/messaging/Session.h
index 6c023629e0..e8d6efb35d 100644
--- a/cpp/include/qpid/messaging/Session.h
+++ b/cpp/include/qpid/messaging/Session.h
@@ -46,7 +46,7 @@ class SessionImpl;
* A session represents a distinct 'conversation' which can involve
* sending and receiving messages to and from different addresses.
*/
-class Session : public qpid::messaging::Handle<SessionImpl>
+class QPID_MESSAGING_CLASS_EXTERN Session : public qpid::messaging::Handle<SessionImpl>
{
public:
QPID_MESSAGING_EXTERN Session(SessionImpl* impl = 0);
@@ -62,6 +62,12 @@ class Session : public qpid::messaging::Handle<SessionImpl>
*/
QPID_MESSAGING_EXTERN void close();
+ /**
+ * Commits the sessions transaction.
+ *
+ * @exception TransactionAborted if the original session is lost
+ * forcing an automatic rollback.
+ */
QPID_MESSAGING_EXTERN void commit();
QPID_MESSAGING_EXTERN void rollback();
@@ -78,6 +84,10 @@ class Session : public qpid::messaging::Handle<SessionImpl>
*/
QPID_MESSAGING_EXTERN void acknowledge(Message&, bool sync=false);
/**
+ * Acknowledges all message up to the specified message.
+ */
+ QPID_MESSAGING_EXTERN void acknowledgeUpTo(Message&, bool sync=false);
+ /**
* Rejects the specified message. The broker does not redeliver a
* message that has been rejected. Once a message has been
* acknowledged, it can no longer be rejected.
@@ -135,25 +145,51 @@ class Session : public qpid::messaging::Handle<SessionImpl>
/**
* Create a new sender through which messages can be sent to the
* specified address.
+ *
+ * @exception ResolutionError if there is an error in resolving
+ * the address
*/
QPID_MESSAGING_EXTERN Sender createSender(const Address& address);
+ /**
+ * Create a new sender through which messages can be sent to the
+ * specified address.
+ *
+ * @exception ResolutionError if there is an error in resolving
+ * the address
+ *
+ * @exception MalformedAddress if the syntax of address is not
+ * valid
+ */
QPID_MESSAGING_EXTERN Sender createSender(const std::string& address);
/**
* Create a new receiver through which messages can be received
* from the specified address.
+ *
+ * @exception ResolutionError if there is an error in resolving
+ * the address
*/
QPID_MESSAGING_EXTERN Receiver createReceiver(const Address& address);
+ /**
+ * Create a new receiver through which messages can be received
+ * from the specified address.
+ *
+ * @exception ResolutionError if there is an error in resolving
+ * the address
+ *
+ * @exception MalformedAddress if the syntax of address is not
+ * valid
+ */
QPID_MESSAGING_EXTERN Receiver createReceiver(const std::string& address);
/**
* Returns the sender with the specified name.
- *@exception KeyError if there is none for that name.
+ * @exception KeyError if there is none for that name.
*/
QPID_MESSAGING_EXTERN Sender getSender(const std::string& name) const;
/**
* Returns the receiver with the specified name.
- *@exception KeyError if there is none for that name.
+ * @exception KeyError if there is none for that name.
*/
QPID_MESSAGING_EXTERN Receiver getReceiver(const std::string& name) const;
/**
@@ -162,7 +198,16 @@ class Session : public qpid::messaging::Handle<SessionImpl>
*/
QPID_MESSAGING_EXTERN Connection getConnection() const;
+ /**
+ * @returns true if the session has been rendered invalid by some
+ * exception, false if it is valid for use.
+ */
QPID_MESSAGING_EXTERN bool hasError();
+ /**
+ * If the session has been rendered invalid by some exception,
+ * this method will result in that exception being thrown on
+ * calling this method.
+ */
QPID_MESSAGING_EXTERN void checkError();
#ifndef SWIG
diff --git a/cpp/include/qpid/messaging/exceptions.h b/cpp/include/qpid/messaging/exceptions.h
index 0ff608b343..31e2488d91 100644
--- a/cpp/include/qpid/messaging/exceptions.h
+++ b/cpp/include/qpid/messaging/exceptions.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -29,34 +29,47 @@
namespace qpid {
namespace messaging {
-/** \ingroup messaging
+/** \ingroup messaging
*/
-struct MessagingException : public qpid::types::Exception
+/**
+ * This is the base class for all messaging related exceptions thrown
+ * by this API.
+ */
+struct QPID_MESSAGING_CLASS_EXTERN MessagingException : public qpid::types::Exception
{
QPID_MESSAGING_EXTERN MessagingException(const std::string& msg);
QPID_MESSAGING_EXTERN virtual ~MessagingException() throw();
-
+
qpid::types::Variant::Map detail;
//TODO: override what() to include detail if present
};
-struct InvalidOptionString : public MessagingException
+/**
+ * Thrown when the syntax of the option string used to configure a
+ * connection in not valid
+ */
+struct QPID_MESSAGING_CLASS_EXTERN InvalidOptionString : public MessagingException
{
QPID_MESSAGING_EXTERN InvalidOptionString(const std::string& msg);
};
-struct KeyError : public MessagingException
+/**
+ * Thrown to indicate a failed lookup of some local object. For
+ * example when attempting to retrieve a session, sender or receiver
+ * by name.
+ */
+struct QPID_MESSAGING_CLASS_EXTERN KeyError : public MessagingException
{
QPID_MESSAGING_EXTERN KeyError(const std::string&);
};
-struct LinkError : public MessagingException
+struct QPID_MESSAGING_CLASS_EXTERN LinkError : public MessagingException
{
QPID_MESSAGING_EXTERN LinkError(const std::string&);
};
-struct AddressError : public LinkError
+struct QPID_MESSAGING_CLASS_EXTERN AddressError : public LinkError
{
QPID_MESSAGING_EXTERN AddressError(const std::string&);
};
@@ -65,85 +78,118 @@ struct AddressError : public LinkError
* Thrown when a syntactically correct address cannot be resolved or
* used.
*/
-struct ResolutionError : public AddressError
+struct QPID_MESSAGING_CLASS_EXTERN ResolutionError : public AddressError
{
QPID_MESSAGING_EXTERN ResolutionError(const std::string& msg);
};
-struct AssertionFailed : public ResolutionError
+/**
+ * Thrown when creating a sender or receiver for an address for which
+ * some asserted property of the node is not matched.
+ */
+struct QPID_MESSAGING_CLASS_EXTERN AssertionFailed : public ResolutionError
{
QPID_MESSAGING_EXTERN AssertionFailed(const std::string& msg);
};
-struct NotFound : public ResolutionError
+/**
+ * Thrown on attempts to create a sender or receiver to a non-existent
+ * node.
+ */
+struct QPID_MESSAGING_CLASS_EXTERN NotFound : public ResolutionError
{
QPID_MESSAGING_EXTERN NotFound(const std::string& msg);
};
/**
- * Thrown when an address string with inalid sytanx is used.
+ * Thrown when an address string with invalid syntax is used.
*/
-struct MalformedAddress : public AddressError
+struct QPID_MESSAGING_CLASS_EXTERN MalformedAddress : public AddressError
{
QPID_MESSAGING_EXTERN MalformedAddress(const std::string& msg);
};
-struct ReceiverError : public LinkError
+struct QPID_MESSAGING_CLASS_EXTERN ReceiverError : public LinkError
{
QPID_MESSAGING_EXTERN ReceiverError(const std::string&);
};
-struct FetchError : public ReceiverError
+struct QPID_MESSAGING_CLASS_EXTERN FetchError : public ReceiverError
{
QPID_MESSAGING_EXTERN FetchError(const std::string&);
};
-struct NoMessageAvailable : public FetchError
+/**
+ * Thrown by Receiver::fetch(), Receiver::get() and
+ * Session::nextReceiver() to indicate that there no message was
+ * available before the timeout specified.
+ */
+struct QPID_MESSAGING_CLASS_EXTERN NoMessageAvailable : public FetchError
{
QPID_MESSAGING_EXTERN NoMessageAvailable();
};
-struct SenderError : public LinkError
+struct QPID_MESSAGING_CLASS_EXTERN SenderError : public LinkError
{
QPID_MESSAGING_EXTERN SenderError(const std::string&);
};
-struct SendError : public SenderError
+struct QPID_MESSAGING_CLASS_EXTERN SendError : public SenderError
{
QPID_MESSAGING_EXTERN SendError(const std::string&);
};
-struct TargetCapacityExceeded : public SendError
+/**
+ * Thrown to indicate that the sender attempted to send a message that
+ * would result in the target node on the peer exceeding a
+ * preconfigured capacity.
+ */
+struct QPID_MESSAGING_CLASS_EXTERN TargetCapacityExceeded : public SendError
{
QPID_MESSAGING_EXTERN TargetCapacityExceeded(const std::string&);
};
-struct SessionError : public MessagingException
+struct QPID_MESSAGING_CLASS_EXTERN SessionError : public MessagingException
{
QPID_MESSAGING_EXTERN SessionError(const std::string&);
};
-struct TransactionError : public SessionError
+struct QPID_MESSAGING_CLASS_EXTERN TransactionError : public SessionError
{
QPID_MESSAGING_EXTERN TransactionError(const std::string&);
};
-struct TransactionAborted : public TransactionError
+/**
+ * Thrown on Session::commit() if reconnection results in the
+ * transaction being automatically aborted.
+ */
+struct QPID_MESSAGING_CLASS_EXTERN TransactionAborted : public TransactionError
{
QPID_MESSAGING_EXTERN TransactionAborted(const std::string&);
};
-struct UnauthorizedAccess : public SessionError
+/**
+ * Thrown to indicate that the application attempted to do something
+ * for which it was not authorised by its peer.
+ */
+struct QPID_MESSAGING_CLASS_EXTERN UnauthorizedAccess : public SessionError
{
QPID_MESSAGING_EXTERN UnauthorizedAccess(const std::string&);
};
-struct ConnectionError : public MessagingException
+struct QPID_MESSAGING_CLASS_EXTERN ConnectionError : public MessagingException
{
QPID_MESSAGING_EXTERN ConnectionError(const std::string&);
};
-struct TransportFailure : public MessagingException
+/**
+ * Thrown to indicate loss of underlying connection. When
+ * auto-reconnect is used this will be caught by the library and used
+ * to trigger reconnection attempts. If reconnection fails (according
+ * to whatever settings have been configured), then an instnace of
+ * this class will be thrown to signal that.
+ */
+struct QPID_MESSAGING_CLASS_EXTERN TransportFailure : public MessagingException
{
QPID_MESSAGING_EXTERN TransportFailure(const std::string&);
};
diff --git a/cpp/include/qpid/sys/ExceptionHolder.h b/cpp/include/qpid/sys/ExceptionHolder.h
index 9eff1d64c7..4bc934cf75 100644
--- a/cpp/include/qpid/sys/ExceptionHolder.h
+++ b/cpp/include/qpid/sys/ExceptionHolder.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -42,14 +42,11 @@ class ExceptionHolder : public Raisable {
public:
ExceptionHolder() {}
// Use default copy & assign.
-
+
/** Take ownership of ex */
template <class Ex> ExceptionHolder(Ex* ex) { wrap(ex); }
- template <class Ex> ExceptionHolder(const boost::shared_ptr<Ex>& ex) { wrap(ex.release()); }
-
template <class Ex> ExceptionHolder& operator=(Ex* ex) { wrap(ex); return *this; }
- template <class Ex> ExceptionHolder& operator=(boost::shared_ptr<Ex> ex) { wrap(ex.release()); return *this; }
-
+
void raise() const { if (wrapper.get()) wrapper->raise() ; }
std::string what() const { return wrapper.get() ? wrapper->what() : std::string(); }
bool empty() const { return !wrapper.get(); }
@@ -66,7 +63,7 @@ class ExceptionHolder : public Raisable {
template <class Ex> void wrap(Ex* ex) { wrapper.reset(new Wrapper<Ex>(ex)); }
boost::shared_ptr<Raisable> wrapper;
};
-
+
}} // namespace qpid::sys
diff --git a/cpp/include/qpid/sys/IntegerTypes.h b/cpp/include/qpid/sys/IntegerTypes.h
index 89635f033e..75fa921de0 100755
--- a/cpp/include/qpid/sys/IntegerTypes.h
+++ b/cpp/include/qpid/sys/IntegerTypes.h
@@ -21,7 +21,7 @@
*
*/
-#if (defined(_WINDOWS) || defined (WIN32)) && defined(_MSC_VER)
+#if (defined(_WINDOWS) || defined (WIN32))
#include "qpid/sys/windows/IntegerTypes.h"
#endif
#if !defined _WINDOWS && !defined WIN32
diff --git a/cpp/include/qpid/sys/Runnable.h b/cpp/include/qpid/sys/Runnable.h
index 0f1243a277..fed7663cb6 100644
--- a/cpp/include/qpid/sys/Runnable.h
+++ b/cpp/include/qpid/sys/Runnable.h
@@ -30,7 +30,7 @@ namespace sys {
/**
* Interface for objects that can be run, e.g. in a thread.
*/
-class Runnable
+class QPID_COMMON_CLASS_EXTERN Runnable
{
public:
/** Type to represent a runnable as a Functor */
diff --git a/cpp/include/qpid/sys/Thread.h b/cpp/include/qpid/sys/Thread.h
index 45a39e796f..f556612908 100644
--- a/cpp/include/qpid/sys/Thread.h
+++ b/cpp/include/qpid/sys/Thread.h
@@ -25,7 +25,11 @@
#include "qpid/CommonImportExport.h"
#ifdef _WIN32
-# define QPID_TSS __declspec(thread)
+# ifdef _MSC_VER
+# define QPID_TSS __declspec(thread)
+# else
+# define QPID_TSS __thread
+# endif
#elif defined (__GNUC__)
# define QPID_TSS __thread
#elif defined (__SUNPRO_CC)
diff --git a/cpp/include/qpid/sys/Time.h b/cpp/include/qpid/sys/Time.h
index d3ab832229..9c5ac66e9a 100644
--- a/cpp/include/qpid/sys/Time.h
+++ b/cpp/include/qpid/sys/Time.h
@@ -119,7 +119,7 @@ class Duration {
friend class AbsTime;
public:
- QPID_COMMON_EXTERN inline Duration(int64_t time0 = 0);
+ QPID_COMMON_INLINE_EXTERN inline Duration(int64_t time0 = 0);
QPID_COMMON_EXTERN explicit Duration(const AbsTime& start, const AbsTime& finish);
inline operator int64_t() const;
};
@@ -167,6 +167,9 @@ QPID_COMMON_EXTERN void usleep(uint64_t usecs);
/** Output formatted date/time for now*/
void outputFormattedNow(std::ostream&);
+/** Output unformatted nanosecond-resolution time for now */
+void outputHiresNow(std::ostream&);
+
}}
#endif /*!_sys_Time_h*/
diff --git a/cpp/include/qpid/sys/windows/IntegerTypes.h b/cpp/include/qpid/sys/windows/IntegerTypes.h
index ece1a618e9..28b82da1a0 100755
--- a/cpp/include/qpid/sys/windows/IntegerTypes.h
+++ b/cpp/include/qpid/sys/windows/IntegerTypes.h
@@ -22,13 +22,17 @@
*/
typedef unsigned char uint8_t;
-typedef char int8_t;
typedef unsigned short uint16_t;
typedef short int16_t;
typedef unsigned int uint32_t;
typedef int int32_t;
+#if defined(_MSC_VER)
+typedef signed char int8_t;
typedef unsigned __int64 uint64_t;
typedef __int64 int64_t;
+#else
+#include <stdint.h>
+#endif
// Visual Studio doesn't define other common types, so set them up here too.
typedef unsigned int uint;
diff --git a/cpp/include/qpid/types/Exception.h b/cpp/include/qpid/types/Exception.h
index d061a7df0e..483d104cc8 100644
--- a/cpp/include/qpid/types/Exception.h
+++ b/cpp/include/qpid/types/Exception.h
@@ -28,7 +28,7 @@
namespace qpid {
namespace types {
-class Exception : public std::exception
+class QPID_TYPES_CLASS_EXTERN Exception : public std::exception
{
public:
QPID_TYPES_EXTERN explicit Exception(const std::string& message=std::string()) throw();
diff --git a/cpp/include/qpid/types/ImportExport.h b/cpp/include/qpid/types/ImportExport.h
index bb10575fcd..8fa41884fb 100644
--- a/cpp/include/qpid/types/ImportExport.h
+++ b/cpp/include/qpid/types/ImportExport.h
@@ -20,14 +20,16 @@
* under the License.
*/
-#if defined(WIN32) && !defined(QPID_DECLARE_STATIC)
+#include "qpid/ImportExport.h"
+
#if defined(TYPES_EXPORT) || defined (qpidtypes_EXPORTS)
-#define QPID_TYPES_EXTERN __declspec(dllexport)
-#else
-#define QPID_TYPES_EXTERN __declspec(dllimport)
-#endif
+# define QPID_TYPES_EXTERN QPID_EXPORT
+# define QPID_TYPES_CLASS_EXTERN QPID_CLASS_EXPORT
+# define QPID_TYPES_INLINE_EXTERN QPID_INLINE_EXPORT
#else
-#define QPID_TYPES_EXTERN
+# define QPID_TYPES_EXTERN QPID_IMPORT
+# define QPID_TYPES_CLASS_EXTERN QPID_CLASS_IMPORT
+# define QPID_TYPES_INLINE_EXTERN QPID_INLINE_IMPORT
#endif
#endif /*!QPID_TYPES_IMPORTEXPORT_H*/
diff --git a/cpp/include/qpid/types/Uuid.h b/cpp/include/qpid/types/Uuid.h
index 467a895184..02af4c7e7f 100644
--- a/cpp/include/qpid/types/Uuid.h
+++ b/cpp/include/qpid/types/Uuid.h
@@ -29,7 +29,7 @@
namespace qpid {
namespace types {
-class Uuid
+class QPID_TYPES_CLASS_EXTERN Uuid
{
public:
static const size_t SIZE;
diff --git a/cpp/include/qpid/types/Variant.h b/cpp/include/qpid/types/Variant.h
index 9ae672b7c2..4459fc4123 100644
--- a/cpp/include/qpid/types/Variant.h
+++ b/cpp/include/qpid/types/Variant.h
@@ -36,7 +36,7 @@ namespace types {
/**
* Thrown when an illegal conversion of a variant is attempted.
*/
-struct InvalidConversion : public Exception
+struct QPID_TYPES_CLASS_EXTERN InvalidConversion : public Exception
{
InvalidConversion(const std::string& msg);
};
@@ -60,12 +60,14 @@ enum VariantType {
VAR_UUID
};
+std::string getTypeName(VariantType type);
+
class VariantImpl;
/**
* Represents a value of variable type.
*/
-class Variant
+class QPID_TYPES_CLASS_EXTERN Variant
{
public:
typedef std::map<std::string, Variant> Map;
diff --git a/cpp/managementgen/Makefile.am b/cpp/managementgen/Makefile.am
index 6c2024ccaa..4fc5edcad4 100644
--- a/cpp/managementgen/Makefile.am
+++ b/cpp/managementgen/Makefile.am
@@ -19,20 +19,26 @@
qmfpythondir = $(pythondir)
dist_bin_SCRIPTS = \
qmf-gen
-nobase_qmfpython_DATA = \
+
+pkgpyexec_qmfgendir = $(pyexecdir)/qmfgen
+pkgpyexec_qmfgen_PYTHON = \
qmfgen/__init__.py \
qmfgen/generate.py \
qmfgen/schema.py \
+ qmfgen/management-types.xml
+
+pkgpyexec_qmfgentmpldir = $(pyexecdir)/qmfgen/templates
+pkgpyexec_qmfgentmpl_PYTHON = \
qmfgen/templates/Args.h \
qmfgen/templates/Class.cpp \
qmfgen/templates/Class.h \
+ qmfgen/templates/CMakeLists.cmake \
qmfgen/templates/Event.cpp \
qmfgen/templates/Event.h \
qmfgen/templates/Makefile.mk \
qmfgen/templates/Package.cpp \
qmfgen/templates/Package.h \
qmfgen/templates/V2Package.cpp \
- qmfgen/templates/V2Package.h \
- qmfgen/management-types.xml
+ qmfgen/templates/V2Package.h
EXTRA_DIST = $(nobase_qmfpython_DATA) CMakeLists.txt
diff --git a/cpp/managementgen/qmfgen/schema.py b/cpp/managementgen/qmfgen/schema.py
index afdfe42639..59e951fb6e 100755
--- a/cpp/managementgen/qmfgen/schema.py
+++ b/cpp/managementgen/qmfgen/schema.py
@@ -1731,9 +1731,9 @@ class SchemaPackage:
stream.write(" qmf::SchemaProperty arg(\"%s\", %s);\n" % (arg.name, typeName))
if subType:
stream.write(" arg.setSubtype(\"%s\");\n" % subType)
- if stat.unit:
+ if arg.unit:
stream.write(" arg.setUnit(\"%s\");\n" % arg.unit)
- if stat.desc:
+ if arg.desc:
stream.write(" arg.setDesc(\"%s\");\n" % arg.desc)
stream.write(" arg.setDirection(%s);\n" % self.qmfv2Dir(arg.dir))
stream.write(" method.addArgument(arg);\n")
diff --git a/cpp/rubygen/0-10/specification.rb b/cpp/rubygen/0-10/specification.rb
index 7366599eba..ef193f5fd0 100755
--- a/cpp/rubygen/0-10/specification.rb
+++ b/cpp/rubygen/0-10/specification.rb
@@ -252,7 +252,7 @@ class Specification < CppGen
include "#{@dir}/specification"
namespace(@ns) {
genl "template <class F, class R=typename F::result_type>"
- cpp_class("ProxyTemplate") {
+ cpp_extern_class("QPID_COMMON_CLASS_EXTERN", "ProxyTemplate") {
public
genl "ProxyTemplate(F f=F()) : functor(f) {}"
@amqp.classes.each { |c|
diff --git a/cpp/rubygen/MethodBodyDefaultVisitor.rb b/cpp/rubygen/MethodBodyDefaultVisitor.rb
index 4f9b369117..11dbcb8f83 100755
--- a/cpp/rubygen/MethodBodyDefaultVisitor.rb
+++ b/cpp/rubygen/MethodBodyDefaultVisitor.rb
@@ -33,7 +33,7 @@ class MethodBodyDefaultVisitorGen < CppGen
include "qpid/framing/MethodBodyConstVisitor"
namespace(@namespace) {
genl
- cpp_class(@classname, "public MethodBodyConstVisitor") {
+ cpp_extern_class("QPID_COMMON_CLASS_EXTERN", @classname, "public MethodBodyConstVisitor") {
genl "public:"
genl "virtual void defaultVisit() = 0;"
@amqp.methods_.each { |m|
diff --git a/cpp/rubygen/amqpgen.rb b/cpp/rubygen/amqpgen.rb
index 69e65a4056..88720cad5f 100755
--- a/cpp/rubygen/amqpgen.rb
+++ b/cpp/rubygen/amqpgen.rb
@@ -61,7 +61,8 @@ end
class Module
# Add trailing _ to avoid conflict with Object methods.
def mangle(sym)
- (Object.method_defined? sym) ? (sym.to_s+"_").intern : sym
+ sym = (sym.to_s+"_").to_sym if (Object.method_defined?(sym) or sym == :type)
+ sym
end
# Add attribute reader for XML attribute.
@@ -190,7 +191,8 @@ class AmqpElement
"command-fragments" => "session.command-fragment",
"in-doubt" => "dtx.xid",
"tx-publish" => "str-8",
- "queues" => "str-8"
+ "queues" => "str-8",
+ "prepared" => "str-8"
}
def array_type(name)
diff --git a/cpp/rubygen/cppgen.rb b/cpp/rubygen/cppgen.rb
index f0995105f1..7dc21fe1bc 100755
--- a/cpp/rubygen/cppgen.rb
+++ b/cpp/rubygen/cppgen.rb
@@ -377,6 +377,9 @@ class CppGen < Generator
def cpp_class(name, *bases, &block)
struct_class("class", name, bases, &block);
end
+ def cpp_extern_class(scope, name, *bases, &block)
+ struct_class("class "+scope, name, bases, &block);
+ end
def typedef(type, name) genl "typedef #{type} #{name};\n"; end
diff --git a/cpp/rubygen/framing.0-10/MethodBodyDefaultVisitor.rb b/cpp/rubygen/framing.0-10/MethodBodyDefaultVisitor.rb
index 00962de4f9..4c58ff2bbb 100755
--- a/cpp/rubygen/framing.0-10/MethodBodyDefaultVisitor.rb
+++ b/cpp/rubygen/framing.0-10/MethodBodyDefaultVisitor.rb
@@ -33,7 +33,7 @@ class MethodBodyDefaultVisitorGen < CppGen
include "qpid/CommonImportExport.h"
namespace(@namespace) {
genl "class AMQMethodBody;"
- cpp_class(@classname, "public MethodBodyConstVisitor") {
+ cpp_extern_class("QPID_COMMON_CLASS_EXTERN", @classname, "public MethodBodyConstVisitor") {
genl "public:"
genl "virtual void defaultVisit(const AMQMethodBody&) = 0;"
@amqp.methods_.each { |m|
diff --git a/cpp/rubygen/framing.0-10/OperationsInvoker.rb b/cpp/rubygen/framing.0-10/OperationsInvoker.rb
index f9b6cac76b..f9b5ce58d8 100755
--- a/cpp/rubygen/framing.0-10/OperationsInvoker.rb
+++ b/cpp/rubygen/framing.0-10/OperationsInvoker.rb
@@ -69,7 +69,7 @@ class OperationsInvokerGen < CppGen
def invoker_h(invoker, target, methods)
return if methods.empty?
genl
- cpp_class(invoker, "public qpid::framing::Invoker") {
+ cpp_extern_class("QPID_COMMON_CLASS_EXTERN", invoker, "public qpid::framing::Invoker") {
genl "#{target}& target;"
public
genl("Invoker(#{target}& target_) : target(target_) {}")
diff --git a/cpp/rubygen/framing.0-10/Proxy.rb b/cpp/rubygen/framing.0-10/Proxy.rb
index 6e3cb4fd4d..3325616754 100755
--- a/cpp/rubygen/framing.0-10/Proxy.rb
+++ b/cpp/rubygen/framing.0-10/Proxy.rb
@@ -37,7 +37,7 @@ class ProxyGen < CppGen
def inner_class_decl(c)
cname=c.name.caps
- cpp_class(cname, "public Proxy") {
+ cpp_extern_class("QPID_COMMON_CLASS_EXTERN", cname, "public Proxy") {
gen <<EOS
public:
#{cname}(FrameHandler& f) : Proxy(f) {}
@@ -69,7 +69,7 @@ EOS
include "qpid/CommonImportExport.h"
namespace("qpid::framing") {
- cpp_class(@classname, "public Proxy") {
+ cpp_extern_class("QPID_COMMON_CLASS_EXTERN", @classname, "public Proxy") {
public
genl "QPID_COMMON_EXTERN #{@classname}(FrameHandler& out);"
genl
diff --git a/cpp/rubygen/framing.0-10/Session.rb b/cpp/rubygen/framing.0-10/Session.rb
index 61f0e03a8b..e800df9b2e 100755
--- a/cpp/rubygen/framing.0-10/Session.rb
+++ b/cpp/rubygen/framing.0-10/Session.rb
@@ -56,8 +56,8 @@ module SyncAsync
def decl_ctor_opeq()
genl
genl "QPID_CLIENT_EXTERN #{@classname}();"
- genl "QPID_CLIENT_EXTERN #{@classname}(const #{@version_base}& other);"
- genl "QPID_CLIENT_EXTERN #{@classname}& operator=(const #{@version_base}& other);"
+ genl "QPID_CLIENT_INLINE_EXTERN #{@classname}(const #{@version_base}& other);"
+ genl "QPID_CLIENT_INLINE_EXTERN #{@classname}& operator=(const #{@version_base}& other);"
end
def defn_ctor_opeq(inline="")
diff --git a/cpp/rubygen/framing.0-10/structs.rb b/cpp/rubygen/framing.0-10/structs.rb
index c3684aea66..62b33ce773 100755
--- a/cpp/rubygen/framing.0-10/structs.rb
+++ b/cpp/rubygen/framing.0-10/structs.rb
@@ -406,7 +406,7 @@ EOS
namespace qpid {
namespace framing {
-class #{classname} #{inheritance} {
+class QPID_COMMON_CLASS_EXTERN #{classname} #{inheritance} {
EOS
if (is_packed(s))
indent { s.fields.each { |f| genl "#{f.cpptype.name} #{f.cppname};" unless f.type_ == "bit"} }
diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt
index 60f505a10e..bb46f1258b 100644
--- a/cpp/src/CMakeLists.txt
+++ b/cpp/src/CMakeLists.txt
@@ -96,7 +96,7 @@ MACRO (add_msvc_version_full verProject verProjectType verProjectFileExt verFN1
inherit_value ("winver_${verProject}_InternalName" "${verProject}")
inherit_value ("winver_${verProject}_OriginalFilename" "${verProject}.${verProjectFileExt}")
inherit_value ("winver_${verProject}_ProductName" "${winver_DESCRIPTION_SUMMARY}")
-
+
# Create strings to be substituted into the template file
set ("winverFileVersionBinary" "${winver_${verProject}_FileVersionBinary}")
set ("winverProductVersionBinary" "${winver_${verProject}_ProductVersionBinary}")
@@ -126,7 +126,7 @@ ENDMACRO (add_msvc_version_full)
#
MACRO (add_msvc_version verProject verProjectType verProjectFileExt)
if (MSVC)
- add_msvc_version_full (${verProject}
+ add_msvc_version_full (${verProject}
${verProjectType}
${verProjectFileExt}
${winver_FILE_VERSION_N1}
@@ -313,10 +313,6 @@ if (NOT Boost_FILESYSTEM_LIBRARY)
set(Boost_FILESYSTEM_LIBRARY boost_filesystem)
endif (NOT Boost_FILESYSTEM_LIBRARY)
-if (NOT Boost_SYSTEM_LIBRARY)
- set(Boost_SYSTEM_LIBRARY boost_system)
-endif (NOT Boost_SYSTEM_LIBRARY)
-
if (NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
set(Boost_UNIT_TEST_FRAMEWORK_LIBRARY boost_unit_test_framework)
endif (NOT Boost_UNIT_TEST_FRAMEWORK_LIBRARY)
@@ -584,6 +580,15 @@ include (ssl.cmake)
check_symbol_exists (LOG_AUTHPRIV "sys/syslog.h" HAVE_LOG_AUTHPRIV)
check_symbol_exists (LOG_FTP "sys/syslog.h" HAVE_LOG_FTP)
+# Allow MSVC user to select 'WinXP-SP3/Windows Server 2003' as build target version
+set (win32_winnt_default OFF)
+if (CMAKE_SYSTEM_NAME STREQUAL Windows)
+ if (MSVC)
+ set (win32_winnt_default ON)
+ endif (MSVC)
+endif (CMAKE_SYSTEM_NAME STREQUAL Windows)
+option(SET_WIN32_WINNT "In Windows-MSVC build: define _WIN32_WINNT=0x0502 to select target version: Windows XP with SP3" ${win32_winnt_default})
+
if (CMAKE_SYSTEM_NAME STREQUAL Windows)
if (MSVC)
add_definitions(
@@ -593,10 +598,11 @@ if (CMAKE_SYSTEM_NAME STREQUAL Windows)
/wd4244
/wd4800
/wd4355
+ /wd4267
)
- if (MSVC80)
- add_definitions(/D "_WIN32_WINNT=0x0501")
- endif (MSVC80)
+ if (SET_WIN32_WINNT)
+ add_definitions(/D "_WIN32_WINNT=0x0502")
+ endif (SET_WIN32_WINNT)
# set the RelWithDebInfo compile/link switches to equal Release
set (CMAKE_CXX_FLAGS_RELWITHDEBINFO "/MD /O2 /Ob2 /D NDEBUG")
@@ -640,8 +646,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL Windows)
)
set (qpidcommon_platform_LIBS
- ${windows_ssl_libs} ws2_32
- )
+ ${Boost_THREAD_LIBRARY} ${windows_ssl_libs} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_DATE_TIME_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY} ws2_32 )
set (qpidbroker_platform_SOURCES
qpid/broker/windows/BrokerDefaults.cpp
qpid/broker/windows/SaslAuthenticator.cpp
@@ -662,7 +667,7 @@ if (CMAKE_SYSTEM_NAME STREQUAL Windows)
windows/QpiddBroker.cpp
windows/SCM.cpp
)
-
+
set (qpidmessaging_platform_SOURCES
qpid/messaging/HandleInstantiator.cpp
)
@@ -920,8 +925,6 @@ set (qpidmessaging_SOURCES
qpid/client/amqp0_10/SessionImpl.cpp
qpid/client/amqp0_10/SenderImpl.h
qpid/client/amqp0_10/SenderImpl.cpp
- qpid/client/amqp0_10/SimpleUrlParser.h
- qpid/client/amqp0_10/SimpleUrlParser.cpp
)
add_msvc_version (qpidmessaging library dll)
@@ -943,7 +946,7 @@ if (NOT QPID_GENERATED_HEADERS_IN_SOURCE)
endif (NOT QPID_GENERATED_HEADERS_IN_SOURCE)
-if (WIN32)
+if (MSVC)
# Install the DtcPlugin project and call it qpidxarm.
set(AMQP_WCF_DIR ${qpid-cpp_SOURCE_DIR}/../wcf)
set(qpidxarm_SOURCES ${AMQP_WCF_DIR}/src/Apache/Qpid/DtcPlugin/DtcPlugin.cpp)
@@ -956,7 +959,7 @@ if (WIN32)
COMPONENT ${QPID_COMPONENT_CLIENT})
install_pdb (qpidxarm ${QPID_COMPONENT_CLIENT})
endif (EXISTS ${qpidxarm_SOURCES})
-endif (WIN32)
+endif (MSVC)
set (qpidbroker_SOURCES
${mgen_broker_cpp}
@@ -974,6 +977,8 @@ set (qpidbroker_SOURCES
qpid/broker/Queue.cpp
qpid/broker/QueueCleaner.cpp
qpid/broker/QueueListeners.cpp
+ qpid/broker/FifoDistributor.cpp
+ qpid/broker/MessageGroupManager.cpp
qpid/broker/PersistableMessage.cpp
qpid/broker/Bridge.cpp
qpid/broker/Connection.cpp
@@ -990,7 +995,6 @@ set (qpidbroker_SOURCES
qpid/broker/ExchangeRegistry.cpp
qpid/broker/FanOutExchange.cpp
qpid/broker/HeadersExchange.cpp
- qpid/broker/IncompleteMessageList.cpp
qpid/broker/Link.cpp
qpid/broker/LinkRegistry.cpp
qpid/broker/Message.cpp
@@ -1003,7 +1007,7 @@ set (qpidbroker_SOURCES
qpid/broker/QueueEvents.cpp
qpid/broker/QueuePolicy.cpp
qpid/broker/QueueRegistry.cpp
- qpid/broker/RateTracker.cpp
+ qpid/broker/QueueFlowLimit.cpp
qpid/broker/RecoveryManagerImpl.cpp
qpid/broker/RecoveredEnqueue.cpp
qpid/broker/RecoveredDequeue.cpp
@@ -1069,13 +1073,15 @@ endif (CPACK_GENERATOR STREQUAL "NSIS")
# REVISION => Version of underlying implementation.
# Bump if implementation changes but API/ABI doesn't
# AGE => Number of API/ABI versions this is backward compatible with
-set (qmf_version 1.0.0)
+set (qmf_version 2.0.0)
+set (qmf2_version 1.0.0)
set (qmfengine_version 1.0.0)
set (qmf_SOURCES
qpid/agent/ManagementAgentImpl.cpp
qpid/agent/ManagementAgentImpl.h
)
+
add_msvc_version (qmf library dll)
add_library (qmf SHARED ${qmf_SOURCES})
target_link_libraries (qmf qpidclient)
@@ -1086,6 +1092,88 @@ install (TARGETS qmf OPTIONAL
COMPONENT ${QPID_COMPONENT_QMF})
install_pdb (qmf ${QPID_COMPONENT_QMF})
+if(NOT WIN32)
+ set (qmf2_HEADERS
+ ../include/qmf/AgentEvent.h
+ ../include/qmf/Agent.h
+ ../include/qmf/AgentSession.h
+ ../include/qmf/ConsoleEvent.h
+ ../include/qmf/ConsoleSession.h
+ ../include/qmf/DataAddr.h
+ ../include/qmf/Data.h
+ ../include/qmf/exceptions.h
+ ../include/qmf/Handle.h
+ ../include/qmf/ImportExport.h
+ ../include/qmf/posix/EventNotifier.h
+ ../include/qmf/Query.h
+ ../include/qmf/Schema.h
+ ../include/qmf/SchemaId.h
+ ../include/qmf/SchemaMethod.h
+ ../include/qmf/SchemaProperty.h
+ ../include/qmf/SchemaTypes.h
+ ../include/qmf/Subscription.h
+ )
+
+ set (qmf2_SOURCES
+ ${qmf2_HEADERS}
+ qmf/agentCapability.h
+ qmf/Agent.cpp
+ qmf/AgentEvent.cpp
+ qmf/AgentEventImpl.h
+ qmf/AgentImpl.h
+ qmf/AgentSession.cpp
+ qmf/AgentSubscription.cpp
+ qmf/AgentSubscription.h
+ qmf/ConsoleEvent.cpp
+ qmf/ConsoleEventImpl.h
+ qmf/ConsoleSession.cpp
+ qmf/ConsoleSessionImpl.h
+ qmf/constants.cpp
+ qmf/constants.h
+ qmf/DataAddr.cpp
+ qmf/DataAddrImpl.h
+ qmf/Data.cpp
+ qmf/DataImpl.h
+ qmf/EventNotifierImpl.h
+ qmf/EventNotifierImpl.cpp
+ qmf/PosixEventNotifier.cpp
+ qmf/PosixEventNotifierImpl.cpp
+ qmf/exceptions.cpp
+ qmf/Expression.cpp
+ qmf/Expression.h
+ qmf/Hash.cpp
+ qmf/Hash.h
+ qmf/PrivateImplRef.h
+ qmf/Query.cpp
+ qmf/QueryImpl.h
+ qmf/Schema.cpp
+ qmf/SchemaCache.cpp
+ qmf/SchemaCache.h
+ qmf/SchemaId.cpp
+ qmf/SchemaIdImpl.h
+ qmf/SchemaImpl.h
+ qmf/SchemaMethod.cpp
+ qmf/SchemaMethodImpl.h
+ qmf/SchemaProperty.cpp
+ qmf/SchemaPropertyImpl.h
+ qmf/Subscription.cpp
+ qmf/SubscriptionImpl.h
+ )
+
+ add_msvc_version (qmf2 library dll)
+ add_library (qmf2 SHARED ${qmf2_SOURCES})
+ target_link_libraries (qmf2 qpidmessaging qpidtypes qpidclient qpidcommon)
+ set_target_properties (qmf2 PROPERTIES
+ VERSION ${qmf2_version})
+ install (TARGETS qmf2 OPTIONAL
+ DESTINATION ${QPID_INSTALL_LIBDIR}
+ COMPONENT ${QPID_COMPONENT_QMF})
+ install (FILES ${qmf2_HEADERS}
+ DESTINATION ${QPID_INSTALL_INCLUDEDIR}/qmf
+ COMPONENT ${QPID_COMPONENT_QMF})
+ install_pdb (qmf2 ${QPID_COMPONENT_QMF})
+endif (NOT WIN32)
+
set (qmfengine_SOURCES
qmf/engine/Agent.cpp
qmf/engine/BrokerProxyImpl.cpp
diff --git a/cpp/src/CMakeWinVersions.cmake b/cpp/src/CMakeWinVersions.cmake
index 9bffd2ba0e..0bac7cab47 100644
--- a/cpp/src/CMakeWinVersions.cmake
+++ b/cpp/src/CMakeWinVersions.cmake
@@ -34,11 +34,11 @@
# set ("winver_PACKAGE_NAME" "qpid-cpp")
# set ("winver_DESCRIPTION_SUMMARY" "Apache Qpid C++")
# set ("winver_FILE_VERSION_N1" "0")
-# set ("winver_FILE_VERSION_N2" "9")
+# set ("winver_FILE_VERSION_N2" "11")
# set ("winver_FILE_VERSION_N3" "0")
# set ("winver_FILE_VERSION_N4" "0")
# set ("winver_PRODUCT_VERSION_N1" "0")
-# set ("winver_PRODUCT_VERSION_N2" "9")
+# set ("winver_PRODUCT_VERSION_N2" "11")
# set ("winver_PRODUCT_VERSION_N3" "0")
# set ("winver_PRODUCT_VERSION_N4" "0")
# set ("winver_LEGAL_COPYRIGHT" "")
@@ -46,10 +46,10 @@
#
# Specification of per-project settings:
#
-# set ("winver_${projectName}_FileVersionBinary" "0,9,0,0")
-# set ("winver_${projectName}_ProductVersionBinary" "0,9,0,0")
-# set ("winver_${projectName}_FileVersionString" "0, 9, 0, 0")
-# set ("winver_${projectName}_ProductVersionString" "0, 9, 0, 0")
+# set ("winver_${projectName}_FileVersionBinary" "0,11,0,0")
+# set ("winver_${projectName}_ProductVersionBinary" "0,11,0,0")
+# set ("winver_${projectName}_FileVersionString" "0, 11, 0, 0")
+# set ("winver_${projectName}_ProductVersionString" "0, 11, 0, 0")
# set ("winver_${projectName}_FileDescription" "qpid-cpp-qpidcommon Library")
# set ("winver_${projectName}_LegalCopyright" "")
# set ("winver_${projectName}_InternalName" "qpidcommon")
diff --git a/cpp/src/Makefile.am b/cpp/src/Makefile.am
index 2cd6ad462f..6230a8f6f6 100644
--- a/cpp/src/Makefile.am
+++ b/cpp/src/Makefile.am
@@ -37,6 +37,7 @@ windows_dist = \
qpid/sys/windows/IOHandle.cpp \
qpid/sys/windows/IoHandlePrivate.h \
qpid/sys/windows/LockFile.cpp \
+ qpid/sys/windows/mingw32_compat.h \
qpid/sys/windows/PollableCondition.cpp \
qpid/sys/windows/PipeHandle.cpp \
../include/qpid/sys/windows/Mutex.h \
@@ -88,7 +89,7 @@ rgen_cmd=ruby -I $(rgen_dir) $(rgen_dir)/generate . ../include $(specs) all
$(rgen_srcs) $(srcdir)/rubygen.mk: rgen.timestamp
rgen.timestamp: $(rgen_generator) $(specs)
- $(rgen_cmd) $(srcdir)/rubygen.mk; touch $@
+ $(rgen_cmd) $(srcdir)/rubygen.mk && touch $@
$(rgen_generator):
# The CMake version is needed for dist
@@ -127,14 +128,14 @@ qpidexec_SCRIPTS =
qpidtestdir = $(qpidexecdir)/tests
qpidtest_PROGRAMS =
qpidtest_SCRIPTS =
-tmoduledir = $(libdir)/qpid/tests
-tmodule_LTLIBRARIES=
+tmoduleexecdir = $(libdir)/qpid/tests
+tmoduleexec_LTLIBRARIES=
AM_CXXFLAGS += -DBOOST_FILESYSTEM_VERSION=2
## Automake macros to build libraries and executables.
-qpidd_CXXFLAGS = $(AM_CXXFLAGS) -DQPIDD_MODULE_DIR=\"$(dmoduledir)\" -DQPIDD_CONF_FILE=\"$(sysconfdir)/qpidd.conf\"
-libqpidclient_la_CXXFLAGS = $(AM_CXXFLAGS) -DQPIDC_MODULE_DIR=\"$(cmoduledir)\" -DQPIDC_CONF_FILE=\"$(confdir)/qpidc.conf\"
+qpidd_CXXFLAGS = $(AM_CXXFLAGS) -DQPIDD_MODULE_DIR=\"$(dmoduleexecdir)\" -DQPIDD_CONF_FILE=\"$(sysconfdir)/qpidd.conf\"
+libqpidclient_la_CXXFLAGS = $(AM_CXXFLAGS) -DQPIDC_MODULE_DIR=\"$(cmoduleexecdir)\" -DQPIDC_CONF_FILE=\"$(confdir)/qpidc.conf\"
qpidd_LDADD = \
libqpidbroker.la \
@@ -176,7 +177,7 @@ nobase_include_HEADERS += \
../include/qpid/sys/posix/Time.h \
../include/qpid/sys/posix/check.h
-if HAVE_EPOLL
+if HAVE_EPOLL
poller = qpid/sys/epoll/EpollPoller.cpp
endif
@@ -195,15 +196,15 @@ libqpidcommon_la_SOURCES += $(poller) $(systeminfo)
posix_broker_src = \
qpid/broker/posix/BrokerDefaults.cpp
-lib_LTLIBRARIES = libqpidtypes.la libqpidcommon.la libqpidbroker.la libqpidclient.la libqpidmessaging.la
+lib_LTLIBRARIES = libqpidtypes.la libqpidcommon.la libqpidbroker.la libqpidclient.la libqpidmessaging.la
# Definitions for client and daemon plugins
PLUGINLDFLAGS=-no-undefined -module -avoid-version
confdir=$(sysconfdir)/qpid
-dmoduledir=$(libdir)/qpid/daemon
-cmoduledir=$(libdir)/qpid/client
-dmodule_LTLIBRARIES =
-cmodule_LTLIBRARIES =
+dmoduleexecdir=$(libdir)/qpid/daemon
+cmoduleexecdir=$(libdir)/qpid/client
+dmoduleexec_LTLIBRARIES =
+cmoduleexec_LTLIBRARIES =
include cluster.mk
include acl.mk
@@ -245,7 +246,7 @@ rdma_la_LIBADD = \
rdma_la_LDFLAGS = $(PLUGINLDFLAGS)
rdma_la_CXXFLAGS = \
$(AM_CXXFLAGS) -Wno-missing-field-initializers
-dmodule_LTLIBRARIES += \
+dmoduleexec_LTLIBRARIES += \
rdma.la
rdmaconnector_la_SOURCES = \
@@ -257,7 +258,7 @@ rdmaconnector_la_LIBADD = \
rdmaconnector_la_LDFLAGS = $(PLUGINLDFLAGS)
rdmaconnector_la_CXXFLAGS = \
$(AM_CXXFLAGS) -Wno-missing-field-initializers
-cmodule_LTLIBRARIES += \
+cmoduleexec_LTLIBRARIES += \
rdmaconnector.la
# RDMA test/sample programs
@@ -332,6 +333,7 @@ libqpidcommon_la_SOURCES += \
qpid/Address.cpp \
qpid/DataDir.cpp \
qpid/DataDir.h \
+ qpid/DisableExceptionLogging.h \
qpid/Exception.cpp \
qpid/Modules.cpp \
qpid/Modules.h \
@@ -341,6 +343,7 @@ libqpidcommon_la_SOURCES += \
qpid/RefCounted.h \
qpid/RefCountedBuffer.cpp \
qpid/RefCountedBuffer.h \
+ qpid/BufferRef.h \
qpid/Sasl.h \
qpid/SaslFactory.cpp \
qpid/SaslFactory.h \
@@ -561,8 +564,7 @@ libqpidbroker_la_SOURCES = \
qpid/broker/HandlerImpl.h \
qpid/broker/HeadersExchange.cpp \
qpid/broker/HeadersExchange.h \
- qpid/broker/IncompleteMessageList.cpp \
- qpid/broker/IncompleteMessageList.h \
+ qpid/broker/AsyncCompletion.h \
qpid/broker/LegacyLVQ.h \
qpid/broker/LegacyLVQ.cpp \
qpid/broker/Link.cpp \
@@ -612,9 +614,9 @@ libqpidbroker_la_SOURCES = \
qpid/broker/QueueRegistry.cpp \
qpid/broker/QueueRegistry.h \
qpid/broker/QueuedMessage.h \
+ qpid/broker/QueueFlowLimit.h \
+ qpid/broker/QueueFlowLimit.cpp \
qpid/broker/RateFlowcontrol.h \
- qpid/broker/RateTracker.cpp \
- qpid/broker/RateTracker.h \
qpid/broker/RecoverableConfig.h \
qpid/broker/RecoverableExchange.h \
qpid/broker/RecoverableMessage.h \
@@ -651,6 +653,7 @@ libqpidbroker_la_SOURCES = \
qpid/broker/SessionState.h \
qpid/broker/SignalHandler.cpp \
qpid/broker/SignalHandler.h \
+ qpid/broker/StatefulQueueObserver.h \
qpid/broker/System.cpp \
qpid/broker/System.h \
qpid/broker/ThresholdAlerts.cpp \
@@ -668,6 +671,11 @@ libqpidbroker_la_SOURCES = \
qpid/broker/TxPublish.h \
qpid/broker/Vhost.cpp \
qpid/broker/Vhost.h \
+ qpid/broker/MessageDistributor.h \
+ qpid/broker/FifoDistributor.h \
+ qpid/broker/FifoDistributor.cpp \
+ qpid/broker/MessageGroupManager.cpp \
+ qpid/broker/MessageGroupManager.h \
qpid/management/ManagementAgent.cpp \
qpid/management/ManagementAgent.h \
qpid/management/ManagementDirectExchange.cpp \
@@ -739,7 +747,7 @@ libqpidclient_la_SOURCES = \
QPIDCLIENT_VERSION_INFO = 2:0:0
libqpidclient_la_LDFLAGS = -version-info $(QPIDCLIENT_VERSION_INFO)
-libqpidtypes_la_libadd=-luuid
+libqpidtypes_la_LIBADD= -luuid
libqpidtypes_la_SOURCES= \
qpid/types/Exception.cpp \
qpid/types/Uuid.cpp \
@@ -786,9 +794,7 @@ libqpidmessaging_la_SOURCES = \
qpid/client/amqp0_10/SessionImpl.h \
qpid/client/amqp0_10/SessionImpl.cpp \
qpid/client/amqp0_10/SenderImpl.h \
- qpid/client/amqp0_10/SenderImpl.cpp \
- qpid/client/amqp0_10/SimpleUrlParser.h \
- qpid/client/amqp0_10/SimpleUrlParser.cpp
+ qpid/client/amqp0_10/SenderImpl.cpp
QPIDMESSAGING_VERSION_INFO = 2:0:0
libqpidmessaging_la_LDFLAGS = -version-info $(QPIDMESSAGING_VERSION_INFO)
@@ -801,6 +807,7 @@ nobase_include_HEADERS += \
../include/qpid/Address.h \
../include/qpid/CommonImportExport.h \
../include/qpid/Exception.h \
+ ../include/qpid/ImportExport.h \
../include/qpid/InlineAllocator.h \
../include/qpid/InlineVector.h \
../include/qpid/Msg.h \
@@ -883,14 +890,10 @@ nobase_include_HEADERS += \
../include/qpid/types/Variant.h \
../include/qpid/types/ImportExport.h
-# Force build of qpidd during dist phase so help2man will work.
-dist-hook: $(BUILT_SOURCES)
- $(MAKE) qpidd
-
# Create the default data directory
install-data-local:
$(mkinstalldirs) $(DESTDIR)/$(localstatedir)/lib/qpidd
-# Support for pkg-config
+# Support for pkg-config
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = qpid.pc
diff --git a/cpp/src/acl.mk b/cpp/src/acl.mk
index bcd1d88335..b8e2ff0e13 100644
--- a/cpp/src/acl.mk
+++ b/cpp/src/acl.mk
@@ -18,8 +18,8 @@
#
#
# acl library makefile fragment, to be included in Makefile.am
-#
-dmodule_LTLIBRARIES += acl.la
+#
+dmoduleexec_LTLIBRARIES += acl.la
acl_la_SOURCES = \
qpid/acl/Acl.cpp \
diff --git a/cpp/src/cluster.mk b/cpp/src/cluster.mk
index a791b2d41a..3ce4ce25b3 100644
--- a/cpp/src/cluster.mk
+++ b/cpp/src/cluster.mk
@@ -18,7 +18,7 @@
#
#
# Cluster library makefile fragment, to be included in Makefile.am
-#
+#
# Optional CMAN support
@@ -34,7 +34,7 @@ endif
if HAVE_LIBCPG
-dmodule_LTLIBRARIES += cluster.la
+dmoduleexec_LTLIBRARIES += cluster.la
cluster_la_SOURCES = \
$(CMAN_SOURCES) \
@@ -102,7 +102,7 @@ cluster_la_CXXFLAGS = $(AM_CXXFLAGS) -fno-strict-aliasing
cluster_la_LDFLAGS = $(PLUGINLDFLAGS)
# The watchdog plugin and helper executable
-dmodule_LTLIBRARIES += watchdog.la
+dmoduleexec_LTLIBRARIES += watchdog.la
watchdog_la_SOURCES = qpid/cluster/WatchDogPlugin.cpp
watchdog_la_LIBADD = libqpidbroker.la
watchdog_la_LDFLAGS = $(PLUGINLDFLAGS)
diff --git a/cpp/src/posix/QpiddBroker.cpp b/cpp/src/posix/QpiddBroker.cpp
index 86504ba7fc..1cebcfc3ac 100644
--- a/cpp/src/posix/QpiddBroker.cpp
+++ b/cpp/src/posix/QpiddBroker.cpp
@@ -138,6 +138,9 @@ struct QpiddDaemon : public Daemon {
brokerPtr->accept();
uint16_t port=brokerPtr->getPort(options->daemon.transport);
ready(port); // Notify parent.
+ if (options->parent->broker.enableMgmt && (options->parent->broker.port == 0 || options->daemon.transport != TCP)) {
+ dynamic_cast<qmf::org::apache::qpid::broker::Broker*>(brokerPtr->GetManagementObject())->set_port(port);
+ }
brokerPtr->run();
}
};
@@ -182,8 +185,13 @@ int QpiddBroker::execute (QpiddOptions *options) {
boost::intrusive_ptr<Broker> brokerPtr(new Broker(options->broker));
ScopedSetBroker ssb(brokerPtr);
brokerPtr->accept();
- if (options->broker.port == 0 || myOptions->daemon.transport != TCP)
- cout << uint16_t(brokerPtr->getPort(myOptions->daemon.transport)) << endl;
+ if (options->broker.port == 0 || myOptions->daemon.transport != TCP) {
+ uint16_t port = brokerPtr->getPort(myOptions->daemon.transport);
+ cout << port << endl;
+ if (options->broker.enableMgmt) {
+ dynamic_cast<qmf::org::apache::qpid::broker::Broker*>(brokerPtr->GetManagementObject())->set_port(port);
+ }
+ }
brokerPtr->run();
}
return 0;
diff --git a/cpp/src/qmf.mk b/cpp/src/qmf.mk
index f3462f1a93..3b6583bfaf 100644
--- a/cpp/src/qmf.mk
+++ b/cpp/src/qmf.mk
@@ -43,6 +43,7 @@ QMF2_API = \
../include/qmf/ConsoleSession.h \
../include/qmf/DataAddr.h \
../include/qmf/Data.h \
+ ../include/qmf/posix/EventNotifier.h \
../include/qmf/exceptions.h \
../include/qmf/Handle.h \
../include/qmf/ImportExport.h \
@@ -92,6 +93,7 @@ libqmf2_la_SOURCES = \
qmf/AgentEventImpl.h \
qmf/AgentImpl.h \
qmf/AgentSession.cpp \
+ qmf/AgentSessionImpl.h \
qmf/AgentSubscription.cpp \
qmf/AgentSubscription.h \
qmf/ConsoleEvent.cpp \
@@ -104,17 +106,22 @@ libqmf2_la_SOURCES = \
qmf/DataAddrImpl.h \
qmf/Data.cpp \
qmf/DataImpl.h \
+ qmf/EventNotifierImpl.cpp \
+ qmf/EventNotifierImpl.h \
qmf/exceptions.cpp \
qmf/Expression.cpp \
qmf/Expression.h \
qmf/Hash.cpp \
qmf/Hash.h \
+ qmf/PosixEventNotifier.cpp \
+ qmf/PosixEventNotifierImpl.cpp \
+ qmf/PosixEventNotifierImpl.h \
qmf/PrivateImplRef.h \
qmf/Query.cpp \
qmf/QueryImpl.h \
- qmf/Schema.cpp \
qmf/SchemaCache.cpp \
qmf/SchemaCache.h \
+ qmf/Schema.cpp \
qmf/SchemaId.cpp \
qmf/SchemaIdImpl.h \
qmf/SchemaImpl.h \
diff --git a/cpp/src/qmf/Agent.cpp b/cpp/src/qmf/Agent.cpp
index 915f2a1c88..684f8e4fba 100644
--- a/cpp/src/qmf/Agent.cpp
+++ b/cpp/src/qmf/Agent.cpp
@@ -72,7 +72,7 @@ Schema Agent::getSchema(const SchemaId& s, Duration t) { return impl->getSchema(
AgentImpl::AgentImpl(const std::string& n, uint32_t e, ConsoleSessionImpl& s) :
name(n), directSubject(n), epoch(e), session(s), touched(true), untouchedCount(0), capability(0),
- sender(session.directSender), nextCorrelator(1), schemaCache(s.schemaCache)
+ sender(session.directSender), schemaCache(s.schemaCache)
{
}
@@ -102,12 +102,11 @@ const Variant& AgentImpl::getAttribute(const string& k) const
ConsoleEvent AgentImpl::query(const Query& query, Duration timeout)
{
boost::shared_ptr<SyncContext> context(new SyncContext());
- uint32_t correlator;
+ uint32_t correlator(session.correlator());
ConsoleEvent result;
{
qpid::sys::Mutex::ScopedLock l(lock);
- correlator = nextCorrelator++;
contextMap[correlator] = context;
}
try {
@@ -151,12 +150,7 @@ ConsoleEvent AgentImpl::query(const string& text, Duration timeout)
uint32_t AgentImpl::queryAsync(const Query& query)
{
- uint32_t correlator;
-
- {
- qpid::sys::Mutex::ScopedLock l(lock);
- correlator = nextCorrelator++;
- }
+ uint32_t correlator(session.correlator());
sendQuery(query, correlator);
return correlator;
@@ -172,12 +166,11 @@ uint32_t AgentImpl::queryAsync(const string& text)
ConsoleEvent AgentImpl::callMethod(const string& method, const Variant::Map& args, const DataAddr& addr, Duration timeout)
{
boost::shared_ptr<SyncContext> context(new SyncContext());
- uint32_t correlator;
+ uint32_t correlator(session.correlator());
ConsoleEvent result;
{
qpid::sys::Mutex::ScopedLock l(lock);
- correlator = nextCorrelator++;
contextMap[correlator] = context;
}
try {
@@ -213,12 +206,7 @@ ConsoleEvent AgentImpl::callMethod(const string& method, const Variant::Map& arg
uint32_t AgentImpl::callMethodAsync(const string& method, const Variant::Map& args, const DataAddr& addr)
{
- uint32_t correlator;
-
- {
- qpid::sys::Mutex::ScopedLock l(lock);
- correlator = nextCorrelator++;
- }
+ uint32_t correlator(session.correlator());
sendMethod(method, args, addr, correlator);
return correlator;
@@ -596,12 +584,7 @@ void AgentImpl::sendMethod(const string& method, const Variant::Map& args, const
void AgentImpl::sendSchemaRequest(const SchemaId& id)
{
- uint32_t correlator;
-
- {
- qpid::sys::Mutex::ScopedLock l(lock);
- correlator = nextCorrelator++;
- }
+ uint32_t correlator(session.correlator());
if (capability >= AGENT_CAPABILITY_V2_SCHEMA) {
Query query(QUERY_SCHEMA, id);
diff --git a/cpp/src/qmf/AgentImpl.h b/cpp/src/qmf/AgentImpl.h
index 7fa4f4373a..09754a3a7e 100644
--- a/cpp/src/qmf/AgentImpl.h
+++ b/cpp/src/qmf/AgentImpl.h
@@ -99,7 +99,6 @@ namespace qmf {
uint32_t capability;
qpid::messaging::Sender sender;
qpid::types::Variant::Map attributes;
- uint32_t nextCorrelator;
std::map<uint32_t, boost::shared_ptr<SyncContext> > contextMap;
boost::shared_ptr<SchemaCache> schemaCache;
mutable std::set<std::string> packageSet;
diff --git a/cpp/src/qmf/AgentSession.cpp b/cpp/src/qmf/AgentSession.cpp
index 4c5a72a467..251c25fd44 100644
--- a/cpp/src/qmf/AgentSession.cpp
+++ b/cpp/src/qmf/AgentSession.cpp
@@ -19,132 +19,7 @@
*
*/
-#include "qpid/RefCounted.h"
-#include "qmf/PrivateImplRef.h"
-#include "qmf/exceptions.h"
-#include "qmf/AgentSession.h"
-#include "qmf/AgentEventImpl.h"
-#include "qmf/SchemaIdImpl.h"
-#include "qmf/SchemaImpl.h"
-#include "qmf/DataAddrImpl.h"
-#include "qmf/DataImpl.h"
-#include "qmf/QueryImpl.h"
-#include "qmf/agentCapability.h"
-#include "qmf/constants.h"
-#include "qpid/sys/Mutex.h"
-#include "qpid/sys/Condition.h"
-#include "qpid/sys/Thread.h"
-#include "qpid/sys/Runnable.h"
-#include "qpid/log/Statement.h"
-#include "qpid/messaging/Connection.h"
-#include "qpid/messaging/Session.h"
-#include "qpid/messaging/Receiver.h"
-#include "qpid/messaging/Sender.h"
-#include "qpid/messaging/Message.h"
-#include "qpid/messaging/AddressParser.h"
-#include "qpid/management/Buffer.h"
-#include <queue>
-#include <map>
-#include <set>
-#include <iostream>
-#include <memory>
-
-using namespace std;
-using namespace qpid::messaging;
-using namespace qmf;
-using qpid::types::Variant;
-
-namespace qmf {
- class AgentSessionImpl : public virtual qpid::RefCounted, public qpid::sys::Runnable {
- public:
- ~AgentSessionImpl();
-
- //
- // 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; }
- void open();
- void close();
- bool nextEvent(AgentEvent& e, Duration t);
-
- void registerSchema(Schema& s);
- DataAddr addData(Data& d, const 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 raiseException(AgentEvent& e, const Data& d);
- void response(AgentEvent& e, const Data& d);
- void complete(AgentEvent& e);
- void methodSuccess(AgentEvent& e);
- void raiseEvent(const Data& d);
- void raiseEvent(const Data& d, int s);
-
- private:
- typedef map<DataAddr, Data, DataAddrCompare> DataIndex;
- typedef 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;
- bool opened;
- queue<AgentEvent> eventQueue;
- qpid::sys::Thread* thread;
- bool threadCanceled;
- uint32_t bootSequence;
- uint32_t interval;
- uint64_t lastHeartbeat;
- uint64_t lastVisit;
- bool forceHeartbeat;
- bool externalStorage;
- bool autoAllowQueries;
- bool autoAllowMethods;
- uint32_t maxSubscriptions;
- uint32_t minSubInterval;
- uint32_t subLifetime;
- bool publicEvents;
- bool listenOnDirect;
- bool strictSecurity;
- uint64_t schemaUpdateTime;
- string directBase;
- string topicBase;
-
- SchemaMap schemata;
- DataIndex globalIndex;
- map<SchemaId, DataIndex, SchemaIdCompareNoHash> schemaIndex;
-
- void checkOpen();
- void setAgentName();
- void enqueueEvent(const AgentEvent&);
- 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 handleSchemaRequest(AgentEvent&);
- void handleV1SchemaRequest(qpid::management::Buffer&, uint32_t, const Message&);
- void dispatch(Message);
- void sendHeartbeat();
- void send(Message, const Address&);
- void flushResponses(AgentEvent&, bool);
- void periodicProcessing(uint64_t);
- void run();
- };
-}
-
-typedef qmf::PrivateImplRef<AgentSession> PI;
+#include "qmf/AgentSessionImpl.h"
AgentSession::AgentSession(AgentSessionImpl* impl) { PI::ctor(*this, impl); }
AgentSession::AgentSession(const AgentSession& s) : qmf::Handle<AgentSessionImpl>() { PI::copy(*this, s); }
@@ -161,6 +36,7 @@ const string& AgentSession::getName() const { return impl->getName(); }
void AgentSession::open() { impl->open(); }
void AgentSession::close() { impl->close(); }
bool AgentSession::nextEvent(AgentEvent& e, Duration t) { return impl->nextEvent(e, t); }
+int AgentSession::pendingEvents() const { return impl->pendingEvents(); }
void AgentSession::registerSchema(Schema& s) { impl->registerSchema(s); }
DataAddr AgentSession::addData(Data& d, const string& n, bool p) { return impl->addData(d, n, p); }
void AgentSession::delData(const DataAddr& a) { impl->delData(a); }
@@ -179,11 +55,11 @@ void AgentSession::raiseEvent(const Data& d, int s) { impl->raiseEvent(d, s); }
//========================================================================================
AgentSessionImpl::AgentSessionImpl(Connection& c, const string& options) :
- connection(c), domain("default"), opened(false), thread(0), threadCanceled(false),
+ connection(c), domain("default"), opened(false), eventNotifier(0), thread(0), threadCanceled(false),
bootSequence(1), interval(60), lastHeartbeat(0), lastVisit(0), forceHeartbeat(false),
externalStorage(false), autoAllowQueries(true), autoAllowMethods(true),
maxSubscriptions(64), minSubInterval(3000), subLifetime(300), publicEvents(true),
- listenOnDirect(true), strictSecurity(false),
+ listenOnDirect(true), strictSecurity(false), maxThreadWaitTime(5),
schemaUpdateTime(uint64_t(qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now())))
{
//
@@ -244,7 +120,14 @@ AgentSessionImpl::AgentSessionImpl(Connection& c, const string& options) :
iter = optMap.find("strict-security");
if (iter != optMap.end())
strictSecurity = iter->second.asBool();
+
+ iter = optMap.find("max-thread-wait-time");
+ if (iter != optMap.end())
+ maxThreadWaitTime = iter->second.asUint32();
}
+
+ if (maxThreadWaitTime > interval)
+ maxThreadWaitTime = interval;
}
@@ -252,6 +135,11 @@ AgentSessionImpl::~AgentSessionImpl()
{
if (opened)
close();
+
+ if (thread) {
+ thread->join();
+ delete thread;
+ }
}
@@ -260,6 +148,12 @@ void AgentSessionImpl::open()
if (opened)
throw QmfException("The session is already open");
+ // If the thread exists, join and delete it before creating a new one.
+ if (thread) {
+ thread->join();
+ delete thread;
+ }
+
const string addrArgs(";{create:never,node:{type:topic}}");
const string routableAddr("direct-agent.route." + qpid::types::Uuid(true).str());
attributes["_direct_subject"] = routableAddr;
@@ -297,34 +191,47 @@ void AgentSessionImpl::open()
}
-void AgentSessionImpl::close()
+void AgentSessionImpl::closeAsync()
{
if (!opened)
return;
- // Stop and join the receiver thread
+ // Stop the receiver thread. Don't join it until the destructor is called or open() is called.
threadCanceled = true;
- thread->join();
- delete thread;
-
- // Close the AMQP session
- session.close();
opened = false;
}
+void AgentSessionImpl::close()
+{
+ closeAsync();
+
+ if (thread) {
+ thread->join();
+ delete thread;
+ thread = 0;
+ }
+}
+
+
bool AgentSessionImpl::nextEvent(AgentEvent& event, Duration timeout)
{
uint64_t milliseconds = timeout.getMilliseconds();
qpid::sys::Mutex::ScopedLock l(lock);
- if (eventQueue.empty())
- cond.wait(lock, qpid::sys::AbsTime(qpid::sys::now(),
- qpid::sys::Duration(milliseconds * qpid::sys::TIME_MSEC)));
+ if (eventQueue.empty() && milliseconds > 0) {
+ int64_t nsecs(qpid::sys::TIME_INFINITE);
+ if ((uint64_t)(nsecs / 1000000) > milliseconds)
+ nsecs = (int64_t) milliseconds * 1000000;
+ qpid::sys::Duration then(nsecs);
+ cond.wait(lock, qpid::sys::AbsTime(qpid::sys::now(), then));
+ }
if (!eventQueue.empty()) {
event = eventQueue.front();
eventQueue.pop();
+ if (eventQueue.empty())
+ alertEventNotifierLH(false);
return true;
}
@@ -332,6 +239,26 @@ bool AgentSessionImpl::nextEvent(AgentEvent& event, Duration timeout)
}
+int AgentSessionImpl::pendingEvents() const
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return eventQueue.size();
+}
+
+
+void AgentSessionImpl::setEventNotifier(EventNotifierImpl* notifier)
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ eventNotifier = notifier;
+}
+
+EventNotifierImpl* AgentSessionImpl::getEventNotifier() const
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return eventNotifier;
+}
+
+
void AgentSessionImpl::registerSchema(Schema& schema)
{
if (!schema.isFinalized())
@@ -587,8 +514,10 @@ void AgentSessionImpl::enqueueEvent(const AgentEvent& event)
qpid::sys::Mutex::ScopedLock l(lock);
bool notify = eventQueue.empty();
eventQueue.push(event);
- if (notify)
+ if (notify) {
cond.notify();
+ alertEventNotifierLH(true);
+ }
}
@@ -1032,6 +961,13 @@ void AgentSessionImpl::periodicProcessing(uint64_t seconds)
}
+void AgentSessionImpl::alertEventNotifierLH(bool readable)
+{
+ if (eventNotifier)
+ eventNotifier->setReadable(readable);
+}
+
+
void AgentSessionImpl::run()
{
QPID_LOG(debug, "AgentSession thread started for agent " << agentName);
@@ -1041,7 +977,7 @@ void AgentSessionImpl::run()
periodicProcessing((uint64_t) qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now()) / qpid::sys::TIME_SEC);
Receiver rx;
- bool valid = session.nextReceiver(rx, Duration::SECOND);
+ bool valid = session.nextReceiver(rx, Duration::SECOND * maxThreadWaitTime);
if (threadCanceled)
break;
if (valid) {
@@ -1058,6 +994,19 @@ void AgentSessionImpl::run()
enqueueEvent(AgentEvent(new AgentEventImpl(AGENT_THREAD_FAILED)));
}
+ session.close();
QPID_LOG(debug, "AgentSession thread exiting for agent " << agentName);
}
+
+AgentSessionImpl& AgentSessionImplAccess::get(AgentSession& session)
+{
+ return *session.impl;
+}
+
+
+const AgentSessionImpl& AgentSessionImplAccess::get(const AgentSession& session)
+{
+ return *session.impl;
+}
+
diff --git a/cpp/src/qmf/AgentSessionImpl.h b/cpp/src/qmf/AgentSessionImpl.h
new file mode 100644
index 0000000000..ae512a4054
--- /dev/null
+++ b/cpp/src/qmf/AgentSessionImpl.h
@@ -0,0 +1,175 @@
+#ifndef __QMF_AGENT_SESSION_IMPL_H
+#define __QMF_AGENT_SESSION_IMPL_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/RefCounted.h"
+#include "qmf/PrivateImplRef.h"
+#include "qmf/exceptions.h"
+#include "qmf/AgentSession.h"
+#include "qmf/AgentEventImpl.h"
+#include "qmf/EventNotifierImpl.h"
+#include "qpid/messaging/Connection.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Condition.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/log/Statement.h"
+#include "qpid/messaging/Connection.h"
+#include "qpid/messaging/Session.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/AddressParser.h"
+#include "qpid/management/Buffer.h"
+#include "qpid/RefCounted.h"
+#include "qmf/PrivateImplRef.h"
+#include "qmf/AgentSession.h"
+#include "qmf/exceptions.h"
+#include "qmf/AgentSession.h"
+#include "qmf/SchemaIdImpl.h"
+#include "qmf/SchemaImpl.h"
+#include "qmf/DataAddrImpl.h"
+#include "qmf/DataImpl.h"
+#include "qmf/QueryImpl.h"
+#include "qmf/agentCapability.h"
+#include "qmf/constants.h"
+
+#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 {
+ class AgentSessionImpl : public virtual qpid::RefCounted, public qpid::sys::Runnable {
+ public:
+ ~AgentSessionImpl();
+
+ //
+ // 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; }
+ void open();
+ void closeAsync();
+ void close();
+ bool nextEvent(AgentEvent& e, 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);
+ void delData(const DataAddr&);
+
+ void authAccept(AgentEvent& e);
+ void authReject(AgentEvent& e, const string& m);
+ void raiseException(AgentEvent& e, const string& s);
+ void raiseException(AgentEvent& e, const Data& d);
+ void response(AgentEvent& e, const Data& d);
+ void complete(AgentEvent& e);
+ void methodSuccess(AgentEvent& e);
+ void raiseEvent(const Data& d);
+ void raiseEvent(const Data& d, int s);
+
+ private:
+ typedef map<DataAddr, Data, DataAddrCompare> DataIndex;
+ typedef 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;
+ bool opened;
+ queue<AgentEvent> eventQueue;
+ EventNotifierImpl* eventNotifier;
+ qpid::sys::Thread* thread;
+ bool threadCanceled;
+ uint32_t bootSequence;
+ uint32_t interval;
+ uint64_t lastHeartbeat;
+ uint64_t lastVisit;
+ bool forceHeartbeat;
+ bool externalStorage;
+ bool autoAllowQueries;
+ bool autoAllowMethods;
+ uint32_t maxSubscriptions;
+ uint32_t minSubInterval;
+ uint32_t subLifetime;
+ bool publicEvents;
+ bool listenOnDirect;
+ bool strictSecurity;
+ uint32_t maxThreadWaitTime;
+ uint64_t schemaUpdateTime;
+ string directBase;
+ string topicBase;
+
+ SchemaMap schemata;
+ DataIndex globalIndex;
+ 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 handleSchemaRequest(AgentEvent&);
+ void handleV1SchemaRequest(qpid::management::Buffer&, uint32_t, const Message&);
+ void dispatch(Message);
+ void sendHeartbeat();
+ void send(Message, const Address&);
+ void flushResponses(AgentEvent&, bool);
+ void periodicProcessing(uint64_t);
+ void run();
+ };
+
+ struct AgentSessionImplAccess {
+ static AgentSessionImpl& get(AgentSession& session);
+ static const AgentSessionImpl& get(const AgentSession& session);
+ };
+}
+
+
+#endif
+
diff --git a/cpp/src/qmf/ConsoleSession.cpp b/cpp/src/qmf/ConsoleSession.cpp
index e12c1152f6..2dfc894c58 100644
--- a/cpp/src/qmf/ConsoleSession.cpp
+++ b/cpp/src/qmf/ConsoleSession.cpp
@@ -54,6 +54,7 @@ void ConsoleSession::setAgentFilter(const string& f) { impl->setAgentFilter(f);
void ConsoleSession::open() { impl->open(); }
void ConsoleSession::close() { impl->close(); }
bool ConsoleSession::nextEvent(ConsoleEvent& e, Duration t) { return impl->nextEvent(e, t); }
+int ConsoleSession::pendingEvents() const { return impl->pendingEvents(); }
uint32_t ConsoleSession::getAgentCount() const { return impl->getAgentCount(); }
Agent ConsoleSession::getAgent(uint32_t i) const { return impl->getAgent(i); }
Agent ConsoleSession::getConnectedBrokerAgent() const { return impl->getConnectedBrokerAgent(); }
@@ -65,9 +66,9 @@ Subscription ConsoleSession::subscribe(const string& q, const string& f, const s
//========================================================================================
ConsoleSessionImpl::ConsoleSessionImpl(Connection& c, const string& options) :
- connection(c), domain("default"), maxAgentAgeMinutes(5),
- opened(false), thread(0), threadCanceled(false), lastVisit(0), lastAgePass(0),
- connectedBrokerInAgentList(false), schemaCache(new SchemaCache())
+ connection(c), domain("default"), maxAgentAgeMinutes(5), listenOnDirect(true), strictSecurity(false), maxThreadWaitTime(5),
+ opened(false), eventNotifier(0), thread(0), threadCanceled(false), lastVisit(0), lastAgePass(0),
+ connectedBrokerInAgentList(false), schemaCache(new SchemaCache()), nextCorrelator(1)
{
if (!options.empty()) {
qpid::messaging::AddressParser parser(options);
@@ -91,7 +92,14 @@ ConsoleSessionImpl::ConsoleSessionImpl(Connection& c, const string& options) :
iter = optMap.find("strict-security");
if (iter != optMap.end())
strictSecurity = iter->second.asBool();
+
+ iter = optMap.find("max-thread-wait-time");
+ if (iter != optMap.end())
+ maxThreadWaitTime = iter->second.asUint32();
}
+
+ if (maxThreadWaitTime > 60)
+ maxThreadWaitTime = 60;
}
@@ -99,6 +107,11 @@ ConsoleSessionImpl::~ConsoleSessionImpl()
{
if (opened)
close();
+
+ if (thread) {
+ thread->join();
+ delete thread;
+ }
}
@@ -153,6 +166,12 @@ void ConsoleSessionImpl::open()
if (opened)
throw QmfException("The session is already open");
+ // If the thread exists, join and delete it before creating a new one.
+ if (thread) {
+ thread->join();
+ delete thread;
+ }
+
// Establish messaging addresses
directBase = "qmf." + domain + ".direct";
topicBase = "qmf." + domain + ".topic";
@@ -181,45 +200,57 @@ void ConsoleSessionImpl::open()
// Start the receiver thread
threadCanceled = false;
+ opened = true;
thread = new qpid::sys::Thread(*this);
// Send an agent_locate to direct address 'broker' to identify the connected-broker-agent.
sendBrokerLocate();
if (agentQuery)
sendAgentLocate();
-
- opened = true;
}
-void ConsoleSessionImpl::close()
+void ConsoleSessionImpl::closeAsync()
{
if (!opened)
throw QmfException("The session is already closed");
- // Stop and join the receiver thread
+ // Stop the receiver thread. Don't join it until the destructor is called or open() is called.
threadCanceled = true;
- thread->join();
- delete thread;
-
- // Close the AMQP session
- session.close();
opened = false;
}
+void ConsoleSessionImpl::close()
+{
+ closeAsync();
+
+ if (thread) {
+ thread->join();
+ delete thread;
+ thread = 0;
+ }
+}
+
+
bool ConsoleSessionImpl::nextEvent(ConsoleEvent& event, Duration timeout)
{
uint64_t milliseconds = timeout.getMilliseconds();
qpid::sys::Mutex::ScopedLock l(lock);
- if (eventQueue.empty())
- cond.wait(lock, qpid::sys::AbsTime(qpid::sys::now(),
- qpid::sys::Duration(milliseconds * qpid::sys::TIME_MSEC)));
+ if (eventQueue.empty() && milliseconds > 0) {
+ int64_t nsecs(qpid::sys::TIME_INFINITE);
+ if ((uint64_t)(nsecs / 1000000) > milliseconds)
+ nsecs = (int64_t) milliseconds * 1000000;
+ qpid::sys::Duration then(nsecs);
+ cond.wait(lock, qpid::sys::AbsTime(qpid::sys::now(), then));
+ }
if (!eventQueue.empty()) {
event = eventQueue.front();
eventQueue.pop();
+ if (eventQueue.empty())
+ alertEventNotifierLH(false);
return true;
}
@@ -227,6 +258,27 @@ bool ConsoleSessionImpl::nextEvent(ConsoleEvent& event, Duration timeout)
}
+int ConsoleSessionImpl::pendingEvents() const
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return eventQueue.size();
+}
+
+
+void ConsoleSessionImpl::setEventNotifier(EventNotifierImpl* notifier)
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ eventNotifier = notifier;
+}
+
+
+EventNotifierImpl* ConsoleSessionImpl::getEventNotifier() const
+{
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return eventNotifier;
+}
+
+
uint32_t ConsoleSessionImpl::getAgentCount() const
{
qpid::sys::Mutex::ScopedLock l(lock);
@@ -268,8 +320,10 @@ void ConsoleSessionImpl::enqueueEventLH(const ConsoleEvent& event)
{
bool notify = eventQueue.empty();
eventQueue.push(event);
- if (notify)
+ if (notify) {
cond.notify();
+ alertEventNotifierLH(true);
+ }
}
@@ -421,7 +475,23 @@ void ConsoleSessionImpl::handleAgentUpdate(const string& agentName, const Varian
iter = content.find("_values");
if (iter == content.end())
return;
- Variant::Map attrs(iter->second.asMap());
+ const Variant::Map& in_attrs(iter->second.asMap());
+ Variant::Map attrs;
+
+ //
+ // Copy the map from the message to "attrs". Translate any old-style
+ // keys to their new key values in the process.
+ //
+ for (iter = in_attrs.begin(); iter != in_attrs.end(); iter++) {
+ if (iter->first == "epoch")
+ attrs[protocol::AGENT_ATTR_EPOCH] = iter->second;
+ else if (iter->first == "timestamp")
+ attrs[protocol::AGENT_ATTR_TIMESTAMP] = iter->second;
+ else if (iter->first == "heartbeat_interval")
+ attrs[protocol::AGENT_ATTR_HEARTBEAT_INTERVAL] = iter->second;
+ else
+ attrs[iter->first] = iter->second;
+ }
iter = attrs.find(protocol::AGENT_ATTR_EPOCH);
if (iter != attrs.end())
@@ -562,6 +632,13 @@ void ConsoleSessionImpl::periodicProcessing(uint64_t seconds)
}
+void ConsoleSessionImpl::alertEventNotifierLH(bool readable)
+{
+ if (eventNotifier)
+ eventNotifier->setReadable(readable);
+}
+
+
void ConsoleSessionImpl::run()
{
QPID_LOG(debug, "ConsoleSession thread started");
@@ -572,7 +649,7 @@ void ConsoleSessionImpl::run()
qpid::sys::TIME_SEC);
Receiver rx;
- bool valid = session.nextReceiver(rx, Duration::SECOND);
+ bool valid = session.nextReceiver(rx, Duration::SECOND * maxThreadWaitTime);
if (threadCanceled)
break;
if (valid) {
@@ -589,6 +666,18 @@ void ConsoleSessionImpl::run()
enqueueEvent(ConsoleEvent(new ConsoleEventImpl(CONSOLE_THREAD_FAILED)));
}
+ session.close();
QPID_LOG(debug, "ConsoleSession thread exiting");
}
+
+ConsoleSessionImpl& ConsoleSessionImplAccess::get(ConsoleSession& session)
+{
+ return *session.impl;
+}
+
+
+const ConsoleSessionImpl& ConsoleSessionImplAccess::get(const ConsoleSession& session)
+{
+ return *session.impl;
+}
diff --git a/cpp/src/qmf/ConsoleSessionImpl.h b/cpp/src/qmf/ConsoleSessionImpl.h
index 675c8bcfb5..e2b30602fa 100644
--- a/cpp/src/qmf/ConsoleSessionImpl.h
+++ b/cpp/src/qmf/ConsoleSessionImpl.h
@@ -27,6 +27,7 @@
#include "qmf/SchemaId.h"
#include "qmf/Schema.h"
#include "qmf/ConsoleEventImpl.h"
+#include "qmf/EventNotifierImpl.h"
#include "qmf/SchemaCache.h"
#include "qmf/Query.h"
#include "qpid/sys/Mutex.h"
@@ -41,9 +42,13 @@
#include "qpid/messaging/Address.h"
#include "qpid/management/Buffer.h"
#include "qpid/types/Variant.h"
+
+#include <boost/shared_ptr.hpp>
#include <map>
#include <queue>
+using namespace std;
+
namespace qmf {
class ConsoleSessionImpl : public virtual qpid::RefCounted, public qpid::sys::Runnable {
public:
@@ -56,8 +61,14 @@ namespace qmf {
void setDomain(const std::string& d) { domain = d; }
void setAgentFilter(const std::string& f);
void open();
+ void closeAsync();
void close();
bool nextEvent(ConsoleEvent& e, qpid::messaging::Duration t);
+ int pendingEvents() const;
+
+ void setEventNotifier(EventNotifierImpl* notifier);
+ EventNotifierImpl* getEventNotifier() const;
+
uint32_t getAgentCount() const;
Agent getAgent(uint32_t i) const;
Agent getConnectedBrokerAgent() const { return connectedBrokerAgent; }
@@ -75,9 +86,11 @@ namespace qmf {
uint32_t maxAgentAgeMinutes;
bool listenOnDirect;
bool strictSecurity;
+ uint32_t maxThreadWaitTime;
Query agentQuery;
bool opened;
std::queue<ConsoleEvent> eventQueue;
+ EventNotifierImpl* eventNotifier;
qpid::sys::Thread* thread;
bool threadCanceled;
uint64_t lastVisit;
@@ -89,6 +102,8 @@ namespace qmf {
std::string directBase;
std::string topicBase;
boost::shared_ptr<SchemaCache> schemaCache;
+ qpid::sys::Mutex corrlock;
+ uint32_t nextCorrelator;
void enqueueEvent(const ConsoleEvent&);
void enqueueEventLH(const ConsoleEvent&);
@@ -98,10 +113,17 @@ namespace qmf {
void handleAgentUpdate(const std::string&, const qpid::types::Variant::Map&, const qpid::messaging::Message&);
void handleV1SchemaResponse(qpid::management::Buffer&, uint32_t, const qpid::messaging::Message&);
void periodicProcessing(uint64_t);
+ void alertEventNotifierLH(bool readable);
void run();
+ uint32_t correlator() { qpid::sys::Mutex::ScopedLock l(corrlock); return nextCorrelator++; }
friend class AgentImpl;
};
+
+ struct ConsoleSessionImplAccess {
+ static ConsoleSessionImpl& get(ConsoleSession& session);
+ static const ConsoleSessionImpl& get(const ConsoleSession& session);
+ };
}
#endif
diff --git a/cpp/src/qmf/DataAddr.cpp b/cpp/src/qmf/DataAddr.cpp
index fb51d5787f..d16e12062e 100644
--- a/cpp/src/qmf/DataAddr.cpp
+++ b/cpp/src/qmf/DataAddr.cpp
@@ -36,7 +36,9 @@ DataAddr::~DataAddr() { PI::dtor(*this); }
DataAddr& DataAddr::operator=(const DataAddr& s) { return PI::assign(*this, s); }
bool DataAddr::operator==(const DataAddr& o) { return *impl == *o.impl; }
+bool DataAddr::operator==(const DataAddr& o) const { return *impl == *o.impl; }
bool DataAddr::operator<(const DataAddr& o) { return *impl < *o.impl; }
+bool DataAddr::operator<(const DataAddr& o) const { return *impl < *o.impl; }
DataAddr::DataAddr(const qpid::types::Variant::Map& m) { PI::ctor(*this, new DataAddrImpl(m)); }
DataAddr::DataAddr(const string& n, const string& a, uint32_t e) { PI::ctor(*this, new DataAddrImpl(n, a, e)); }
@@ -45,7 +47,7 @@ const string& DataAddr::getAgentName() const { return impl->getAgentName(); }
uint32_t DataAddr::getAgentEpoch() const { return impl->getAgentEpoch(); }
Variant::Map DataAddr::asMap() const { return impl->asMap(); }
-bool DataAddrImpl::operator==(const DataAddrImpl& other)
+bool DataAddrImpl::operator==(const DataAddrImpl& other) const
{
return
agentName == other.agentName &&
@@ -54,7 +56,7 @@ bool DataAddrImpl::operator==(const DataAddrImpl& other)
}
-bool DataAddrImpl::operator<(const DataAddrImpl& other)
+bool DataAddrImpl::operator<(const DataAddrImpl& other) const
{
if (agentName < other.agentName) return true;
if (agentName > other.agentName) return false;
diff --git a/cpp/src/qmf/DataAddrImpl.h b/cpp/src/qmf/DataAddrImpl.h
index 3f9cae9453..11d512f0c4 100644
--- a/cpp/src/qmf/DataAddrImpl.h
+++ b/cpp/src/qmf/DataAddrImpl.h
@@ -38,8 +38,8 @@ namespace qmf {
//
// Methods from API handle
//
- bool operator==(const DataAddrImpl&);
- bool operator<(const DataAddrImpl&);
+ bool operator==(const DataAddrImpl&) const;
+ bool operator<(const DataAddrImpl&) const;
DataAddrImpl(const qpid::types::Variant::Map&);
DataAddrImpl(const std::string& _name, const std::string& _agentName, uint32_t _agentEpoch=0) :
agentName(_agentName), name(_name), agentEpoch(_agentEpoch) {}
diff --git a/cpp/src/qmf/EventNotifierImpl.cpp b/cpp/src/qmf/EventNotifierImpl.cpp
new file mode 100644
index 0000000000..20114aaa5e
--- /dev/null
+++ b/cpp/src/qmf/EventNotifierImpl.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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 "qmf/EventNotifierImpl.h"
+#include "qmf/AgentSessionImpl.h"
+#include "qmf/ConsoleSessionImpl.h"
+
+EventNotifierImpl::EventNotifierImpl(AgentSession& agentSession)
+ : readable(false), agent(agentSession)
+{
+ AgentSessionImplAccess::get(agent).setEventNotifier(this);
+}
+
+
+EventNotifierImpl::EventNotifierImpl(ConsoleSession& consoleSession)
+ : readable(false), console(consoleSession)
+{
+ ConsoleSessionImplAccess::get(console).setEventNotifier(this);
+}
+
+
+EventNotifierImpl::~EventNotifierImpl()
+{
+ if (agent.isValid())
+ AgentSessionImplAccess::get(agent).setEventNotifier(NULL);
+ if (console.isValid())
+ ConsoleSessionImplAccess::get(console).setEventNotifier(NULL);
+}
+
+void EventNotifierImpl::setReadable(bool readable)
+{
+ update(readable);
+ this->readable = readable;
+}
+
+
+bool EventNotifierImpl::isReadable() const
+{
+ return this->readable;
+}
diff --git a/cpp/src/tests/qrsh_utils/qrsh_example_command.cpp b/cpp/src/qmf/EventNotifierImpl.h
index 386e2f73f0..d85f9979d2 100644
--- a/cpp/src/tests/qrsh_utils/qrsh_example_command.cpp
+++ b/cpp/src/qmf/EventNotifierImpl.h
@@ -1,5 +1,7 @@
+#ifndef __QMF_EVENT_NOTIFIER_IMPL_H
+#define __QMF_EVENT_NOTIFIER_IMPL_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
@@ -7,46 +9,40 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable 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 <stdio.h>
-#include <unistd.h>
-
+ */
+#include "qmf/AgentSession.h"
+#include "qmf/ConsoleSession.h"
-main ( int argc, char ** argv )
+namespace qmf
{
- fprintf ( stderr, "Hello, I am the Example Child!\n");
- fprintf ( stderr, "my arguments %d are:\n", argc - 1 );
- fprintf ( stdout, "And hello to stdout, too!\n");
-
- int i;
- for ( i = 1; i < argc; ++ i )
- {
- fprintf ( stderr, "arg %d: |%s|\n", i, argv[i] );
- }
-
- for ( i = 0; i < 15; ++ i )
- {
- fprintf ( stderr, "child sleeping...\n" );
- sleep ( 1 );
- }
-
- fprintf ( stderr, "child exiting with code 13.\n" );
-
- return 13;
+ class EventNotifierImpl {
+ private:
+ bool readable;
+ AgentSession agent;
+ ConsoleSession console;
+
+ public:
+ EventNotifierImpl(AgentSession& agentSession);
+ EventNotifierImpl(ConsoleSession& consoleSession);
+ virtual ~EventNotifierImpl();
+
+ void setReadable(bool readable);
+ bool isReadable() const;
+
+ protected:
+ virtual void update(bool readable) = 0;
+ };
}
-
-
+#endif
diff --git a/cpp/src/qmf/PosixEventNotifier.cpp b/cpp/src/qmf/PosixEventNotifier.cpp
new file mode 100644
index 0000000000..a364cc155d
--- /dev/null
+++ b/cpp/src/qmf/PosixEventNotifier.cpp
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "qmf/posix/EventNotifier.h"
+#include "qmf/PosixEventNotifierImpl.h"
+#include "qmf/PrivateImplRef.h"
+
+using namespace qmf;
+using namespace std;
+
+typedef qmf::PrivateImplRef<posix::EventNotifier> PI;
+
+posix::EventNotifier::EventNotifier(PosixEventNotifierImpl* impl) { PI::ctor(*this, impl); }
+
+posix::EventNotifier::EventNotifier(AgentSession& agentSession)
+{
+ PI::ctor(*this, new PosixEventNotifierImpl(agentSession));
+}
+
+
+posix::EventNotifier::EventNotifier(ConsoleSession& consoleSession)
+{
+ PI::ctor(*this, new PosixEventNotifierImpl(consoleSession));
+}
+
+
+posix::EventNotifier::EventNotifier(const posix::EventNotifier& that)
+ : Handle<PosixEventNotifierImpl>()
+{
+ PI::copy(*this, that);
+}
+
+
+posix::EventNotifier::~EventNotifier()
+{
+ PI::dtor(*this);
+}
+
+posix::EventNotifier& posix::EventNotifier::operator=(const posix::EventNotifier& that)
+{
+ return PI::assign(*this, that);
+}
+
+
+int posix::EventNotifier::getHandle() const
+{
+ return impl->getHandle();
+}
+
diff --git a/cpp/src/qmf/PosixEventNotifierImpl.cpp b/cpp/src/qmf/PosixEventNotifierImpl.cpp
new file mode 100644
index 0000000000..011dbcc214
--- /dev/null
+++ b/cpp/src/qmf/PosixEventNotifierImpl.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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 "PosixEventNotifierImpl.h"
+#include "qpid/log/Statement.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#define BUFFER_SIZE 10
+
+using namespace qmf;
+
+PosixEventNotifierImpl::PosixEventNotifierImpl(AgentSession& agentSession)
+ : EventNotifierImpl(agentSession)
+{
+ openHandle();
+}
+
+
+PosixEventNotifierImpl::PosixEventNotifierImpl(ConsoleSession& consoleSession)
+ : EventNotifierImpl(consoleSession)
+{
+ openHandle();
+}
+
+
+PosixEventNotifierImpl::~PosixEventNotifierImpl()
+{
+ closeHandle();
+}
+
+
+void PosixEventNotifierImpl::update(bool readable)
+{
+ char buffer[BUFFER_SIZE];
+
+ if(readable && !this->isReadable()) {
+ if (::write(myHandle, "1", 1) == -1)
+ QPID_LOG(error, "PosixEventNotifierImpl::update write failed: " << errno);
+ }
+ else if(!readable && this->isReadable()) {
+ if (::read(yourHandle, buffer, BUFFER_SIZE) == -1)
+ QPID_LOG(error, "PosixEventNotifierImpl::update read failed: " << errno);
+ }
+}
+
+
+void PosixEventNotifierImpl::openHandle()
+{
+ int pair[2];
+
+ if(::pipe(pair) == -1)
+ throw QmfException("Unable to open event notifier handle.");
+
+ yourHandle = pair[0];
+ myHandle = pair[1];
+
+ int flags;
+
+ flags = ::fcntl(yourHandle, F_GETFL);
+ if((::fcntl(yourHandle, F_SETFL, flags | O_NONBLOCK)) == -1)
+ throw QmfException("Unable to make remote handle non-blocking.");
+
+ flags = ::fcntl(myHandle, F_GETFL);
+ if((::fcntl(myHandle, F_SETFL, flags | O_NONBLOCK)) == -1)
+ throw QmfException("Unable to make local handle non-blocking.");
+}
+
+
+void PosixEventNotifierImpl::closeHandle()
+{
+ if(myHandle > 0) {
+ ::close(myHandle);
+ myHandle = -1;
+ }
+
+ if(yourHandle > 0) {
+ ::close(yourHandle);
+ yourHandle = -1;
+ }
+}
+
+
+PosixEventNotifierImpl& PosixEventNotifierImplAccess::get(posix::EventNotifier& notifier)
+{
+ return *notifier.impl;
+}
+
+
+const PosixEventNotifierImpl& PosixEventNotifierImplAccess::get(const posix::EventNotifier& notifier)
+{
+ return *notifier.impl;
+}
+
diff --git a/cpp/src/qmf/PosixEventNotifierImpl.h b/cpp/src/qmf/PosixEventNotifierImpl.h
new file mode 100644
index 0000000000..c8a7446bd5
--- /dev/null
+++ b/cpp/src/qmf/PosixEventNotifierImpl.h
@@ -0,0 +1,61 @@
+#ifndef __QMF_POSIX_EVENT_NOTIFIER_IMPL_H
+#define __QMF_POSIX_EVENT_NOTIFIER_IMPL_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 "qmf/posix/EventNotifier.h"
+#include "qmf/EventNotifierImpl.h"
+#include "qpid/RefCounted.h"
+
+namespace qmf
+{
+ class AgentSession;
+ class ConsoleSession;
+
+ class PosixEventNotifierImpl : public EventNotifierImpl, public virtual qpid::RefCounted
+ {
+ public:
+ PosixEventNotifierImpl(AgentSession& agentSession);
+ PosixEventNotifierImpl(ConsoleSession& consoleSession);
+ virtual ~PosixEventNotifierImpl();
+
+ int getHandle() const { return yourHandle; }
+
+ private:
+ int myHandle;
+ int yourHandle;
+
+ void openHandle();
+ void closeHandle();
+
+ protected:
+ void update(bool readable);
+ };
+
+ struct PosixEventNotifierImplAccess
+ {
+ static PosixEventNotifierImpl& get(posix::EventNotifier& notifier);
+ static const PosixEventNotifierImpl& get(const posix::EventNotifier& notifier);
+ };
+
+}
+
+#endif
+
diff --git a/cpp/src/qmf/PrivateImplRef.h b/cpp/src/qmf/PrivateImplRef.h
index 8b698c4199..960cbb2e09 100644
--- a/cpp/src/qmf/PrivateImplRef.h
+++ b/cpp/src/qmf/PrivateImplRef.h
@@ -23,8 +23,8 @@
*/
#include "qmf/ImportExport.h"
-#include <boost/intrusive_ptr.hpp>
#include "qpid/RefCounted.h"
+#include <boost/intrusive_ptr.hpp>
namespace qmf {
diff --git a/cpp/src/qmf/engine/ResilientConnection.cpp b/cpp/src/qmf/engine/ResilientConnection.cpp
index ab65b8d768..41dd9ff00c 100644
--- a/cpp/src/qmf/engine/ResilientConnection.cpp
+++ b/cpp/src/qmf/engine/ResilientConnection.cpp
@@ -334,8 +334,7 @@ void ResilientConnectionImpl::notify()
{
if (notifyFd != -1)
{
- int unused_ret; //Suppress warnings about ignoring return value.
- unused_ret = ::write(notifyFd, ".", 1);
+ (void) ::write(notifyFd, ".", 1);
}
}
@@ -432,8 +431,7 @@ void ResilientConnectionImpl::EnqueueEvent(ResilientConnectionEvent::EventKind k
if (notifyFd != -1)
{
- int unused_ret; //Suppress warnings about ignoring return value.
- unused_ret = ::write(notifyFd, ".", 1);
+ (void) ::write(notifyFd, ".", 1);
}
}
diff --git a/cpp/src/qmf/engine/SchemaImpl.cpp b/cpp/src/qmf/engine/SchemaImpl.cpp
index e0948a9911..9d363d3012 100644
--- a/cpp/src/qmf/engine/SchemaImpl.cpp
+++ b/cpp/src/qmf/engine/SchemaImpl.cpp
@@ -35,17 +35,17 @@ using qpid::framing::Uuid;
SchemaHash::SchemaHash()
{
for (int idx = 0; idx < 16; idx++)
- hash[idx] = 0x5A;
+ hash.b[idx] = 0x5A;
}
void SchemaHash::encode(Buffer& buffer) const
{
- buffer.putBin128(hash);
+ buffer.putBin128(hash.b);
}
void SchemaHash::decode(Buffer& buffer)
{
- buffer.getBin128(hash);
+ buffer.getBin128(hash.b);
}
void SchemaHash::update(uint8_t data)
@@ -55,9 +55,8 @@ void SchemaHash::update(uint8_t data)
void SchemaHash::update(const char* data, uint32_t len)
{
- uint64_t* first = (uint64_t*) hash;
- uint64_t* second = (uint64_t*) hash + 1;
-
+ uint64_t* first = &hash.q[0];
+ uint64_t* second = &hash.q[1];
for (uint32_t idx = 0; idx < len; idx++) {
*first = *first ^ (uint64_t) data[idx];
*second = *second << 1;
diff --git a/cpp/src/qmf/engine/SchemaImpl.h b/cpp/src/qmf/engine/SchemaImpl.h
index 8b079a5ec6..683fb6f8f0 100644
--- a/cpp/src/qmf/engine/SchemaImpl.h
+++ b/cpp/src/qmf/engine/SchemaImpl.h
@@ -35,7 +35,10 @@ namespace engine {
// they've been registered.
class SchemaHash {
- uint8_t hash[16];
+ union h {
+ uint8_t b[16];
+ uint64_t q[2];
+ } hash;
public:
SchemaHash();
void encode(qpid::framing::Buffer& buffer) const;
@@ -47,7 +50,7 @@ namespace engine {
void update(Direction d) { update((uint8_t) d); }
void update(Access a) { update((uint8_t) a); }
void update(bool b) { update((uint8_t) (b ? 1 : 0)); }
- const uint8_t* get() const { return hash; }
+ const uint8_t* get() const { return hash.b; }
bool operator==(const SchemaHash& other) const;
bool operator<(const SchemaHash& other) const;
bool operator>(const SchemaHash& other) const;
diff --git a/cpp/src/qpid/Address.cpp b/cpp/src/qpid/Address.cpp
index e2b2dfbcdf..bed3d592df 100644
--- a/cpp/src/qpid/Address.cpp
+++ b/cpp/src/qpid/Address.cpp
@@ -28,7 +28,13 @@ namespace qpid {
const string Address::TCP("tcp");
ostream& operator<<(ostream& os, const Address& a) {
- return os << a.protocol << ":" << a.host << ":" << a.port;
+ // If the host is an IPv6 literal we need to print "[]" around it
+ // (we detect IPv6 literals because they contain ":" which is otherwise illegal)
+ if (a.host.find(':') != string::npos) {
+ return os << a.protocol << ":[" << a.host << "]:" << a.port;
+ } else {
+ return os << a.protocol << ":" << a.host << ":" << a.port;
+ }
}
bool operator==(const Address& x, const Address& y) {
diff --git a/cpp/src/qpid/BufferRef.h b/cpp/src/qpid/BufferRef.h
new file mode 100644
index 0000000000..bfe1f9ebaa
--- /dev/null
+++ b/cpp/src/qpid/BufferRef.h
@@ -0,0 +1,70 @@
+#ifndef QPID_BUFFERREF_H
+#define QPID_BUFFERREF_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/RefCounted.h"
+#include <boost/intrusive_ptr.hpp>
+
+namespace qpid {
+
+/** Template for mutable or const buffer references */
+template <class T> class BufferRefT {
+ public:
+ BufferRefT() : begin_(0), end_(0) {}
+
+ BufferRefT(boost::intrusive_ptr<RefCounted> c, T* begin, T* end) :
+ counter(c), begin_(begin), end_(end) {}
+
+ template <class U> BufferRefT(const BufferRefT<U>& other) :
+ counter(other.counter), begin_(other.begin_), end_(other.end_) {}
+
+ T* begin() const { return begin_; }
+ T* end() const { return end_; }
+
+ /** Return a sub-buffer of the current buffer */
+ BufferRefT sub_buffer(T* begin, T* end) {
+ assert(begin_ <= begin && begin <= end_);
+ assert(begin_ <= end && end <= end_);
+ assert(begin <= end);
+ return BufferRefT(counter, begin, end);
+ }
+
+ private:
+ boost::intrusive_ptr<RefCounted> counter;
+ T* begin_;
+ T* end_;
+};
+
+/**
+ * Reference to a mutable ref-counted buffer.
+ */
+typedef BufferRefT<char> BufferRef;
+
+/**
+ * Reference to a const ref-counted buffer.
+ */
+typedef BufferRefT<const char> ConstBufferRef;
+
+} // namespace qpid
+
+#endif /*!QPID_BUFFERREF_H*/
diff --git a/cpp/src/qpid/client/amqp0_10/SimpleUrlParser.h b/cpp/src/qpid/DisableExceptionLogging.h
index 24f90ca9d6..04a9240513 100644
--- a/cpp/src/qpid/client/amqp0_10/SimpleUrlParser.h
+++ b/cpp/src/qpid/DisableExceptionLogging.h
@@ -1,5 +1,5 @@
-#ifndef QPID_CLIENT_AMQP0_10_SIMPLEURLPARSER_H
-#define QPID_CLIENT_AMQP0_10_SIMPLEURLPARSER_H
+#ifndef QPID_DISABLEEXCEPTIONLOGGING_H
+#define QPID_DISABLEEXCEPTIONLOGGING_H
/*
*
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -21,22 +21,19 @@
* under the License.
*
*/
-#include <string>
+#include "qpid/CommonImportExport.h"
namespace qpid {
-namespace client {
-
-struct ConnectionSettings;
-
-namespace amqp0_10 {
/**
- * Parses a simple url of the form user/password@hostname:port
+ * Temporarily disable logging in qpid::Exception constructor.
+ * Used by log::Logger to avoid logging exceptions during Logger construction.
*/
-struct SimpleUrlParser
+struct DisableExceptionLogging
{
- static void parse(const std::string& url, qpid::client::ConnectionSettings& result);
+ QPID_COMMON_EXTERN DisableExceptionLogging();
+ QPID_COMMON_EXTERN ~DisableExceptionLogging();
};
-}}} // namespace qpid::client::amqp0_10
+} // namespace qpid
-#endif /*!QPID_CLIENT_AMQP0_10_SIMPLEURLPARSER_H*/
+#endif /*!QPID_DISABLEEXCEPTIONLOGGING_H*/
diff --git a/cpp/src/qpid/Exception.cpp b/cpp/src/qpid/Exception.cpp
index 16a3a13d17..a6696f06e1 100644
--- a/cpp/src/qpid/Exception.cpp
+++ b/cpp/src/qpid/Exception.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
@@ -21,13 +21,25 @@
#include "qpid/log/Statement.h"
#include "qpid/Exception.h"
+#include "qpid/DisableExceptionLogging.h"
#include <typeinfo>
#include <assert.h>
#include <string.h>
namespace qpid {
+// Note on static initialization order: if an exception is constructed
+// in a static constructor before disableExceptionLogging has been
+// initialized, the worst that can happen is we lose an exception log
+// message. Since we shouldn't be throwing a lot of exceptions during
+// static construction this seems safe.
+static bool disableExceptionLogging = false;
+
+DisableExceptionLogging::DisableExceptionLogging() { disableExceptionLogging = true; }
+DisableExceptionLogging::~DisableExceptionLogging() { disableExceptionLogging = false; }
+
Exception::Exception(const std::string& msg) throw() : message(msg) {
+ if (disableExceptionLogging) return;
QPID_LOG_IF(debug, !msg.empty(), "Exception constructed: " << message);
}
diff --git a/cpp/src/qpid/Modules.cpp b/cpp/src/qpid/Modules.cpp
index 8f58df6ed1..727e05d212 100644
--- a/cpp/src/qpid/Modules.cpp
+++ b/cpp/src/qpid/Modules.cpp
@@ -64,7 +64,6 @@ void tryShlib(const char* libname_, bool noThrow) {
if (!isShlibName(libname)) libname += suffix();
try {
sys::Shlib shlib(libname);
- QPID_LOG (info, "Loaded Module: " << libname);
}
catch (const std::exception& /*e*/) {
if (!noThrow)
@@ -82,7 +81,7 @@ void loadModuleDir (std::string dirname, bool isDefault)
return;
throw Exception ("Directory not found: " + dirname);
}
- if (!fs::is_directory(dirPath))
+ if (!fs::is_directory(dirPath))
{
throw Exception ("Invalid value for module-dir: " + dirname + " is not a directory");
}
diff --git a/cpp/src/qpid/Options.cpp b/cpp/src/qpid/Options.cpp
index 499fb71bc3..4b13e349f5 100644
--- a/cpp/src/qpid/Options.cpp
+++ b/cpp/src/qpid/Options.cpp
@@ -30,23 +30,6 @@ namespace qpid {
using namespace std;
-/*
- * ---------------------------------------------
- * Explanation for Boost 103200 conditional code
- * ---------------------------------------------
- *
- * Please see large comment in Options.h .
- *
- */
-
-#if ( BOOST_VERSION == 103200 )
-std::vector<std::string> Options::long_names;
-std::vector<std::string> Options::short_names;
-#endif
-
-
-
-
namespace {
struct EnvOptMapper {
@@ -69,49 +52,11 @@ struct EnvOptMapper {
static const std::string prefix("QPID_");
if (envVar.substr(0, prefix.size()) == prefix) {
string env = envVar.substr(prefix.size());
-#if (BOOST_VERSION >= 103300)
typedef const std::vector< boost::shared_ptr<po::option_description> > OptDescs;
OptDescs::const_iterator i =
find_if(opts.options().begin(), opts.options().end(), boost::bind(matchStr, env, _1));
if (i != opts.options().end())
return (*i)->long_name();
-#else
- /*
- * For Boost version 103200 and below.
- *
- * In Boost version 103200, the options_description::options member,
- * used above, is private. So what I will do here is use the
- * count() funtion, which returns a 1 or 0 indicating presence or
- * absence of the environment variable.
- *
- * If it is present, I will return its name. Env vars do not have
- * short and long forms, so the name is synonymous with the long
- * name. (This would not work for command line args.)
- * And if it's absent -- an empty string.
- */
-
-
- /*
- * The env vars come in unaltered, i.e. QPID_FOO, but the
- * options are stored normalized as "qpid-foo". Change the
- * local variable "env" so it can be found by "opts".
- */
- for (std::string::iterator i = env.begin(); i != env.end(); ++i)
- {
- *i = (*i == '_')
- ? '-'
- : ::tolower(*i);
- }
-
- if ( opts.count(env.c_str()) > 0 )
- {
- return env.c_str();
- }
- else
- {
- return string();
- }
-#endif
}
return string();
}
@@ -166,10 +111,6 @@ std::string prettyArg(const std::string& name, const std::string& value) {
Options::Options(const string& name) :
po::options_description(name)
-
-#if ( BOOST_VERSION == 103200 )
- , m_less_easy(this, this)
-#endif
{
}
@@ -186,7 +127,6 @@ void Options::parse(int argc, char const* const* argv, const std::string& config
parsing="command line options";
if (argc > 0 && argv != 0) {
if (allowUnknown) {
-#if (BOOST_VERSION >= 103300)
// This hideous workaround is required because boost 1.33 has a bug
// that causes 'allow_unregistered' to not work.
po::command_line_parser clp = po::command_line_parser(argc, const_cast<char**>(argv)).
@@ -200,113 +140,6 @@ void Options::parse(int argc, char const* const* argv, const std::string& config
filtopts.options.push_back (*i);
po::store(filtopts, vm);
-#elif ( BOOST_VERSION == 103200 )
-
- /*
- * "Tokenize" the argv to get rid of equals signs.
- */
- vector<string> tokenized_argv;
- vector<string>::iterator iter;
-
- for ( int i = 0; i < argc; ++ i )
- {
- string s = argv[i];
- size_t equals_pos = s.find_first_of ( '=' );
-
- if ( string::npos == equals_pos ) // There's no equals sign. This is a token.
- {
- tokenized_argv.push_back(s);
- }
- else
- {
- // Two tokens -- before and after the equals position.
- tokenized_argv.push_back ( s.substr(0, equals_pos) );
- tokenized_argv.push_back ( s.substr(equals_pos+1) );
- }
- }
-
-
- /*
- * Now "filter" the tokenized argv, to get rid of all
- * unrecognized options. Because Boost 103200 has no
- * facility for dealing gracefully with "unregistered"
- * options.
- */
- vector<string> filtered_argv;
- vector<string>::iterator the_end = tokenized_argv.end();
-
- // The program-name gets in for free...
- iter = tokenized_argv.begin();
- filtered_argv.push_back ( * iter );
- ++ iter;
-
- // ...but all other args get checked.
- while ( iter < the_end )
- {
- /*
- * If this is an argument that is registered,
- * copy it to filtered_argv and also copy all
- * of its arguments.
- */
- if ( is_registered_option ( * iter ) )
- {
- // Store this recognized arg.
- filtered_argv.push_back ( * iter );
- ++ iter;
-
- // Copy all values for the above arg.
- // Args are tokens that do not start with a minus.
- while ( (iter < the_end) && ((* iter)[0] != '-') )
- {
- filtered_argv.push_back ( * iter );
- ++ iter;
- }
- }
- else
- {
- // Skip this unrecognized arg.
- ++ iter;
-
- // Copy all values for the above arg.
- // Values are tokens that do not start with a minus.
- while ( (iter < the_end) && ( '-' != (*iter)[0] ) )
- {
- ++ iter;
- }
- }
- }
-
- // Make an array of temporary C strings, because
- // the interface I can use wants it that way.
- int new_argc = filtered_argv.size();
- char ** new_argv = new char * [ new_argc ];
- int i = 0;
-
- // cout << "NEW ARGV: |";
- for ( iter = filtered_argv.begin();
- iter < filtered_argv.end();
- ++ iter, ++ i
- )
- {
- new_argv[i] = strdup( (* iter).c_str() );
- // cout << " " << new_argv[i] ;
- }
- // cout << "|\n";
-
-
- // Use the array of C strings.
- po::basic_parsed_options<char> bpo = po::parse_command_line(new_argc, const_cast<char**>(new_argv), *this);
- po::store(bpo, vm);
-
-
- // Now free the temporary C strings.
- for ( i = 0; i < new_argc; ++ i )
- {
- free ( new_argv[i] );
- }
- delete[] new_argv;
-
-#endif
}
else
po::store(po::parse_command_line(argc, const_cast<char**>(argv), *this), vm);
@@ -363,107 +196,5 @@ CommonOptions::CommonOptions(const string& name, const string& configfile)
}
-
-
-#if ( BOOST_VERSION == 103200 )
-options_description_less_easy_init&
-options_description_less_easy_init::operator()(char const * name,
- char const * description)
-{
- // Snoop on the arguments....
- owner->register_names ( name );
- // ... then call parent function explicitly.
- po::options_description_easy_init::operator() ( name, description );
- return * this;
-}
-
-
-options_description_less_easy_init&
-options_description_less_easy_init::operator()(char const * name,
- const po::value_semantic* s)
-{
- // Snoop on the arguments....
- owner->register_names ( name );
- // ... then call parent function explicitly.
- po::options_description_easy_init::operator() ( name, s );
- return * this;
-}
-
-
-options_description_less_easy_init&
-options_description_less_easy_init::operator()(const char* name,
- const po::value_semantic* s,
- const char* description)
-{
- // Snoop on the arguments....
- owner->register_names ( name );
- // ... then call parent function explicitly.
- po::options_description_easy_init::operator() ( name, s, description );
- return * this;
-}
-
-
-
-
-
-void
-Options::register_names ( std::string s )
-{
-
- std::string::size_type comma_pos = s.find_first_of ( ',' );
-
- if ( std::string::npos == comma_pos )
- {
- // There is no short-name.
- long_names.push_back ( s );
- }
- else
- {
- std::string long_name = s.substr(0, comma_pos),
- short_name = s.substr(comma_pos+1);
- long_names .push_back ( long_name );
- short_names.push_back ( short_name );
- }
-
- /*
- * There is no way to tell when the adding of new options is finished,
- * so I re-sort after each one.
- */
- std::sort ( long_names .begin(), long_names .end() );
- std::sort ( short_names.begin(), short_names.end() );
-}
-
-
-
-
-
-bool
-Options::is_registered_option ( std::string s )
-{
- std::string without_dashes = s.substr ( s.find_first_not_of ( '-' ) );
- std::vector<std::string>::iterator i;
-
- // Look among the long names.
- i = std::find ( long_names.begin(),
- long_names.end(),
- without_dashes
- );
- if ( i != long_names.end() )
- return true;
-
- // Look among the short names.
- i = std::find ( short_names.begin(),
- short_names.end(),
- without_dashes
- );
- if ( i != short_names.end() )
- return true;
-
-
- return false;
-}
-#endif
-
-
} // namespace qpid
diff --git a/cpp/src/qpid/RefCounted.h b/cpp/src/qpid/RefCounted.h
index e7a9bf31c1..f9e0107103 100644
--- a/cpp/src/qpid/RefCounted.h
+++ b/cpp/src/qpid/RefCounted.h
@@ -53,8 +53,10 @@ protected:
// intrusive_ptr support.
namespace boost {
-inline void intrusive_ptr_add_ref(const qpid::RefCounted* p) { p->addRef(); }
-inline void intrusive_ptr_release(const qpid::RefCounted* p) { p->release(); }
+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(); }
}
diff --git a/cpp/src/qpid/RefCountedBuffer.cpp b/cpp/src/qpid/RefCountedBuffer.cpp
index 9b8f1ebd5e..a82e1a02ab 100644
--- a/cpp/src/qpid/RefCountedBuffer.cpp
+++ b/cpp/src/qpid/RefCountedBuffer.cpp
@@ -20,34 +20,27 @@
*/
#include "qpid/RefCountedBuffer.h"
+#include <stdlib.h>
#include <new>
namespace qpid {
-RefCountedBuffer::RefCountedBuffer() : count(0) {}
-
-void RefCountedBuffer::destroy() const {
+void RefCountedBuffer::released() const {
this->~RefCountedBuffer();
- ::delete[] reinterpret_cast<const char*>(this);
-}
-
-char* RefCountedBuffer::addr() const {
- return const_cast<char*>(reinterpret_cast<const char*>(this)+sizeof(RefCountedBuffer));
+ ::free (reinterpret_cast<void *>(const_cast<RefCountedBuffer *>(this)));
}
-RefCountedBuffer::pointer RefCountedBuffer::create(size_t n) {
- char* store=::new char[n+sizeof(RefCountedBuffer)];
+BufferRef RefCountedBuffer::create(size_t n) {
+ void* store=::malloc (n + sizeof(RefCountedBuffer));
+ if (NULL == store)
+ throw std::bad_alloc();
new(store) RefCountedBuffer;
- return pointer(reinterpret_cast<RefCountedBuffer*>(store));
+ char* start = reinterpret_cast<char *>(store) + sizeof(RefCountedBuffer);
+ return BufferRef(
+ boost::intrusive_ptr<RefCounted>(reinterpret_cast<RefCountedBuffer*>(store)),
+ start, start+n);
}
-RefCountedBuffer::pointer::pointer() {}
-RefCountedBuffer::pointer::pointer(RefCountedBuffer* x) : p(x) {}
-RefCountedBuffer::pointer::pointer(const pointer& x) : p(x.p) {}
-RefCountedBuffer::pointer::~pointer() {}
-RefCountedBuffer::pointer& RefCountedBuffer::pointer::operator=(const RefCountedBuffer::pointer& x) { p = x.p; return *this; }
-
-char* RefCountedBuffer::pointer::cp() const { return p ? p->get() : 0; }
} // namespace qpid
diff --git a/cpp/src/qpid/RefCountedBuffer.h b/cpp/src/qpid/RefCountedBuffer.h
index 75a23862be..f0ea86130b 100644
--- a/cpp/src/qpid/RefCountedBuffer.h
+++ b/cpp/src/qpid/RefCountedBuffer.h
@@ -22,68 +22,23 @@
*
*/
-#include <boost/utility.hpp>
-#include <boost/detail/atomic_count.hpp>
-#include <boost/intrusive_ptr.hpp>
+#include <qpid/RefCounted.h>
+#include <qpid/BufferRef.h>
namespace qpid {
/**
- * Reference-counted byte buffer.
- * No alignment guarantees.
+ * Reference-counted byte buffer. No alignment guarantees.
*/
-class RefCountedBuffer : boost::noncopyable {
- mutable boost::detail::atomic_count count;
- RefCountedBuffer();
- void destroy() const;
- char* addr() const;
-
-public:
- /** Smart char pointer to a reference counted buffer */
- class pointer {
- boost::intrusive_ptr<RefCountedBuffer> p;
- char* cp() const;
- pointer(RefCountedBuffer* x);
- friend class RefCountedBuffer;
-
- public:
- pointer();
- pointer(const pointer&);
- ~pointer();
- pointer& operator=(const pointer&);
-
- char* get() { return cp(); }
- operator char*() { return cp(); }
- char& operator*() { return *cp(); }
- char& operator[](size_t i) { return cp()[i]; }
-
- const char* get() const { return cp(); }
- operator const char*() const { return cp(); }
- const char& operator*() const { return *cp(); }
- const char& operator[](size_t i) const { return cp()[i]; }
- };
-
+class RefCountedBuffer : public RefCounted {
+ public:
/** Create a reference counted buffer of size n */
- static pointer create(size_t n);
-
- /** Get a pointer to the start of the buffer. */
- char* get() { return addr(); }
- const char* get() const { return addr(); }
- char& operator[](size_t i) { return get()[i]; }
- const char& operator[](size_t i) const { return get()[i]; }
+ static BufferRef create(size_t n);
- void addRef() const { ++count; }
- void release() const { if (--count==0) destroy(); }
- long refCount() { return count; }
+ protected:
+ void released() const;
};
} // namespace qpid
-// intrusive_ptr support.
-namespace boost {
-inline void intrusive_ptr_add_ref(const qpid::RefCountedBuffer* p) { p->addRef(); }
-inline void intrusive_ptr_release(const qpid::RefCountedBuffer* p) { p->release(); }
-}
-
-
#endif /*!QPID_REFCOUNTEDBUFFER_H*/
diff --git a/cpp/src/qpid/Sasl.h b/cpp/src/qpid/Sasl.h
index 9a9d61b037..4d579fa051 100644
--- a/cpp/src/qpid/Sasl.h
+++ b/cpp/src/qpid/Sasl.h
@@ -47,8 +47,8 @@ class Sasl
* client supports.
* @param externalSecuritySettings security related details from the underlying transport
*/
- virtual std::string start(const std::string& mechanisms,
- const qpid::sys::SecuritySettings* externalSecuritySettings = 0) = 0;
+ virtual bool start(const std::string& mechanisms, std::string& response,
+ const qpid::sys::SecuritySettings* externalSecuritySettings = 0) = 0;
virtual std::string step(const std::string& challenge) = 0;
virtual std::string getMechanism() = 0;
virtual std::string getUserId() = 0;
diff --git a/cpp/src/qpid/SaslFactory.cpp b/cpp/src/qpid/SaslFactory.cpp
index 055883abee..a8d1f94c1e 100644
--- a/cpp/src/qpid/SaslFactory.cpp
+++ b/cpp/src/qpid/SaslFactory.cpp
@@ -112,7 +112,7 @@ class CyrusSasl : public Sasl
public:
CyrusSasl(const std::string & username, const std::string & password, const std::string & serviceName, const std::string & hostName, int minSsf, int maxSsf, bool allowInteraction);
~CyrusSasl();
- std::string start(const std::string& mechanisms, const SecuritySettings* externalSettings);
+ bool start(const std::string& mechanisms, std::string& response, const SecuritySettings* externalSettings);
std::string step(const std::string& challenge);
std::string getMechanism();
std::string getUserId();
@@ -182,17 +182,18 @@ CyrusSasl::CyrusSasl(const std::string & username, const std::string & password,
callbacks[i].id = SASL_CB_AUTHNAME;
callbacks[i].proc = (CallbackProc*) &getUserFromSettings;
callbacks[i++].context = &settings;
- }
- callbacks[i].id = SASL_CB_PASS;
- if (settings.password.empty()) {
- callbacks[i].proc = 0;
- callbacks[i++].context = 0;
- } else {
- callbacks[i].proc = (CallbackProc*) &getPasswordFromSettings;
- callbacks[i++].context = &settings;
+ callbacks[i].id = SASL_CB_PASS;
+ if (settings.password.empty()) {
+ callbacks[i].proc = 0;
+ callbacks[i++].context = 0;
+ } else {
+ callbacks[i].proc = (CallbackProc*) &getPasswordFromSettings;
+ callbacks[i++].context = &settings;
+ }
}
+
callbacks[i].id = SASL_CB_LIST_END;
callbacks[i].proc = 0;
callbacks[i++].context = 0;
@@ -209,7 +210,7 @@ namespace {
const std::string SSL("ssl");
}
-std::string CyrusSasl::start(const std::string& mechanisms, const SecuritySettings* externalSettings)
+bool CyrusSasl::start(const std::string& mechanisms, std::string& response, const SecuritySettings* externalSettings)
{
QPID_LOG(debug, "CyrusSasl::start(" << mechanisms << ")");
int result = sasl_client_new(settings.service.c_str(),
@@ -282,7 +283,12 @@ std::string CyrusSasl::start(const std::string& mechanisms, const SecuritySettin
mechanism = std::string(chosenMechanism);
QPID_LOG(debug, "CyrusSasl::start(" << mechanisms << "): selected "
<< mechanism << " response: '" << std::string(out, outlen) << "'");
- return std::string(out, outlen);
+ if (out) {
+ response = std::string(out, outlen);
+ return true;
+ } else {
+ return false;
+ }
}
std::string CyrusSasl::step(const std::string& challenge)
diff --git a/cpp/src/qpid/Url.cpp b/cpp/src/qpid/Url.cpp
index ab796f4642..f699b60c17 100644
--- a/cpp/src/qpid/Url.cpp
+++ b/cpp/src/qpid/Url.cpp
@@ -156,11 +156,12 @@ class UrlParser {
return false;
}
- // TODO aconway 2008-11-20: this does not fully implement
- // http://www.ietf.org/rfc/rfc3986.txt. Works for DNS names and
- // ipv4 literals but won't handle ipv6.
+ // A liberal interpretation of http://www.ietf.org/rfc/rfc3986.txt.
+ // Works for DNS names and and ipv4 and ipv6 literals
//
bool host(string& h) {
+ if (ip6literal(h)) return true;
+
const char* start=i;
while (unreserved() || pctEncoded())
;
@@ -169,6 +170,22 @@ class UrlParser {
return true;
}
+ // This is a bit too liberal for IPv6 literal addresses, but probably good enough
+ bool ip6literal(string& h) {
+ if (literal("[")) {
+ const char* start = i;
+ while (hexDigit() || literal(":") || literal("."))
+ ;
+ const char* end = i;
+ if ( end-start < 2 ) return false; // Smallest valid address is "::"
+ if (literal("]")) {
+ h.assign(start, end);
+ return true;
+ }
+ }
+ return false;
+ }
+
bool unreserved() { return (::isalnum(*i) || ::strchr("-._~", *i)) && advance(); }
bool pctEncoded() { return literal("%") && hexDigit() && hexDigit(); }
diff --git a/cpp/src/qpid/acl/Acl.cpp b/cpp/src/qpid/acl/Acl.cpp
index 67603015d7..4b3dda7962 100644
--- a/cpp/src/qpid/acl/Acl.cpp
+++ b/cpp/src/qpid/acl/Acl.cpp
@@ -180,7 +180,10 @@ Acl::Acl (AclValues& av, Broker& b): aclValues(av), broker(&b), transferAcl(fals
{
case _qmf::Acl::METHOD_RELOADACLFILE :
readAclFile(text);
- status = Manageable::STATUS_USER;
+ if (text.empty())
+ status = Manageable::STATUS_OK;
+ else
+ status = Manageable::STATUS_USER;
break;
}
diff --git a/cpp/src/qpid/acl/AclPlugin.cpp b/cpp/src/qpid/acl/AclPlugin.cpp
index e4d721ea44..d611797c49 100644
--- a/cpp/src/qpid/acl/AclPlugin.cpp
+++ b/cpp/src/qpid/acl/AclPlugin.cpp
@@ -69,7 +69,7 @@ struct AclPlugin : public Plugin {
}
acl = new Acl(values, b);
- b.setAcl(acl.get());
+ b.setAcl(acl.get());
b.addFinalizer(boost::bind(&AclPlugin::shutdown, this));
}
diff --git a/cpp/src/qpid/agent/ManagementAgentImpl.cpp b/cpp/src/qpid/agent/ManagementAgentImpl.cpp
index 593d403a11..f183ff8e0c 100644
--- a/cpp/src/qpid/agent/ManagementAgentImpl.cpp
+++ b/cpp/src/qpid/agent/ManagementAgentImpl.cpp
@@ -305,43 +305,47 @@ void ManagementAgentImpl::raiseEvent(const ManagementEvent& event, severity_t se
"emerg", "alert", "crit", "error", "warn",
"note", "info", "debug"
};
- sys::Mutex::ScopedLock lock(agentLock);
- Buffer outBuffer(eventBuffer, MA_BUFFER_SIZE);
- uint8_t sev = (severity == SEV_DEFAULT) ? event.getSeverity() : (uint8_t) severity;
+ string content;
stringstream key;
-
- // key << "console.event." << assignedBrokerBank << "." << assignedAgentBank << "." <<
- // event.getPackageName() << "." << event.getEventName();
- key << "agent.ind.event." << keyifyNameStr(event.getPackageName())
- << "." << keyifyNameStr(event.getEventName())
- << "." << severityStr[sev]
- << "." << vendorNameKey
- << "." << productNameKey
- << "." << instanceNameKey;
-
- Variant::Map map_;
- Variant::Map schemaId;
- Variant::Map values;
Variant::Map headers;
- string content;
- map_["_schema_id"] = mapEncodeSchemaId(event.getPackageName(),
- event.getEventName(),
- event.getMd5Sum(),
- ManagementItem::CLASS_KIND_EVENT);
- event.mapEncode(values);
- map_["_values"] = values;
- map_["_timestamp"] = uint64_t(Duration(EPOCH, now()));
- map_["_severity"] = sev;
+ {
+ sys::Mutex::ScopedLock lock(agentLock);
+ Buffer outBuffer(eventBuffer, MA_BUFFER_SIZE);
+ uint8_t sev = (severity == SEV_DEFAULT) ? event.getSeverity() : (uint8_t) severity;
+
+ // key << "console.event." << assignedBrokerBank << "." << assignedAgentBank << "." <<
+ // event.getPackageName() << "." << event.getEventName();
+ key << "agent.ind.event." << keyifyNameStr(event.getPackageName())
+ << "." << keyifyNameStr(event.getEventName())
+ << "." << severityStr[sev]
+ << "." << vendorNameKey
+ << "." << productNameKey
+ << "." << instanceNameKey;
+
+ Variant::Map map_;
+ Variant::Map schemaId;
+ Variant::Map values;
+
+ map_["_schema_id"] = mapEncodeSchemaId(event.getPackageName(),
+ event.getEventName(),
+ event.getMd5Sum(),
+ ManagementItem::CLASS_KIND_EVENT);
+ event.mapEncode(values);
+ map_["_values"] = values;
+ map_["_timestamp"] = uint64_t(Duration(EPOCH, now()));
+ map_["_severity"] = sev;
- headers["method"] = "indication";
- headers["qmf.opcode"] = "_data_indication";
- headers["qmf.content"] = "_event";
- headers["qmf.agent"] = name_address;
+ headers["method"] = "indication";
+ headers["qmf.opcode"] = "_data_indication";
+ headers["qmf.content"] = "_event";
+ headers["qmf.agent"] = name_address;
+
+ Variant::List list;
+ list.push_back(map_);
+ ListCodec::encode(list, content);
+ }
- Variant::List list;
- list.push_back(map_);
- ListCodec::encode(list, content);
connThreadBody.sendBuffer(content, "", headers, topicExchange, key.str(), "amqp/list");
}
@@ -521,9 +525,12 @@ void ManagementAgentImpl::sendException(const string& rte, const string& rtk, co
void ManagementAgentImpl::handleSchemaRequest(Buffer& inBuffer, uint32_t sequence, const string& rte, const string& rtk)
{
- sys::Mutex::ScopedLock lock(agentLock);
string packageName;
SchemaClassKey key;
+ uint32_t outLen(0);
+ char localBuffer[MA_BUFFER_SIZE];
+ Buffer outBuffer(localBuffer, MA_BUFFER_SIZE);
+ bool found(false);
inBuffer.getShortString(packageName);
inBuffer.getShortString(key.name);
@@ -531,26 +538,30 @@ void ManagementAgentImpl::handleSchemaRequest(Buffer& inBuffer, uint32_t sequenc
QPID_LOG(trace, "RCVD SchemaRequest: package=" << packageName << " class=" << key.name);
- PackageMap::iterator pIter = packages.find(packageName);
- if (pIter != packages.end()) {
- ClassMap& cMap = pIter->second;
- ClassMap::iterator cIter = cMap.find(key);
- if (cIter != cMap.end()) {
- SchemaClass& schema = cIter->second;
- Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
- string body;
-
- encodeHeader(outBuffer, 's', sequence);
- schema.writeSchemaCall(body);
- outBuffer.putRawData(body);
- outLen = MA_BUFFER_SIZE - outBuffer.available();
- outBuffer.reset();
- connThreadBody.sendBuffer(outBuffer, outLen, rte, rtk);
-
- QPID_LOG(trace, "SENT SchemaInd: package=" << packageName << " class=" << key.name);
+ {
+ sys::Mutex::ScopedLock lock(agentLock);
+ PackageMap::iterator pIter = packages.find(packageName);
+ if (pIter != packages.end()) {
+ ClassMap& cMap = pIter->second;
+ ClassMap::iterator cIter = cMap.find(key);
+ if (cIter != cMap.end()) {
+ SchemaClass& schema = cIter->second;
+ string body;
+
+ encodeHeader(outBuffer, 's', sequence);
+ schema.writeSchemaCall(body);
+ outBuffer.putRawData(body);
+ outLen = MA_BUFFER_SIZE - outBuffer.available();
+ outBuffer.reset();
+ found = true;
+ }
}
}
+
+ if (found) {
+ connThreadBody.sendBuffer(outBuffer, outLen, rte, rtk);
+ QPID_LOG(trace, "SENT SchemaInd: package=" << packageName << " class=" << key.name);
+ }
}
void ManagementAgentImpl::handleConsoleAddedIndication()
@@ -969,18 +980,6 @@ ManagementAgentImpl::PackageMap::iterator ManagementAgentImpl::findOrAddPackage(
pair<PackageMap::iterator, bool> result =
packages.insert(pair<string, ClassMap>(name, ClassMap()));
- if (connected) {
- // Publish a package-indication message
- Buffer outBuffer(outputBuffer, MA_BUFFER_SIZE);
- uint32_t outLen;
-
- encodeHeader(outBuffer, 'p');
- encodePackageIndication(outBuffer, result.first);
- outLen = MA_BUFFER_SIZE - outBuffer.available();
- outBuffer.reset();
- connThreadBody.sendBuffer(outBuffer, outLen, "qpid.management", "schema.package");
- }
-
return result.first;
}
@@ -1038,131 +1037,146 @@ void ManagementAgentImpl::encodeClassIndication(Buffer& buf,
QPID_LOG(trace, "SENT ClassInd: package=" << (*pIter).first << " class=" << key.name);
}
+struct MessageItem {
+ string content;
+ Variant::Map headers;
+ string key;
+ MessageItem(const Variant::Map& h, const string& k) : headers(h), key(k) {}
+};
+
void ManagementAgentImpl::periodicProcessing()
{
string addr_key_base = "agent.ind.data.";
- sys::Mutex::ScopedLock lock(agentLock);
list<ObjectId> deleteList;
-
- if (!connected)
- return;
+ list<boost::shared_ptr<MessageItem> > message_list;
sendHeartbeat();
- moveNewObjectsLH();
-
- //
- // Clear the been-here flag on all objects in the map.
- //
- for (ObjectMap::iterator iter = managementObjects.begin();
- iter != managementObjects.end();
- iter++) {
- ManagementObject* object = iter->second.get();
- object->setFlags(0);
- if (publishAllData) {
- object->setForcePublish(true);
- }
- }
-
- publishAllData = false;
+ {
+ sys::Mutex::ScopedLock lock(agentLock);
- //
- // Process the entire object map.
- //
- uint32_t v2Objs = 0;
+ if (!connected)
+ return;
- for (ObjectMap::iterator baseIter = managementObjects.begin();
- baseIter != managementObjects.end();
- baseIter++) {
- ManagementObject* baseObject = baseIter->second.get();
+ moveNewObjectsLH();
//
- // Skip until we find a base object requiring a sent message.
+ // Clear the been-here flag on all objects in the map.
//
- if (baseObject->getFlags() == 1 ||
- (!baseObject->getConfigChanged() &&
- !baseObject->getInstChanged() &&
- !baseObject->getForcePublish() &&
- !baseObject->isDeleted()))
- continue;
-
- std::string packageName = baseObject->getPackageName();
- std::string className = baseObject->getClassName();
-
- Variant::List list_;
- string content;
- std::stringstream addr_key;
- Variant::Map headers;
-
- addr_key << addr_key_base;
- addr_key << keyifyNameStr(packageName)
- << "." << keyifyNameStr(className)
- << "." << vendorNameKey
- << "." << productNameKey
- << "." << instanceNameKey;
-
- headers["method"] = "indication";
- headers["qmf.opcode"] = "_data_indication";
- headers["qmf.content"] = "_data";
- headers["qmf.agent"] = name_address;
-
- for (ObjectMap::iterator iter = baseIter;
+ for (ObjectMap::iterator iter = managementObjects.begin();
iter != managementObjects.end();
iter++) {
ManagementObject* object = iter->second.get();
- bool send_stats, send_props;
- if (baseObject->isSameClass(*object) && object->getFlags() == 0) {
- object->setFlags(1);
- if (object->getConfigChanged() || object->getInstChanged())
- object->setUpdateTime();
+ object->setFlags(0);
+ if (publishAllData) {
+ object->setForcePublish(true);
+ }
+ }
+
+ publishAllData = false;
+
+ //
+ // Process the entire object map.
+ //
+ uint32_t v2Objs = 0;
+
+ for (ObjectMap::iterator baseIter = managementObjects.begin();
+ baseIter != managementObjects.end();
+ baseIter++) {
+ ManagementObject* baseObject = baseIter->second.get();
+
+ //
+ // Skip until we find a base object requiring a sent message.
+ //
+ if (baseObject->getFlags() == 1 ||
+ (!baseObject->getConfigChanged() &&
+ !baseObject->getInstChanged() &&
+ !baseObject->getForcePublish() &&
+ !baseObject->isDeleted()))
+ continue;
+
+ std::string packageName = baseObject->getPackageName();
+ std::string className = baseObject->getClassName();
+
+ Variant::List list_;
+ std::stringstream addr_key;
+ Variant::Map headers;
+
+ addr_key << addr_key_base;
+ addr_key << keyifyNameStr(packageName)
+ << "." << keyifyNameStr(className)
+ << "." << vendorNameKey
+ << "." << productNameKey
+ << "." << instanceNameKey;
+
+ headers["method"] = "indication";
+ headers["qmf.opcode"] = "_data_indication";
+ headers["qmf.content"] = "_data";
+ headers["qmf.agent"] = name_address;
+
+ for (ObjectMap::iterator iter = baseIter;
+ iter != managementObjects.end();
+ iter++) {
+ ManagementObject* object = iter->second.get();
+ bool send_stats, send_props;
+ if (baseObject->isSameClass(*object) && object->getFlags() == 0) {
+ object->setFlags(1);
+ if (object->getConfigChanged() || object->getInstChanged())
+ object->setUpdateTime();
+
+ send_props = (object->getConfigChanged() || object->getForcePublish() || object->isDeleted());
+ send_stats = (object->hasInst() && (object->getInstChanged() || object->getForcePublish()));
+
+ if (send_stats || send_props) {
+ Variant::Map map_;
+ Variant::Map values;
+ Variant::Map oid;
+
+ object->getObjectId().mapEncode(oid);
+ map_["_object_id"] = oid;
+ map_["_schema_id"] = mapEncodeSchemaId(object->getPackageName(),
+ object->getClassName(),
+ object->getMd5Sum());
+ object->writeTimestamps(map_);
+ object->mapEncodeValues(values, send_props, send_stats);
+ map_["_values"] = values;
+ list_.push_back(map_);
- send_props = (object->getConfigChanged() || object->getForcePublish() || object->isDeleted());
- send_stats = (object->hasInst() && (object->getInstChanged() || object->getForcePublish()));
-
- if (send_stats || send_props) {
- Variant::Map map_;
- Variant::Map values;
- Variant::Map oid;
-
- object->getObjectId().mapEncode(oid);
- map_["_object_id"] = oid;
- map_["_schema_id"] = mapEncodeSchemaId(object->getPackageName(),
- object->getClassName(),
- object->getMd5Sum());
- object->writeTimestamps(map_);
- object->mapEncodeValues(values, send_props, send_stats);
- map_["_values"] = values;
- list_.push_back(map_);
-
- if (++v2Objs >= maxV2ReplyObjs) {
- v2Objs = 0;
- ListCodec::encode(list_, content);
-
- connThreadBody.sendBuffer(content, "", headers, topicExchange, addr_key.str(), "amqp/list");
- list_.clear();
- content.clear();
- QPID_LOG(trace, "SENT DataIndication");
+ if (++v2Objs >= maxV2ReplyObjs) {
+ v2Objs = 0;
+ boost::shared_ptr<MessageItem> item(new MessageItem(headers, addr_key.str()));
+ ListCodec::encode(list_, item->content);
+ message_list.push_back(item);
+ list_.clear();
+ }
}
+
+ if (object->isDeleted())
+ deleteList.push_back(iter->first);
+ object->setForcePublish(false);
}
+ }
- if (object->isDeleted())
- deleteList.push_back(iter->first);
- object->setForcePublish(false);
+ if (!list_.empty()) {
+ boost::shared_ptr<MessageItem> item(new MessageItem(headers, addr_key.str()));
+ ListCodec::encode(list_, item->content);
+ message_list.push_back(item);
}
}
- if (!list_.empty()) {
- ListCodec::encode(list_, content);
- connThreadBody.sendBuffer(content, "", headers, topicExchange, addr_key.str(), "amqp/list");
- QPID_LOG(trace, "SENT DataIndication");
- }
+ // Delete flagged objects
+ for (list<ObjectId>::reverse_iterator iter = deleteList.rbegin();
+ iter != deleteList.rend();
+ iter++)
+ managementObjects.erase(*iter);
}
- // Delete flagged objects
- for (list<ObjectId>::reverse_iterator iter = deleteList.rbegin();
- iter != deleteList.rend();
- iter++)
- managementObjects.erase(*iter);
+ while (!message_list.empty()) {
+ boost::shared_ptr<MessageItem> item(message_list.front());
+ message_list.pop_front();
+ connThreadBody.sendBuffer(item->content, "", item->headers, topicExchange, item->key, "amqp/list");
+ QPID_LOG(trace, "SENT DataIndication");
+ }
}
@@ -1364,13 +1378,26 @@ bool ManagementAgentImpl::ConnectionThread::isSleeping() const
void ManagementAgentImpl::PublishThread::run()
{
- uint16_t totalSleep;
+ uint16_t totalSleep;
+ uint16_t sleepTime;
while (!shutdown) {
agent.periodicProcessing();
totalSleep = 0;
- while (totalSleep++ < agent.getInterval() && !shutdown) {
- ::sleep(1);
+
+ //
+ // Calculate a sleep time that is no greater than 5 seconds and
+ // no less than 1 second.
+ //
+ sleepTime = agent.getInterval();
+ if (sleepTime > 5)
+ sleepTime = 5;
+ else if (sleepTime == 0)
+ sleepTime = 1;
+
+ while (totalSleep < agent.getInterval() && !shutdown) {
+ ::sleep(sleepTime);
+ totalSleep += sleepTime;
}
}
}
diff --git a/cpp/src/qpid/agent/ManagementAgentImpl.h b/cpp/src/qpid/agent/ManagementAgentImpl.h
index bf340777d1..53f3c13a91 100644
--- a/cpp/src/qpid/agent/ManagementAgentImpl.h
+++ b/cpp/src/qpid/agent/ManagementAgentImpl.h
@@ -62,8 +62,8 @@ class ManagementAgentImpl : public ManagementAgent, public client::MessageListen
uint16_t intervalSeconds = 10,
bool useExternalThread = false,
const std::string& storeFile = "",
- const std::string& uid = "guest",
- const std::string& pwd = "guest",
+ const std::string& uid = "",
+ const std::string& pwd = "",
const std::string& mech = "PLAIN",
const std::string& proto = "tcp");
void init(const management::ConnectionSettings& settings,
diff --git a/cpp/src/qpid/amqp_0_10/Codecs.cpp b/cpp/src/qpid/amqp_0_10/Codecs.cpp
index 0fbe2a60b9..b976a5d09b 100644
--- a/cpp/src/qpid/amqp_0_10/Codecs.cpp
+++ b/cpp/src/qpid/amqp_0_10/Codecs.cpp
@@ -127,10 +127,10 @@ Variant toVariant(boost::shared_ptr<FieldValue> in)
switch (in->getType()) {
//Fixed Width types:
case 0x01: out.setEncoding(amqp0_10_binary);
- case 0x02: out = in->getIntegerValue<int8_t, 1>(); break;
- case 0x03: out = in->getIntegerValue<uint8_t, 1>(); break;
+ case 0x02: out = in->getIntegerValue<int8_t>(); break;
+ case 0x03: out = in->getIntegerValue<uint8_t>(); break;
case 0x04: break; //TODO: iso-8859-15 char
- case 0x08: out = static_cast<bool>(in->getIntegerValue<uint8_t, 1>()); break;
+ case 0x08: out = static_cast<bool>(in->getIntegerValue<uint8_t>()); break;
case 0x10: out.setEncoding(amqp0_10_binary);
case 0x11: out = in->getIntegerValue<int16_t, 2>(); break;
case 0x12: out = in->getIntegerValue<uint16_t, 2>(); break;
diff --git a/cpp/src/qpid/amqp_0_10/SessionHandler.cpp b/cpp/src/qpid/amqp_0_10/SessionHandler.cpp
index b113d49a73..578598a146 100644
--- a/cpp/src/qpid/amqp_0_10/SessionHandler.cpp
+++ b/cpp/src/qpid/amqp_0_10/SessionHandler.cpp
@@ -130,9 +130,6 @@ void SessionHandler::handleException(const qpid::SessionException& e)
}
namespace {
-bool isControl(const AMQFrame& f) {
- return f.getMethod() && f.getMethod()->type() == framing::SEGMENT_TYPE_CONTROL;
-}
bool isCommand(const AMQFrame& f) {
return f.getMethod() && f.getMethod()->type() == framing::SEGMENT_TYPE_COMMAND;
}
@@ -191,9 +188,10 @@ void SessionHandler::detach(const std::string& name) {
void SessionHandler::detached(const std::string& name, uint8_t code) {
CHECK_NAME(name, "session.detached");
awaitingDetached = false;
- if (code != session::DETACH_CODE_NORMAL)
+ if (code != session::DETACH_CODE_NORMAL) {
+ sendReady = receiveReady = false;
channelException(convert(code), "session.detached from peer.");
- else {
+ } else {
handleDetach();
}
}
diff --git a/cpp/src/qpid/amqp_0_10/SessionHandler.h b/cpp/src/qpid/amqp_0_10/SessionHandler.h
index a87a1a155f..8b072fa05c 100644
--- a/cpp/src/qpid/amqp_0_10/SessionHandler.h
+++ b/cpp/src/qpid/amqp_0_10/SessionHandler.h
@@ -41,8 +41,8 @@ namespace amqp_0_10 {
* to a session state.
*/
-class SessionHandler : public framing::AMQP_AllOperations::SessionHandler,
- public framing::FrameHandler::InOutHandler
+class QPID_COMMON_CLASS_EXTERN SessionHandler : public framing::AMQP_AllOperations::SessionHandler,
+ public framing::FrameHandler::InOutHandler
{
public:
QPID_COMMON_EXTERN SessionHandler(framing::FrameHandler* out=0, uint16_t channel=0);
@@ -66,7 +66,7 @@ class SessionHandler : public framing::AMQP_AllOperations::SessionHandler,
QPID_COMMON_EXTERN void handleException(const qpid::SessionException& e);
/** True if the handler is ready to send and receive */
- bool ready() const;
+ QPID_COMMON_EXTERN bool ready() const;
// Protocol methods
QPID_COMMON_EXTERN void attach(const std::string& name, bool force);
diff --git a/cpp/src/qpid/broker/AsyncCompletion.h b/cpp/src/qpid/broker/AsyncCompletion.h
new file mode 100644
index 0000000000..fef994438f
--- /dev/null
+++ b/cpp/src/qpid/broker/AsyncCompletion.h
@@ -0,0 +1,201 @@
+#ifndef _AsyncCompletion_
+#define _AsyncCompletion_
+
+/*
+ *
+ * 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/intrusive_ptr.hpp>
+
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/sys/AtomicValue.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/Monitor.h"
+
+namespace qpid {
+namespace broker {
+
+/**
+ * Class to implement asynchronous notification of completion.
+ *
+ * Use-case: An "initiator" needs to wait for a set of "completers" to
+ * finish a unit of work before an action can occur. This object
+ * tracks the progress of the set of completers, and allows the action
+ * to occur once all completers have signalled that they are done.
+ *
+ * The initiator and completers may be running in separate threads.
+ *
+ * The initiating thread is the thread that initiates the action,
+ * i.e. the connection read thread.
+ *
+ * A completing thread is any thread that contributes to completion,
+ * e.g. a store thread that does an async write.
+ * There may be zero or more completers.
+ *
+ * When the work is complete, a callback is invoked. The callback
+ * may be invoked in the Initiator thread, or one of the Completer
+ * threads. The callback is passed a flag indicating whether or not
+ * the callback is running under the context of the Initiator thread.
+ *
+ * Use model:
+ * 1) Initiator thread invokes begin()
+ * 2) After begin() has been invoked, zero or more Completers invoke
+ * startCompleter(). Completers may be running in the same or
+ * different thread as the Initiator, as long as they guarantee that
+ * startCompleter() is invoked at least once before the Initiator invokes end().
+ * 3) Completers may invoke finishCompleter() at any time, even after the
+ * initiator has invoked end(). finishCompleter() may be called from any
+ * thread.
+ * 4) startCompleter()/finishCompleter() calls "nest": for each call to
+ * startCompleter(), a corresponding call to finishCompleter() must be made.
+ * Once the last finishCompleter() is called, the Completer must no longer
+ * reference the completion object.
+ * 5) The Initiator invokes end() at the point where it has finished
+ * dispatching work to the Completers, and is prepared for the callback
+ * handler to be invoked. Note: if there are no outstanding Completers
+ * pending when the Initiator invokes end(), the callback will be invoked
+ * directly, and the sync parameter will be set true. This indicates to the
+ * Initiator that the callback is executing in the context of the end() call,
+ * and the Initiator is free to optimize the handling of the completion,
+ * assuming no need for synchronization with Completer threads.
+ */
+
+class AsyncCompletion
+{
+ public:
+
+ /** Supplied by the Initiator to the end() method, allows for a callback
+ * when all outstanding completers are done. If the callback cannot be
+ * made during the end() call, the clone() method must supply a copy of
+ * this callback object that persists after end() returns. The cloned
+ * callback object will be used by the last completer thread, and
+ * released when the callback returns.
+ */
+ class Callback : public RefCounted
+ {
+ public:
+ virtual void completed(bool) = 0;
+ virtual boost::intrusive_ptr<Callback> clone() = 0;
+ };
+
+ private:
+ mutable qpid::sys::AtomicValue<uint32_t> completionsNeeded;
+ mutable qpid::sys::Monitor callbackLock;
+ bool inCallback, active;
+
+ void invokeCallback(bool sync) {
+ qpid::sys::Mutex::ScopedLock l(callbackLock);
+ if (active) {
+ if (callback.get()) {
+ inCallback = true;
+ {
+ qpid::sys::Mutex::ScopedUnlock ul(callbackLock);
+ callback->completed(sync);
+ }
+ inCallback = false;
+ callback = boost::intrusive_ptr<Callback>();
+ callbackLock.notifyAll();
+ }
+ active = false;
+ }
+ }
+
+ protected:
+ /** Invoked when all completers have signalled that they have completed
+ * (via calls to finishCompleter()). bool == true if called via end()
+ */
+ boost::intrusive_ptr<Callback> callback;
+
+ public:
+ AsyncCompletion() : completionsNeeded(0), inCallback(false), active(true) {};
+ virtual ~AsyncCompletion() { cancel(); }
+
+
+ /** True when all outstanding operations have compeleted
+ */
+ bool isDone()
+ {
+ return !active;
+ }
+
+ /** Called to signal the start of an asynchronous operation. The operation
+ * is considered pending until finishCompleter() is called.
+ * E.g. called when initiating an async store operation.
+ */
+ void startCompleter() { ++completionsNeeded; }
+
+ /** Called by completer to signal that it has finished the operation started
+ * when startCompleter() was invoked.
+ * e.g. called when async write complete.
+ */
+ void finishCompleter()
+ {
+ if (--completionsNeeded == 0) {
+ invokeCallback(false);
+ }
+ }
+
+ /** called by initiator before any calls to startCompleter can be done.
+ */
+ void begin()
+ {
+ ++completionsNeeded;
+ }
+
+ /** called by initiator after all potential completers have called
+ * startCompleter().
+ */
+ void end(Callback& cb)
+ {
+ assert(completionsNeeded.get() > 0); // ensure begin() has been called!
+ // the following only "decrements" the count if it is 1. This means
+ // there are no more outstanding completers and we are done.
+ if (completionsNeeded.boolCompareAndSwap(1, 0)) {
+ // done! Complete immediately
+ cb.completed(true);
+ return;
+ }
+
+ // the compare-and-swap did not succeed. This means there are
+ // outstanding completers pending (count > 1). Get a persistent
+ // Callback object to use when the last completer is done.
+ // Decrement after setting up the callback ensures that pending
+ // completers cannot touch the callback until it is ready.
+ callback = cb.clone();
+ if (--completionsNeeded == 0) {
+ // note that a completer may have completed during the
+ // callback setup or decrement:
+ invokeCallback(true);
+ }
+ }
+
+ /** may be called by Initiator to cancel the callback. Will wait for
+ * callback to complete if in progress.
+ */
+ virtual void cancel() {
+ qpid::sys::Mutex::ScopedLock l(callbackLock);
+ while (inCallback) callbackLock.wait();
+ callback = boost::intrusive_ptr<Callback>();
+ active = false;
+ }
+};
+
+}} // qpid::broker::
+#endif /*!_AsyncCompletion_*/
diff --git a/cpp/src/qpid/broker/Bridge.cpp b/cpp/src/qpid/broker/Bridge.cpp
index 7fbbf4e2c4..c709606c17 100644
--- a/cpp/src/qpid/broker/Bridge.cpp
+++ b/cpp/src/qpid/broker/Bridge.cpp
@@ -164,6 +164,12 @@ void Bridge::destroy()
listener(this);
}
+bool Bridge::isSessionReady() const
+{
+ SessionHandler& sessionHandler = conn->getChannel(id);
+ return sessionHandler.ready();
+}
+
void Bridge::setPersistenceId(uint64_t pId) const
{
persistenceId = pId;
diff --git a/cpp/src/qpid/broker/Bridge.h b/cpp/src/qpid/broker/Bridge.h
index a846254c57..8b4559a871 100644
--- a/cpp/src/qpid/broker/Bridge.h
+++ b/cpp/src/qpid/broker/Bridge.h
@@ -59,6 +59,8 @@ public:
void destroy();
bool isDurable() { return args.i_durable; }
+ bool isSessionReady() const;
+
management::ManagementObject* GetManagementObject() const;
management::Manageable::status_t ManagementMethod(uint32_t methodId,
management::Args& args,
diff --git a/cpp/src/qpid/broker/Broker.cpp b/cpp/src/qpid/broker/Broker.cpp
index 3c692fc368..ec3cf9d340 100644
--- a/cpp/src/qpid/broker/Broker.cpp
+++ b/cpp/src/qpid/broker/Broker.cpp
@@ -20,6 +20,7 @@
*/
#include "qpid/broker/Broker.h"
+#include "qpid/broker/ConnectionState.h"
#include "qpid/broker/DirectExchange.h"
#include "qpid/broker/FanOutExchange.h"
#include "qpid/broker/HeadersExchange.h"
@@ -31,12 +32,26 @@
#include "qpid/broker/TopicExchange.h"
#include "qpid/broker/Link.h"
#include "qpid/broker/ExpiryPolicy.h"
+#include "qpid/broker/QueueFlowLimit.h"
+#include "qpid/broker/MessageGroupManager.h"
#include "qmf/org/apache/qpid/broker/Package.h"
+#include "qmf/org/apache/qpid/broker/ArgsBrokerCreate.h"
+#include "qmf/org/apache/qpid/broker/ArgsBrokerDelete.h"
+#include "qmf/org/apache/qpid/broker/ArgsBrokerQuery.h"
#include "qmf/org/apache/qpid/broker/ArgsBrokerEcho.h"
#include "qmf/org/apache/qpid/broker/ArgsBrokerGetLogLevel.h"
#include "qmf/org/apache/qpid/broker/ArgsBrokerQueueMoveMessages.h"
#include "qmf/org/apache/qpid/broker/ArgsBrokerSetLogLevel.h"
+#include "qmf/org/apache/qpid/broker/ArgsBrokerSetTimestampConfig.h"
+#include "qmf/org/apache/qpid/broker/ArgsBrokerGetTimestampConfig.h"
+#include "qmf/org/apache/qpid/broker/EventExchangeDeclare.h"
+#include "qmf/org/apache/qpid/broker/EventExchangeDelete.h"
+#include "qmf/org/apache/qpid/broker/EventQueueDeclare.h"
+#include "qmf/org/apache/qpid/broker/EventQueueDelete.h"
+#include "qmf/org/apache/qpid/broker/EventBind.h"
+#include "qmf/org/apache/qpid/broker/EventUnbind.h"
+#include "qpid/amqp_0_10/Codecs.h"
#include "qpid/management/ManagementDirectExchange.h"
#include "qpid/management/ManagementTopicExchange.h"
#include "qpid/log/Logger.h"
@@ -44,7 +59,9 @@
#include "qpid/log/Statement.h"
#include "qpid/log/posix/SinkOptions.h"
#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/FieldTable.h"
#include "qpid/framing/ProtocolInitiation.h"
+#include "qpid/framing/reply_exceptions.h"
#include "qpid/framing/Uuid.h"
#include "qpid/sys/ProtocolFactory.h"
#include "qpid/sys/Poller.h"
@@ -76,7 +93,10 @@ using qpid::management::ManagementAgent;
using qpid::management::ManagementObject;
using qpid::management::Manageable;
using qpid::management::Args;
+using qpid::management::getManagementExecutionContext;
+using qpid::types::Variant;
using std::string;
+using std::make_pair;
namespace _qmf = qmf::org::apache::qpid::broker;
@@ -103,7 +123,12 @@ Broker::Options::Options(const std::string& name) :
maxSessionRate(0),
asyncQueueEvents(false), // Must be false in a cluster.
qmf2Support(true),
- qmf1Support(true)
+ qmf1Support(true),
+ queueFlowStopRatio(80),
+ queueFlowResumeRatio(70),
+ queueThresholdEventRatio(80),
+ defaultMsgGroup("qpid.no-group"),
+ timestampRcvMsgs(false) // set the 0.10 timestamp delivery property
{
int c = sys::SystemInfo::concurrency();
workerThreads=c+1;
@@ -134,9 +159,14 @@ Broker::Options::Options(const std::string& name) :
("tcp-nodelay", optValue(tcpNoDelay), "Set TCP_NODELAY on TCP connections")
("require-encryption", optValue(requireEncrypted), "Only accept connections that are encrypted")
("known-hosts-url", optValue(knownHosts, "URL or 'none'"), "URL to send as 'known-hosts' to clients ('none' implies empty list)")
- ("sasl-config", optValue(saslConfigPath, "FILE"), "gets sasl config from nonstandard location")
+ ("sasl-config", optValue(saslConfigPath, "DIR"), "gets sasl config info from nonstandard location")
("max-session-rate", optValue(maxSessionRate, "MESSAGES/S"), "Sets the maximum message rate per session (0=unlimited)")
- ("async-queue-events", optValue(asyncQueueEvents, "yes|no"), "Set Queue Events async, used for services like replication");
+ ("async-queue-events", optValue(asyncQueueEvents, "yes|no"), "Set Queue Events async, used for services like replication")
+ ("default-flow-stop-threshold", optValue(queueFlowStopRatio, "PERCENT"), "Percent of queue's maximum capacity at which flow control is activated.")
+ ("default-flow-resume-threshold", optValue(queueFlowResumeRatio, "PERCENT"), "Percent of queue's maximum capacity at which flow control is de-activated.")
+ ("default-event-threshold-ratio", optValue(queueThresholdEventRatio, "%age of limit"), "The ratio of any specified queue limit at which an event will be raised")
+ ("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.");
}
const std::string empty;
@@ -166,9 +196,10 @@ Broker::Broker(const Broker::Options& conf) :
conf.replayFlushLimit*1024, // convert kb to bytes.
conf.replayHardLimit*1024),
*this),
- queueCleaner(queues, timer),
- queueEvents(poller,!conf.asyncQueueEvents),
+ queueCleaner(queues, &timer),
+ queueEvents(poller,!conf.asyncQueueEvents),
recovery(true),
+ inCluster(false),
clusterUpdatee(false),
expiryPolicy(new ExpiryPolicy),
connectionCounter(conf.maxConnections),
@@ -225,8 +256,11 @@ Broker::Broker(const Broker::Options& conf) :
// Early-Initialize plugins
Plugin::earlyInitAll(*this);
+ QueueFlowLimit::setDefaults(conf.queueLimit, conf.queueFlowStopRatio, conf.queueFlowResumeRatio);
+ MessageGroupManager::setDefaults(conf.defaultMsgGroup);
+
// If no plugin store module registered itself, set up the null store.
- if (NullMessageStore::isNullStore(store.get()))
+ if (NullMessageStore::isNullStore(store.get()))
setStore();
exchanges.declare(empty, DirectExchange::typeName); // Default exchange.
@@ -271,6 +305,11 @@ Broker::Broker(const Broker::Options& conf) :
else
QPID_LOG(info, "Management not enabled");
+ // this feature affects performance, so let's be sure that gets logged!
+ if (conf.timestampRcvMsgs) {
+ QPID_LOG(notice, "Receive message timestamping is ENABLED.");
+ }
+
/**
* SASL setup, can fail and terminate startup
*/
@@ -345,14 +384,14 @@ void Broker::run() {
Dispatcher d(poller);
int numIOThreads = config.workerThreads;
std::vector<Thread> t(numIOThreads-1);
-
+
// Run n-1 io threads
for (int i=0; i<numIOThreads-1; ++i)
t[i] = Thread(d);
-
+
// Run final thread
d.run();
-
+
// Now wait for n-1 io threads to exit
for (int i=0; i<numIOThreads-1; ++i) {
t[i].join();
@@ -399,9 +438,9 @@ Manageable::status_t Broker::ManagementMethod (uint32_t methodId,
{
case _qmf::Broker::METHOD_ECHO :
QPID_LOG (debug, "Broker::echo("
- << dynamic_cast<_qmf::ArgsBrokerEcho&>(args).io_sequence
- << ", "
- << dynamic_cast<_qmf::ArgsBrokerEcho&>(args).io_body
+ << dynamic_cast<_qmf::ArgsBrokerEcho&>(args).io_sequence
+ << ", "
+ << dynamic_cast<_qmf::ArgsBrokerEcho&>(args).io_body
<< ")");
status = Manageable::STATUS_OK;
break;
@@ -409,8 +448,9 @@ Manageable::status_t Broker::ManagementMethod (uint32_t methodId,
_qmf::ArgsBrokerConnect& hp=
dynamic_cast<_qmf::ArgsBrokerConnect&>(args);
- QPID_LOG (debug, "Broker::connect()");
string transport = hp.i_transport.empty() ? TCP_TRANSPORT : hp.i_transport;
+ QPID_LOG (debug, "Broker::connect() " << hp.i_host << ":" << hp.i_port << "; transport=" << transport <<
+ "; durable=" << (hp.i_durable?"T":"F") << "; authMech=\"" << hp.i_authMechanism << "\"");
if (!getProtocolFactory(transport)) {
QPID_LOG(error, "Transport '" << transport << "' not supported");
return Manageable::STATUS_NOT_IMPLEMENTED;
@@ -427,9 +467,9 @@ Manageable::status_t Broker::ManagementMethod (uint32_t methodId,
_qmf::ArgsBrokerQueueMoveMessages& moveArgs=
dynamic_cast<_qmf::ArgsBrokerQueueMoveMessages&>(args);
QPID_LOG (debug, "Broker::queueMoveMessages()");
- if (queueMoveMessages(moveArgs.i_srcQueue, moveArgs.i_destQueue, moveArgs.i_qty))
+ if (queueMoveMessages(moveArgs.i_srcQueue, moveArgs.i_destQueue, moveArgs.i_qty, moveArgs.i_filter))
status = Manageable::STATUS_OK;
- else
+ else
return Manageable::STATUS_PARAMETER_INVALID;
break;
}
@@ -443,6 +483,38 @@ Manageable::status_t Broker::ManagementMethod (uint32_t methodId,
QPID_LOG (debug, "Broker::getLogLevel()");
status = Manageable::STATUS_OK;
break;
+ case _qmf::Broker::METHOD_CREATE :
+ {
+ _qmf::ArgsBrokerCreate& a = dynamic_cast<_qmf::ArgsBrokerCreate&>(args);
+ createObject(a.i_type, a.i_name, a.i_properties, a.i_strict, getManagementExecutionContext());
+ status = Manageable::STATUS_OK;
+ break;
+ }
+ case _qmf::Broker::METHOD_DELETE :
+ {
+ _qmf::ArgsBrokerDelete& a = dynamic_cast<_qmf::ArgsBrokerDelete&>(args);
+ deleteObject(a.i_type, a.i_name, a.i_options, getManagementExecutionContext());
+ status = Manageable::STATUS_OK;
+ break;
+ }
+ case _qmf::Broker::METHOD_QUERY :
+ {
+ _qmf::ArgsBrokerQuery& a = dynamic_cast<_qmf::ArgsBrokerQuery&>(args);
+ status = queryObject(a.i_type, a.i_name, a.o_results, getManagementExecutionContext());
+ break;
+ }
+ case _qmf::Broker::METHOD_GETTIMESTAMPCONFIG:
+ {
+ _qmf::ArgsBrokerGetTimestampConfig& a = dynamic_cast<_qmf::ArgsBrokerGetTimestampConfig&>(args);
+ status = getTimestampConfig(a.o_receive, getManagementExecutionContext());
+ break;
+ }
+ case _qmf::Broker::METHOD_SETTIMESTAMPCONFIG:
+ {
+ _qmf::ArgsBrokerSetTimestampConfig& a = dynamic_cast<_qmf::ArgsBrokerSetTimestampConfig&>(args);
+ status = setTimestampConfig(a.i_receive, getManagementExecutionContext());
+ break;
+ }
default:
QPID_LOG (debug, "Broker ManagementMethod not implemented: id=" << methodId << "]");
status = Manageable::STATUS_NOT_IMPLEMENTED;
@@ -452,6 +524,240 @@ Manageable::status_t Broker::ManagementMethod (uint32_t methodId,
return status;
}
+namespace
+{
+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 DURABLE("durable");
+const std::string AUTO_DELETE("auto-delete");
+const std::string ALTERNATE_EXCHANGE("alternate-exchange");
+const std::string EXCHANGE_TYPE("exchange-type");
+const std::string QUEUE_NAME("queue");
+const std::string EXCHANGE_NAME("exchange");
+
+const std::string ATTRIBUTE_TIMESTAMP_0_10("timestamp-0.10");
+
+const std::string _TRUE("true");
+const std::string _FALSE("false");
+}
+
+struct InvalidBindingIdentifier : public qpid::Exception
+{
+ InvalidBindingIdentifier(const std::string& name) : qpid::Exception(name) {}
+ std::string getPrefix() const { return "invalid binding"; }
+};
+
+struct BindingIdentifier
+{
+ std::string exchange;
+ std::string queue;
+ std::string key;
+
+ BindingIdentifier(const std::string& name)
+ {
+ std::vector<std::string> path;
+ split(path, name, "/");
+ switch (path.size()) {
+ case 1:
+ queue = path[0];
+ break;
+ case 2:
+ exchange = path[0];
+ queue = path[1];
+ break;
+ case 3:
+ exchange = path[0];
+ queue = path[1];
+ key = path[2];
+ break;
+ default:
+ throw InvalidBindingIdentifier(name);
+ }
+ }
+};
+
+struct ObjectAlreadyExists : public qpid::Exception
+{
+ ObjectAlreadyExists(const std::string& name) : qpid::Exception(name) {}
+ std::string getPrefix() const { return "object already exists"; }
+};
+
+struct UnknownObjectType : public qpid::Exception
+{
+ UnknownObjectType(const std::string& type) : qpid::Exception(type) {}
+ std::string getPrefix() const { return "unknown object type"; }
+};
+
+void Broker::createObject(const std::string& type, const std::string& name,
+ const Variant::Map& properties, bool /*strict*/, const ConnectionState* context)
+{
+ std::string userId;
+ std::string connectionId;
+ if (context) {
+ userId = context->getUserId();
+ connectionId = context->getUrl();
+ }
+ //TODO: implement 'strict' option (check there are no unrecognised properties)
+ QPID_LOG (debug, "Broker::create(" << type << ", " << name << "," << properties << ")");
+ if (type == TYPE_QUEUE) {
+ bool durable(false);
+ bool autodelete(false);
+ std::string alternateExchange;
+ Variant::Map extensions;
+ for (Variant::Map::const_iterator i = properties.begin(); i != properties.end(); ++i) {
+ // extract durable, auto-delete and alternate-exchange properties
+ if (i->first == DURABLE) durable = i->second;
+ else if (i->first == AUTO_DELETE) autodelete = i->second;
+ else if (i->first == ALTERNATE_EXCHANGE) alternateExchange = i->second.asString();
+ //treat everything else as extension properties
+ else extensions[i->first] = i->second;
+ }
+ framing::FieldTable arguments;
+ amqp_0_10::translate(extensions, arguments);
+
+ std::pair<boost::shared_ptr<Queue>, bool> result =
+ createQueue(name, durable, autodelete, 0, alternateExchange, arguments, userId, connectionId);
+ if (!result.second) {
+ throw ObjectAlreadyExists(name);
+ }
+ } else if (type == TYPE_EXCHANGE || type == TYPE_TOPIC) {
+ bool durable(false);
+ std::string exchangeType("topic");
+ std::string alternateExchange;
+ Variant::Map extensions;
+ for (Variant::Map::const_iterator i = properties.begin(); i != properties.end(); ++i) {
+ // extract durable, auto-delete and alternate-exchange properties
+ if (i->first == DURABLE) durable = i->second;
+ else if (i->first == EXCHANGE_TYPE) exchangeType = i->second.asString();
+ else if (i->first == ALTERNATE_EXCHANGE) alternateExchange = i->second.asString();
+ //treat everything else as extension properties
+ else extensions[i->first] = i->second;
+ }
+ framing::FieldTable arguments;
+ amqp_0_10::translate(extensions, arguments);
+
+ try {
+ std::pair<boost::shared_ptr<Exchange>, bool> result =
+ createExchange(name, exchangeType, durable, alternateExchange, arguments, userId, connectionId);
+ if (!result.second) {
+ throw ObjectAlreadyExists(name);
+ }
+ } catch (const UnknownExchangeTypeException&) {
+ throw Exception(QPID_MSG("Invalid exchange type: " << exchangeType));
+ }
+ } else if (type == TYPE_BINDING) {
+ BindingIdentifier binding(name);
+ std::string exchangeType("topic");
+ Variant::Map extensions;
+ for (Variant::Map::const_iterator i = properties.begin(); i != properties.end(); ++i) {
+ // extract durable, auto-delete and alternate-exchange properties
+ if (i->first == EXCHANGE_TYPE) exchangeType = i->second.asString();
+ //treat everything else as extension properties
+ else extensions[i->first] = i->second;
+ }
+ framing::FieldTable arguments;
+ amqp_0_10::translate(extensions, arguments);
+
+ bind(binding.queue, binding.exchange, binding.key, arguments, userId, connectionId);
+ } else {
+ throw UnknownObjectType(type);
+ }
+}
+
+void Broker::deleteObject(const std::string& type, const std::string& name,
+ const Variant::Map& options, const ConnectionState* context)
+{
+ std::string userId;
+ std::string connectionId;
+ if (context) {
+ userId = context->getUserId();
+ connectionId = context->getUrl();
+ }
+ QPID_LOG (debug, "Broker::delete(" << type << ", " << name << "," << options << ")");
+ if (type == TYPE_QUEUE) {
+ deleteQueue(name, userId, connectionId);
+ } else if (type == TYPE_EXCHANGE || type == TYPE_TOPIC) {
+ deleteExchange(name, userId, connectionId);
+ } else if (type == TYPE_BINDING) {
+ BindingIdentifier binding(name);
+ unbind(binding.queue, binding.exchange, binding.key, userId, connectionId);
+ } else {
+ throw UnknownObjectType(type);
+ }
+
+}
+
+Manageable::status_t Broker::queryObject(const std::string& type,
+ const std::string& name,
+ Variant::Map& results,
+ const ConnectionState* context)
+{
+ std::string userId;
+ std::string connectionId;
+ if (context) {
+ userId = context->getUserId();
+ connectionId = context->getUrl();
+ }
+ QPID_LOG (debug, "Broker::query(" << type << ", " << name << ")");
+
+ if (type == TYPE_QUEUE)
+ return queryQueue( name, userId, connectionId, results );
+
+ if (type == TYPE_EXCHANGE ||
+ type == TYPE_TOPIC ||
+ type == TYPE_BINDING)
+ return Manageable::STATUS_NOT_IMPLEMENTED;
+
+ throw UnknownObjectType(type);
+}
+
+Manageable::status_t Broker::queryQueue( const std::string& name,
+ const std::string& userId,
+ const std::string& /*connectionId*/,
+ Variant::Map& results )
+{
+ (void) results;
+ if (acl) {
+ if (!acl->authorise(userId, acl::ACT_ACCESS, acl::OBJ_QUEUE, name, NULL) )
+ throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied queue query request from " << userId));
+ }
+
+ boost::shared_ptr<Queue> q(queues.find(name));
+ if (!q) {
+ QPID_LOG(error, "Query failed: queue not found, name=" << name);
+ return Manageable::STATUS_UNKNOWN_OBJECT;
+ }
+ q->query( results );
+ return Manageable::STATUS_OK;;
+}
+
+Manageable::status_t Broker::getTimestampConfig(bool& receive,
+ const ConnectionState* context)
+{
+ std::string name; // none needed for broker
+ std::string userId = context->getUserId();
+ if (acl && !acl->authorise(userId, acl::ACT_ACCESS, acl::OBJ_BROKER, name, NULL)) {
+ throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied broker timestamp get request from " << userId));
+ }
+ receive = config.timestampRcvMsgs;
+ return Manageable::STATUS_OK;
+}
+
+Manageable::status_t Broker::setTimestampConfig(const bool receive,
+ const ConnectionState* context)
+{
+ std::string name; // none needed for broker
+ std::string userId = context->getUserId();
+ if (acl && !acl->authorise(userId, acl::ACT_UPDATE, acl::OBJ_BROKER, name, NULL)) {
+ throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied broker timestamp set request from " << userId));
+ }
+ config.timestampRcvMsgs = receive;
+ QPID_LOG(notice, "Receive message timestamping is " << ((config.timestampRcvMsgs) ? "ENABLED." : "DISABLED."));
+ return Manageable::STATUS_OK;
+}
+
void Broker::setLogLevel(const std::string& level)
{
QPID_LOG(notice, "Changing log level to " << level);
@@ -466,7 +772,7 @@ std::string Broker::getLogLevel()
const std::vector<std::string>& selectors = qpid::log::Logger::instance().getOptions().selectors;
for (std::vector<std::string>::const_iterator i = selectors.begin(); i != selectors.end(); ++i) {
if (i != selectors.begin()) level += std::string(",");
- level += *i;
+ level += *i;
}
return level;
}
@@ -499,7 +805,7 @@ void Broker::accept() {
}
void Broker::connect(
- const std::string& host, uint16_t port, const std::string& transport,
+ const std::string& host, const std::string& port, const std::string& transport,
boost::function2<void, int, std::string> failed,
sys::ConnectionCodec::Factory* f)
{
@@ -515,13 +821,14 @@ void Broker::connect(
{
url.throwIfEmpty();
const Address& addr=url[0];
- connect(addr.host, addr.port, addr.protocol, failed, f);
+ connect(addr.host, boost::lexical_cast<std::string>(addr.port), addr.protocol, failed, f);
}
uint32_t Broker::queueMoveMessages(
const std::string& srcQueue,
const std::string& destQueue,
- uint32_t qty)
+ uint32_t qty,
+ const Variant::Map& filter)
{
Queue::shared_ptr src_queue = queues.find(srcQueue);
if (!src_queue)
@@ -530,7 +837,7 @@ uint32_t Broker::queueMoveMessages(
if (!dest_queue)
return 0;
- return src_queue->move(dest_queue, qty);
+ return src_queue->move(dest_queue, qty, &filter);
}
@@ -548,9 +855,228 @@ bool Broker::deferDeliveryImpl(const std::string& ,
void Broker::setClusterTimer(std::auto_ptr<sys::Timer> t) {
clusterTimer = t;
+ queueCleaner.setTimer(clusterTimer.get());
+ dtxManager.setTimer(*clusterTimer.get());
}
const std::string Broker::TCP_TRANSPORT("tcp");
+
+std::pair<boost::shared_ptr<Queue>, bool> Broker::createQueue(
+ const std::string& name,
+ bool durable,
+ bool autodelete,
+ const OwnershipToken* owner,
+ const std::string& alternateExchange,
+ const qpid::framing::FieldTable& arguments,
+ const std::string& userId,
+ const std::string& connectionId)
+{
+ if (acl) {
+ std::map<acl::Property, std::string> params;
+ params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange));
+ params.insert(make_pair(acl::PROP_PASSIVE, _FALSE));
+ params.insert(make_pair(acl::PROP_DURABLE, durable ? _TRUE : _FALSE));
+ params.insert(make_pair(acl::PROP_EXCLUSIVE, owner ? _TRUE : _FALSE));
+ params.insert(make_pair(acl::PROP_AUTODELETE, autodelete ? _TRUE : _FALSE));
+ params.insert(make_pair(acl::PROP_POLICYTYPE, arguments.getAsString("qpid.policy_type")));
+ params.insert(make_pair(acl::PROP_MAXQUEUECOUNT, boost::lexical_cast<string>(arguments.getAsInt("qpid.max_count"))));
+ params.insert(make_pair(acl::PROP_MAXQUEUESIZE, boost::lexical_cast<string>(arguments.getAsInt64("qpid.max_size"))));
+
+ if (!acl->authorise(userId,acl::ACT_CREATE,acl::OBJ_QUEUE,name,&params) )
+ throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied queue create request from " << userId));
+ }
+
+ Exchange::shared_ptr alternate;
+ if (!alternateExchange.empty()) {
+ alternate = exchanges.get(alternateExchange);
+ if (!alternate) throw framing::NotFoundException(QPID_MSG("Alternate exchange does not exist: " << alternateExchange));
+ }
+
+ std::pair<Queue::shared_ptr, bool> result = queues.declare(name, durable, autodelete, owner, alternate, arguments);
+ if (result.second) {
+ //add default binding:
+ result.first->bind(exchanges.getDefault(), name);
+
+ if (managementAgent.get()) {
+ //TODO: debatable whether we should raise an event here for
+ //create when this is a 'declare' event; ideally add a create
+ //event instead?
+ managementAgent->raiseEvent(
+ _qmf::EventQueueDeclare(connectionId, userId, name,
+ durable, owner, autodelete,
+ ManagementAgent::toMap(arguments),
+ "created"));
+ }
+ }
+ return result;
+}
+
+void Broker::deleteQueue(const std::string& name, const std::string& userId,
+ const std::string& connectionId, QueueFunctor check)
+{
+ if (acl && !acl->authorise(userId,acl::ACT_DELETE,acl::OBJ_QUEUE,name,NULL)) {
+ throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied queue delete request from " << userId));
+ }
+
+ Queue::shared_ptr queue = queues.find(name);
+ if (queue) {
+ if (check) check(queue);
+ queues.destroy(name);
+ queue->destroyed();
+ } else {
+ throw framing::NotFoundException(QPID_MSG("Delete failed. No such queue: " << name));
+ }
+
+ if (managementAgent.get())
+ managementAgent->raiseEvent(_qmf::EventQueueDelete(connectionId, userId, name));
+
+}
+
+std::pair<Exchange::shared_ptr, bool> Broker::createExchange(
+ const std::string& name,
+ const std::string& type,
+ bool durable,
+ const std::string& alternateExchange,
+ const qpid::framing::FieldTable& arguments,
+ const std::string& userId,
+ const std::string& connectionId)
+{
+ if (acl) {
+ std::map<acl::Property, std::string> params;
+ params.insert(make_pair(acl::PROP_TYPE, type));
+ params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange));
+ params.insert(make_pair(acl::PROP_PASSIVE, _FALSE));
+ params.insert(make_pair(acl::PROP_DURABLE, durable ? _TRUE : _FALSE));
+ if (!acl->authorise(userId,acl::ACT_CREATE,acl::OBJ_EXCHANGE,name,&params) )
+ throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange create request from " << userId));
+ }
+
+ Exchange::shared_ptr alternate;
+ if (!alternateExchange.empty()) {
+ alternate = exchanges.get(alternateExchange);
+ if (!alternate) throw framing::NotFoundException(QPID_MSG("Alternate exchange does not exist: " << alternateExchange));
+ }
+
+ std::pair<Exchange::shared_ptr, bool> result;
+ result = exchanges.declare(name, type, durable, arguments);
+ if (result.second) {
+ if (alternate) {
+ result.first->setAlternate(alternate);
+ alternate->incAlternateUsers();
+ }
+ if (durable) {
+ store->create(*result.first, arguments);
+ }
+ if (managementAgent.get()) {
+ //TODO: debatable whether we should raise an event here for
+ //create when this is a 'declare' event; ideally add a create
+ //event instead?
+ managementAgent->raiseEvent(_qmf::EventExchangeDeclare(connectionId,
+ userId,
+ name,
+ type,
+ alternateExchange,
+ durable,
+ false,
+ ManagementAgent::toMap(arguments),
+ "created"));
+ }
+ }
+ return result;
+}
+
+void Broker::deleteExchange(const std::string& name, const std::string& userId,
+ const std::string& connectionId)
+{
+ if (acl) {
+ if (!acl->authorise(userId,acl::ACT_DELETE,acl::OBJ_EXCHANGE,name,NULL) )
+ throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange delete request from " << userId));
+ }
+
+ if (name.empty()) {
+ throw framing::InvalidArgumentException(QPID_MSG("Delete not allowed for default exchange"));
+ }
+ Exchange::shared_ptr exchange(exchanges.get(name));
+ if (!exchange) throw framing::NotFoundException(QPID_MSG("Delete failed. No such exchange: " << name));
+ if (exchange->inUseAsAlternate()) throw framing::NotAllowedException(QPID_MSG("Exchange in use as alternate-exchange."));
+ if (exchange->isDurable()) store->destroy(*exchange);
+ if (exchange->getAlternate()) exchange->getAlternate()->decAlternateUsers();
+ exchanges.destroy(name);
+
+ if (managementAgent.get())
+ managementAgent->raiseEvent(_qmf::EventExchangeDelete(connectionId, userId, name));
+
+}
+
+void Broker::bind(const std::string& queueName,
+ const std::string& exchangeName,
+ const std::string& key,
+ const qpid::framing::FieldTable& arguments,
+ const std::string& userId,
+ const std::string& connectionId)
+{
+ if (acl) {
+ std::map<acl::Property, std::string> params;
+ params.insert(make_pair(acl::PROP_QUEUENAME, queueName));
+ params.insert(make_pair(acl::PROP_ROUTINGKEY, key));
+
+ if (!acl->authorise(userId,acl::ACT_BIND,acl::OBJ_EXCHANGE,exchangeName,&params))
+ throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange bind request from " << userId));
+ }
+ if (exchangeName.empty()) {
+ throw framing::InvalidArgumentException(QPID_MSG("Bind not allowed for default exchange"));
+ }
+
+ Queue::shared_ptr queue = queues.find(queueName);
+ Exchange::shared_ptr exchange = exchanges.get(exchangeName);
+ if (!queue) {
+ throw framing::NotFoundException(QPID_MSG("Bind failed. No such queue: " << queueName));
+ } else if (!exchange) {
+ throw framing::NotFoundException(QPID_MSG("Bind failed. No such exchange: " << exchangeName));
+ } else {
+ if (queue->bind(exchange, key, arguments)) {
+ if (managementAgent.get()) {
+ managementAgent->raiseEvent(_qmf::EventBind(connectionId, userId, exchangeName,
+ queueName, key, ManagementAgent::toMap(arguments)));
+ }
+ }
+ }
+}
+
+void Broker::unbind(const std::string& queueName,
+ const std::string& exchangeName,
+ const std::string& key,
+ const std::string& userId,
+ const std::string& connectionId)
+{
+ if (acl) {
+ std::map<acl::Property, std::string> params;
+ params.insert(make_pair(acl::PROP_QUEUENAME, queueName));
+ params.insert(make_pair(acl::PROP_ROUTINGKEY, key));
+ if (!acl->authorise(userId,acl::ACT_UNBIND,acl::OBJ_EXCHANGE,exchangeName,&params) )
+ throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange unbind request from " << userId));
+ }
+ if (exchangeName.empty()) {
+ throw framing::InvalidArgumentException(QPID_MSG("Unbind not allowed for default exchange"));
+ }
+ Queue::shared_ptr queue = queues.find(queueName);
+ Exchange::shared_ptr exchange = exchanges.get(exchangeName);
+ if (!queue) {
+ throw framing::NotFoundException(QPID_MSG("Unbind failed. No such queue: " << queueName));
+ } else if (!exchange) {
+ throw framing::NotFoundException(QPID_MSG("Unbind failed. No such exchange: " << exchangeName));
+ } else {
+ if (exchange->unbind(queue, key, 0)) {
+ if (exchange->isDurable() && queue->isDurable()) {
+ store->unbind(*exchange, *queue, key, qpid::framing::FieldTable());
+ }
+ if (managementAgent.get()) {
+ managementAgent->raiseEvent(_qmf::EventUnbind(connectionId, userId, exchangeName, queueName, key));
+ }
+ }
+ }
+}
+
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/Broker.h b/cpp/src/qpid/broker/Broker.h
index cd6f81dc70..b3b751be98 100644
--- a/cpp/src/qpid/broker/Broker.h
+++ b/cpp/src/qpid/broker/Broker.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -49,6 +49,7 @@
#include "qpid/framing/ProtocolInitiation.h"
#include "qpid/sys/Runnable.h"
#include "qpid/sys/Timer.h"
+#include "qpid/types/Variant.h"
#include "qpid/RefCounted.h"
#include "qpid/broker/AclModule.h"
#include "qpid/sys/Mutex.h"
@@ -57,7 +58,7 @@
#include <string>
#include <vector>
-namespace qpid {
+namespace qpid {
namespace sys {
class ProtocolFactory;
@@ -68,6 +69,7 @@ struct Url;
namespace broker {
+class ConnectionState;
class ExpiryPolicy;
class Message;
@@ -80,7 +82,7 @@ struct NoSuchTransportException : qpid::Exception
};
/**
- * A broker instance.
+ * A broker instance.
*/
class Broker : public sys::Runnable, public Plugin::Target,
public management::Manageable,
@@ -116,29 +118,34 @@ public:
bool asyncQueueEvents;
bool qmf2Support;
bool qmf1Support;
+ uint queueFlowStopRatio; // producer flow control: on
+ uint queueFlowResumeRatio; // producer flow control: off
+ uint16_t queueThresholdEventRatio;
+ std::string defaultMsgGroup;
+ bool timestampRcvMsgs;
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);
+ void inc_connectionCount() {
+ sys::ScopedLock<sys::Mutex> l(connectionCountLock);
connectionCount++;
- }
- void dec_connectionCount() {
- sys::ScopedLock<sys::Mutex> l(connectionCountLock);
+ }
+ void dec_connectionCount() {
+ sys::ScopedLock<sys::Mutex> l(connectionCountLock);
connectionCount--;
}
bool allowConnection() {
- sys::ScopedLock<sys::Mutex> l(connectionCountLock);
+ sys::ScopedLock<sys::Mutex> l(connectionCountLock);
return (maxConnections <= connectionCount);
- }
+ }
};
private:
@@ -148,7 +155,20 @@ public:
void setStore ();
void setLogLevel(const std::string& level);
std::string getLogLevel();
-
+ void createObject(const std::string& type, const std::string& name,
+ const qpid::types::Variant::Map& properties, bool strict, const ConnectionState* context);
+ void deleteObject(const std::string& type, const std::string& name,
+ const qpid::types::Variant::Map& options, const ConnectionState* context);
+ Manageable::status_t queryObject(const std::string& type, const std::string& name,
+ qpid::types::Variant::Map& results, const ConnectionState* context);
+ Manageable::status_t queryQueue( const std::string& name,
+ const std::string& userId,
+ const std::string& connectionId,
+ qpid::types::Variant::Map& results);
+ Manageable::status_t getTimestampConfig(bool& receive,
+ const ConnectionState* context);
+ Manageable::status_t setTimestampConfig(const bool receive,
+ const ConnectionState* context);
boost::shared_ptr<sys::Poller> poller;
sys::Timer timer;
std::auto_ptr<sys::Timer> clusterTimer;
@@ -176,10 +196,10 @@ public:
const boost::intrusive_ptr<Message>& msg);
std::string federationTag;
bool recovery;
- bool clusterUpdatee;
+ bool inCluster, clusterUpdatee;
boost::intrusive_ptr<ExpiryPolicy> expiryPolicy;
ConnectionCounter connectionCounter;
-
+
public:
virtual ~Broker();
@@ -235,7 +255,7 @@ public:
QPID_BROKER_EXTERN void accept();
/** Create a connection to another broker. */
- void connect(const std::string& host, uint16_t port,
+ void connect(const std::string& host, const std::string& port,
const std::string& transport,
boost::function2<void, int, std::string> failed,
sys::ConnectionCodec::Factory* =0);
@@ -247,9 +267,10 @@ public:
/** Move messages from one queue to another.
A zero quantity means to move all messages
*/
- uint32_t queueMoveMessages( const std::string& srcQueue,
+ uint32_t queueMoveMessages( const std::string& srcQueue,
const std::string& destQueue,
- uint32_t qty);
+ uint32_t qty,
+ const qpid::types::Variant::Map& filter);
boost::shared_ptr<sys::ProtocolFactory> getProtocolFactory(const std::string& name = TCP_TRANSPORT) const;
@@ -273,11 +294,20 @@ public:
void setRecovery(bool set) { recovery = set; }
bool getRecovery() const { return recovery; }
- void setClusterUpdatee(bool set) { clusterUpdatee = set; }
+ /** True of this broker is part of a cluster.
+ * Only valid after early initialization of plugins is complete.
+ */
+ bool isInCluster() const { return inCluster; }
+ void setInCluster(bool set) { inCluster = set; }
+
+ /** True if this broker is joining a cluster and in the process of
+ * receiving a state update.
+ */
bool isClusterUpdatee() const { return clusterUpdatee; }
+ void setClusterUpdatee(bool set) { clusterUpdatee = set; }
management::ManagementAgent* getManagementAgent() { return managementAgent.get(); }
-
+
ConnectionCounter& getConnectionCounter() {return connectionCounter;}
/**
@@ -290,6 +320,43 @@ public:
const boost::intrusive_ptr<Message>& msg)> deferDelivery;
bool isAuthenticating ( ) { return config.auth; }
+ bool isTimestamping() { return config.timestampRcvMsgs; }
+
+ typedef boost::function1<void, boost::shared_ptr<Queue> > QueueFunctor;
+
+ std::pair<boost::shared_ptr<Queue>, bool> createQueue(
+ const std::string& name,
+ bool durable,
+ bool autodelete,
+ const OwnershipToken* owner,
+ const std::string& alternateExchange,
+ const qpid::framing::FieldTable& arguments,
+ const std::string& userId,
+ const std::string& connectionId);
+ void deleteQueue(const std::string& name,
+ const std::string& userId,
+ const std::string& connectionId,
+ QueueFunctor check = QueueFunctor());
+ std::pair<Exchange::shared_ptr, bool> createExchange(
+ const std::string& name,
+ const std::string& type,
+ bool durable,
+ const std::string& alternateExchange,
+ const qpid::framing::FieldTable& args,
+ const std::string& userId, const std::string& connectionId);
+ void deleteExchange(const std::string& name, const std::string& userId,
+ const std::string& connectionId);
+ void bind(const std::string& queue,
+ const std::string& exchange,
+ const std::string& key,
+ const qpid::framing::FieldTable& arguments,
+ const std::string& userId,
+ const std::string& connectionId);
+ void unbind(const std::string& queue,
+ const std::string& exchange,
+ const std::string& key,
+ const std::string& userId,
+ const std::string& connectionId);
};
}}
diff --git a/cpp/src/qpid/broker/BrokerImportExport.h b/cpp/src/qpid/broker/BrokerImportExport.h
index 4edf8c9844..ee05788063 100644
--- a/cpp/src/qpid/broker/BrokerImportExport.h
+++ b/cpp/src/qpid/broker/BrokerImportExport.h
@@ -20,14 +20,23 @@
* under the License.
*/
-#if defined(WIN32) && !defined(QPID_BROKER_STATIC)
-#if defined(BROKER_EXPORT) || defined (qpidbroker_EXPORTS)
-#define QPID_BROKER_EXTERN __declspec(dllexport)
+#if defined(WIN32) && !defined(QPID_DECLARE_STATIC)
+# if defined(BROKER_EXPORT) || defined (qpidbroker_EXPORTS)
+# define QPID_BROKER_EXTERN __declspec(dllexport)
+# else
+# define QPID_BROKER_EXTERN __declspec(dllimport)
+# endif
+# ifdef _MSC_VER
+# define QPID_BROKER_CLASS_EXTERN
+# define QPID_BROKER_INLINE_EXTERN QPID_BROKER_EXTERN
+# else
+# define QPID_BROKER_CLASS_EXTERN QPID_BROKER_EXTERN
+# define QPID_BROKER_INLINE_EXTERN
+# endif
#else
-#define QPID_BROKER_EXTERN __declspec(dllimport)
-#endif
-#else
-#define QPID_BROKER_EXTERN
+# define QPID_BROKER_EXTERN
+# define QPID_BROKER_CLASS_EXTERN
+# define QPID_BROKER_INLINE_EXTERN
#endif
#endif
diff --git a/cpp/src/qpid/broker/Connection.cpp b/cpp/src/qpid/broker/Connection.cpp
index 460799280e..0b3059d26c 100644
--- a/cpp/src/qpid/broker/Connection.cpp
+++ b/cpp/src/qpid/broker/Connection.cpp
@@ -156,16 +156,7 @@ Connection::~Connection()
void Connection::received(framing::AMQFrame& frame) {
// Received frame on connection so delay timeout
restartTimeout();
-
- if (frame.getChannel() == 0 && frame.getMethod()) {
- adapter.handle(frame);
- } else {
- if (adapter.isOpen())
- getChannel(frame.getChannel()).in(frame);
- else
- close(connection::CLOSE_CODE_FRAMING_ERROR, "Connection not yet open, invalid frame received.");
- }
-
+ adapter.handle(frame);
if (isLink) //i.e. we are acting as the client to another broker
recordFromServer(frame);
else
@@ -278,8 +269,7 @@ void Connection::setUserId(const string& userId)
ConnectionState::setUserId(userId);
// In a cluster, the cluster code will raise the connect event
// when the connection is replicated to the cluster.
- if (!sys::isCluster())
- raiseConnectEvent();
+ if (!broker.isInCluster()) raiseConnectEvent();
}
void Connection::raiseConnectEvent() {
@@ -289,11 +279,11 @@ void Connection::raiseConnectEvent() {
}
}
-void Connection::setFederationLink(bool b)
+void Connection::setUserProxyAuth(bool b)
{
- ConnectionState::setFederationLink(b);
+ ConnectionState::setUserProxyAuth(b);
if (mgmtObject != 0)
- mgmtObject->set_federationLink(b);
+ mgmtObject->set_userProxyAuth(b);
}
void Connection::close(connection::CloseCode code, const string& text)
@@ -332,31 +322,30 @@ void Connection::closed(){ // Physically closed, suspend open sessions.
try {
while (!channels.empty())
ptr_map_ptr(channels.begin())->handleDetach();
- while (!exclusiveQueues.empty()) {
- boost::shared_ptr<Queue> q(exclusiveQueues.front());
- q->releaseExclusiveOwnership();
- if (q->canAutoDelete()) {
- Queue::tryAutoDelete(broker, q);
- }
- exclusiveQueues.erase(exclusiveQueues.begin());
- }
} catch(std::exception& e) {
QPID_LOG(error, QPID_MSG("While closing connection: " << e.what()));
assert(0);
}
}
+void Connection::doIoCallbacks() {
+ {
+ ScopedLock<Mutex> l(ioCallbackLock);
+ // Although IO callbacks execute in the connection thread context, they are
+ // not cluster safe because they are queued for execution in non-IO threads.
+ ClusterUnsafeScope cus;
+ while (!ioCallbacks.empty()) {
+ boost::function0<void> cb = ioCallbacks.front();
+ ioCallbacks.pop();
+ ScopedUnlock<Mutex> ul(ioCallbackLock);
+ cb(); // Lend the IO thread for management processing
+ }
+ }
+}
+
bool Connection::doOutput() {
try {
- {
- ScopedLock<Mutex> l(ioCallbackLock);
- while (!ioCallbacks.empty()) {
- boost::function0<void> cb = ioCallbacks.front();
- ioCallbacks.pop();
- ScopedUnlock<Mutex> ul(ioCallbackLock);
- cb(); // Lend the IO thread for management processing
- }
- }
+ doIoCallbacks();
if (mgmtClosing) {
closed();
close(connection::CLOSE_CODE_CONNECTION_FORCED, "Closed by Management Request");
@@ -476,8 +465,8 @@ void Connection::OutboundFrameTracker::abort() { next->abort(); }
void Connection::OutboundFrameTracker::activateOutput() { next->activateOutput(); }
void Connection::OutboundFrameTracker::giveReadCredit(int32_t credit) { next->giveReadCredit(credit); }
void Connection::OutboundFrameTracker::send(framing::AMQFrame& f)
-{
- next->send(f);
+{
+ next->send(f);
con.sent(f);
}
void Connection::OutboundFrameTracker::wrap(sys::ConnectionOutputHandlerPtr& p)
diff --git a/cpp/src/qpid/broker/Connection.h b/cpp/src/qpid/broker/Connection.h
index b751848d73..3522d70b35 100644
--- a/cpp/src/qpid/broker/Connection.h
+++ b/cpp/src/qpid/broker/Connection.h
@@ -125,7 +125,7 @@ class Connection : public sys::ConnectionInputHandler,
const std::string& getUserId() const { return ConnectionState::getUserId(); }
const std::string& getMgmtId() const { return mgmtId; }
management::ManagementAgent* getAgent() const { return agent; }
- void setFederationLink(bool b);
+ void setUserProxyAuth(bool b);
/** Connection does not delete the listener. 0 resets. */
void setErrorListener(ErrorListener* l) { errorListener=l; }
ErrorListener* getErrorListener() { return errorListener; }
@@ -153,13 +153,16 @@ class Connection : public sys::ConnectionInputHandler,
void addManagementObject();
const qpid::sys::SecuritySettings& getExternalSecuritySettings() const
- {
+ {
return securitySettings;
}
/** @return true if the initial connection negotiation is complete. */
bool isOpen();
+ // Used by cluster during catch-up, see cluster::OutputInterceptor
+ void doIoCallbacks();
+
private:
typedef boost::ptr_map<framing::ChannelId, SessionHandler> ChannelMap;
typedef std::vector<boost::shared_ptr<Queue> >::iterator queue_iterator;
@@ -201,7 +204,7 @@ class Connection : public sys::ConnectionInputHandler,
sys::ConnectionOutputHandler* next;
};
OutboundFrameTracker outboundTracker;
-
+
void sent(const framing::AMQFrame& f);
public:
diff --git a/cpp/src/qpid/broker/ConnectionHandler.cpp b/cpp/src/qpid/broker/ConnectionHandler.cpp
index 3f97e5b9de..7cd91ae539 100644
--- a/cpp/src/qpid/broker/ConnectionHandler.cpp
+++ b/cpp/src/qpid/broker/ConnectionHandler.cpp
@@ -26,6 +26,7 @@
#include "qpid/broker/SecureConnection.h"
#include "qpid/Url.h"
#include "qpid/framing/AllInvoker.h"
+#include "qpid/framing/ConnectionStartOkBody.h"
#include "qpid/framing/enum.h"
#include "qpid/log/Statement.h"
#include "qpid/sys/SecurityLayer.h"
@@ -63,13 +64,31 @@ void ConnectionHandler::heartbeat()
handler->proxy.heartbeat();
}
+bool ConnectionHandler::handle(const framing::AMQMethodBody& method)
+{
+ //Need special handling for start-ok, in order to distinguish
+ //between null and empty response
+ if (method.isA<ConnectionStartOkBody>()) {
+ handler->startOk(dynamic_cast<const ConnectionStartOkBody&>(method));
+ return true;
+ } else {
+ return invoke(static_cast<AMQP_AllOperations::ConnectionHandler&>(*handler), method);
+ }
+}
+
void ConnectionHandler::handle(framing::AMQFrame& frame)
{
AMQMethodBody* method=frame.getBody()->getMethod();
Connection::ErrorListener* errorListener = handler->connection.getErrorListener();
try{
- if (!invoke(static_cast<AMQP_AllOperations::ConnectionHandler&>(*handler.get()), *method)) {
+ if (method && handle(*method)) {
+ // This is a connection control frame, nothing more to do.
+ } else if (isOpen()) {
handler->connection.getChannel(frame.getChannel()).in(frame);
+ } else {
+ handler->proxy.close(
+ connection::CLOSE_CODE_FRAMING_ERROR,
+ "Connection not yet open, invalid frame received.");
}
}catch(ConnectionException& e){
if (errorListener) errorListener->connectionError(e.what());
@@ -89,13 +108,10 @@ ConnectionHandler::ConnectionHandler(Connection& connection, bool isClient, bool
ConnectionHandler::Handler::Handler(Connection& c, bool isClient, bool isShadow) :
proxy(c.getOutput()),
- connection(c), serverMode(!isClient), acl(0), secured(0),
+ connection(c), serverMode(!isClient), secured(0),
isOpen(false)
{
if (serverMode) {
-
- acl = connection.getBroker().getAcl();
-
FieldTable properties;
Array mechanisms(0x95);
@@ -118,13 +134,20 @@ ConnectionHandler::Handler::Handler(Connection& c, bool isClient, bool isShadow)
ConnectionHandler::Handler::~Handler() {}
-void ConnectionHandler::Handler::startOk(const framing::FieldTable& clientProperties,
- const string& mechanism,
- const string& response,
+void ConnectionHandler::Handler::startOk(const framing::FieldTable& /*clientProperties*/,
+ const string& /*mechanism*/,
+ const string& /*response*/,
const string& /*locale*/)
{
+ //Need special handling for start-ok, in order to distinguish
+ //between null and empty response -> should never use this method
+ assert(false);
+}
+
+void ConnectionHandler::Handler::startOk(const ConnectionStartOkBody& body)
+{
try {
- authenticator->start(mechanism, response);
+ authenticator->start(body.getMechanism(), body.hasResponse() ? &body.getResponse() : 0);
} catch (std::exception& /*e*/) {
management::ManagementAgent* agent = connection.getAgent();
if (agent) {
@@ -136,9 +159,14 @@ void ConnectionHandler::Handler::startOk(const framing::FieldTable& clientProper
}
throw;
}
+ const framing::FieldTable& clientProperties = body.getClientProperties();
connection.setFederationLink(clientProperties.get(QPID_FED_LINK));
- connection.setFederationPeerTag(clientProperties.getAsString(QPID_FED_TAG));
+ if (clientProperties.isSet(QPID_FED_TAG)) {
+ connection.setFederationPeerTag(clientProperties.getAsString(QPID_FED_TAG));
+ }
if (connection.isFederationLink()) {
+ 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");
return;
@@ -183,7 +211,7 @@ void ConnectionHandler::Handler::secureOk(const string& response)
void ConnectionHandler::Handler::tuneOk(uint16_t /*channelmax*/,
uint16_t framemax, uint16_t heartbeat)
{
- connection.setFrameMax(framemax);
+ if (framemax) connection.setFrameMax(framemax);
connection.setHeartbeatInterval(heartbeat);
}
@@ -256,7 +284,6 @@ void ConnectionHandler::Handler::start(const FieldTable& serverProperties,
false ); // disallow interaction
}
std::string supportedMechanismsList;
- bool requestedMechanismIsSupported = false;
Array::const_iterator i;
/*
@@ -269,11 +296,9 @@ void ConnectionHandler::Handler::start(const FieldTable& serverProperties,
if (i != supportedMechanisms.begin())
supportedMechanismsList += SPACE;
supportedMechanismsList += (*i)->get<std::string>();
- requestedMechanismIsSupported = true;
}
}
else {
- requestedMechanismIsSupported = false;
/*
The caller has requested a mechanism. If it's available,
make sure it ends up at the head of the list.
@@ -282,7 +307,6 @@ void ConnectionHandler::Handler::start(const FieldTable& serverProperties,
string currentMechanism = (*i)->get<std::string>();
if ( requestedMechanism == currentMechanism ) {
- requestedMechanismIsSupported = true;
supportedMechanismsList = currentMechanism + SPACE + supportedMechanismsList;
} else {
if (i != supportedMechanisms.begin())
@@ -292,7 +316,9 @@ void ConnectionHandler::Handler::start(const FieldTable& serverProperties,
}
}
- connection.setFederationPeerTag(serverProperties.getAsString(QPID_FED_TAG));
+ if (serverProperties.isSet(QPID_FED_TAG)) {
+ connection.setFederationPeerTag(serverProperties.getAsString(QPID_FED_TAG));
+ }
FieldTable ft;
ft.setInt(QPID_FED_LINK,1);
@@ -301,11 +327,21 @@ void ConnectionHandler::Handler::start(const FieldTable& serverProperties,
string response;
if (sasl.get()) {
const qpid::sys::SecuritySettings& ss = connection.getExternalSecuritySettings();
- response = sasl->start ( requestedMechanism.empty()
- ? supportedMechanismsList
- : requestedMechanism,
- & ss );
- proxy.startOk ( ft, sasl->getMechanism(), response, en_US );
+ if (sasl->start ( requestedMechanism.empty()
+ ? supportedMechanismsList
+ : requestedMechanism,
+ response,
+ & ss )) {
+ proxy.startOk ( ft, sasl->getMechanism(), response, en_US );
+ } else {
+ //response was null
+ ConnectionStartOkBody body;
+ body.setClientProperties(ft);
+ body.setMechanism(sasl->getMechanism());
+ //Don't set response, as none was given
+ body.setLocale(en_US);
+ proxy.send(body);
+ }
}
else {
response = ((char)0) + username + ((char)0) + password;
diff --git a/cpp/src/qpid/broker/ConnectionHandler.h b/cpp/src/qpid/broker/ConnectionHandler.h
index b32167669e..05c5f00c57 100644
--- a/cpp/src/qpid/broker/ConnectionHandler.h
+++ b/cpp/src/qpid/broker/ConnectionHandler.h
@@ -26,8 +26,10 @@
#include "qpid/broker/SaslAuthenticator.h"
#include "qpid/framing/amqp_types.h"
#include "qpid/framing/AMQFrame.h"
+#include "qpid/framing/AMQMethodBody.h"
#include "qpid/framing/AMQP_AllOperations.h"
#include "qpid/framing/AMQP_AllProxy.h"
+#include "qpid/framing/ConnectionStartOkBody.h"
#include "qpid/framing/enum.h"
#include "qpid/framing/FrameHandler.h"
#include "qpid/framing/ProtocolInitiation.h"
@@ -57,12 +59,12 @@ class ConnectionHandler : public framing::FrameHandler
Connection& connection;
bool serverMode;
std::auto_ptr<SaslAuthenticator> authenticator;
- AclModule* acl;
SecureConnection* secured;
bool isOpen;
Handler(Connection& connection, bool isClient, bool isShadow=false);
~Handler();
+ void startOk(const qpid::framing::ConnectionStartOkBody& body);
void startOk(const qpid::framing::FieldTable& clientProperties,
const std::string& mechanism, const std::string& response,
const std::string& locale);
@@ -96,7 +98,7 @@ class ConnectionHandler : public framing::FrameHandler
};
std::auto_ptr<Handler> handler;
-
+ bool handle(const qpid::framing::AMQMethodBody& method);
public:
ConnectionHandler(Connection& connection, bool isClient, bool isShadow=false );
void close(framing::connection::CloseCode code, const std::string& text);
diff --git a/cpp/src/qpid/broker/ConnectionState.h b/cpp/src/qpid/broker/ConnectionState.h
index 774c37408d..fdd3c4ddc0 100644
--- a/cpp/src/qpid/broker/ConnectionState.h
+++ b/cpp/src/qpid/broker/ConnectionState.h
@@ -46,6 +46,7 @@ class ConnectionState : public ConnectionToken, public management::Manageable
framemax(65535),
heartbeat(0),
heartbeatmax(120),
+ userProxyAuth(false), // Can proxy msgs with non-matching auth ids when true (used by federation links & clustering)
federationLink(true),
clientSupportsThrottling(false),
clusterOrderOut(0)
@@ -67,8 +68,10 @@ class ConnectionState : public ConnectionToken, public management::Manageable
void setUrl(const std::string& _url) { url = _url; }
const std::string& getUrl() const { return url; }
- void setFederationLink(bool b) { federationLink = b; }
- bool isFederationLink() const { return federationLink; }
+ void setUserProxyAuth(const bool b) { userProxyAuth = b; }
+ bool isUserProxyAuth() const { return userProxyAuth || federationPeerTag.size() > 0; } // links can proxy msgs with non-matching auth ids
+ void setFederationLink(bool b) { federationLink = b; } // deprecated - use setFederationPeerTag() instead
+ bool isFederationLink() const { return federationPeerTag.size() > 0; }
void setFederationPeerTag(const std::string& tag) { federationPeerTag = std::string(tag); }
const std::string& getFederationPeerTag() const { return federationPeerTag; }
std::vector<Url>& getKnownHosts() { return knownHosts; }
@@ -79,7 +82,6 @@ class ConnectionState : public ConnectionToken, public management::Manageable
Broker& getBroker() { return broker; }
Broker& broker;
- std::vector<boost::shared_ptr<Queue> > exclusiveQueues;
//contained output tasks
sys::AggregateOutput outputTasks;
@@ -106,6 +108,7 @@ class ConnectionState : public ConnectionToken, public management::Manageable
uint16_t heartbeatmax;
std::string userId;
std::string url;
+ bool userProxyAuth;
bool federationLink;
std::string federationPeerTag;
std::vector<Url> knownHosts;
diff --git a/cpp/src/qpid/broker/Consumer.h b/cpp/src/qpid/broker/Consumer.h
index b96443fa7c..2af9b0c121 100644
--- a/cpp/src/qpid/broker/Consumer.h
+++ b/cpp/src/qpid/broker/Consumer.h
@@ -29,22 +29,33 @@ namespace qpid {
namespace broker {
class Queue;
+class QueueListeners;
class Consumer {
const bool acquires;
- public:
- typedef boost::shared_ptr<Consumer> shared_ptr;
-
+ // inListeners allows QueueListeners to efficiently track if this instance is registered
+ // for notifications without having to search its containers
+ bool inListeners;
+ // the name is generated by broker and is unique within broker scope. It is not
+ // provided or known by the remote Consumer.
+ const std::string name;
+ public:
+ typedef boost::shared_ptr<Consumer> shared_ptr;
+
framing::SequenceNumber position;
-
- Consumer(bool preAcquires = true) : acquires(preAcquires) {}
+
+ Consumer(const std::string& _name, bool preAcquires = true)
+ : acquires(preAcquires), inListeners(false), name(_name), position(0) {}
bool preAcquires() const { return acquires; }
+ const std::string& getName() const { return name; }
+
virtual bool deliver(QueuedMessage& msg) = 0;
virtual void notify() = 0;
virtual bool filter(boost::intrusive_ptr<Message>) { return true; }
virtual bool accept(boost::intrusive_ptr<Message>) { return true; }
virtual OwnershipToken* getSession() = 0;
virtual ~Consumer(){}
+ friend class QueueListeners;
};
}}
diff --git a/cpp/src/qpid/broker/Daemon.cpp b/cpp/src/qpid/broker/Daemon.cpp
index b30e5f18cb..c36538beb7 100644
--- a/cpp/src/qpid/broker/Daemon.cpp
+++ b/cpp/src/qpid/broker/Daemon.cpp
@@ -93,11 +93,10 @@ void Daemon::fork()
catch (const exception& e) {
QPID_LOG(critical, "Unexpected error: " << e.what());
uint16_t port = 0;
- int unused_ret; //Supress warning about ignoring return value.
- unused_ret = write(pipeFds[1], &port, sizeof(uint16_t));
+ (void) write(pipeFds[1], &port, sizeof(uint16_t));
std::string pipeFailureMessage = e.what();
- unused_ret = write ( pipeFds[1],
+ (void) write ( pipeFds[1],
pipeFailureMessage.c_str(),
strlen(pipeFailureMessage.c_str())
);
diff --git a/cpp/src/qpid/broker/DeliverableMessage.h b/cpp/src/qpid/broker/DeliverableMessage.h
index ce613e7b6e..c8d21001eb 100644
--- a/cpp/src/qpid/broker/DeliverableMessage.h
+++ b/cpp/src/qpid/broker/DeliverableMessage.h
@@ -29,7 +29,7 @@
namespace qpid {
namespace broker {
- class DeliverableMessage : public Deliverable{
+ class QPID_BROKER_CLASS_EXTERN DeliverableMessage : public Deliverable{
boost::intrusive_ptr<Message> msg;
public:
QPID_BROKER_EXTERN DeliverableMessage(const boost::intrusive_ptr<Message>& msg);
diff --git a/cpp/src/qpid/broker/DeliveryRecord.cpp b/cpp/src/qpid/broker/DeliveryRecord.cpp
index 9443eb6ea5..0b8fe95d5e 100644
--- a/cpp/src/qpid/broker/DeliveryRecord.cpp
+++ b/cpp/src/qpid/broker/DeliveryRecord.cpp
@@ -75,7 +75,7 @@ void DeliveryRecord::deliver(framing::FrameHandler& h, DeliveryId deliveryId, ui
{
id = deliveryId;
if (msg.payload->getRedelivered()){
- msg.payload->getProperties<framing::DeliveryProperties>()->setRedelivered(true);
+ msg.payload->setRedelivered();
}
msg.payload->adjustTtl();
@@ -131,18 +131,20 @@ void DeliveryRecord::committed() const{
void DeliveryRecord::reject()
{
- Exchange::shared_ptr alternate = queue->getAlternateExchange();
- if (alternate) {
- DeliverableMessage delivery(msg.payload);
- alternate->route(delivery, msg.payload->getRoutingKey(), msg.payload->getApplicationHeaders());
- QPID_LOG(info, "Routed rejected message from " << queue->getName() << " to "
- << alternate->getName());
- } else {
- //just drop it
- QPID_LOG(info, "Dropping rejected message from " << queue->getName());
+ if (acquired && !ended) {
+ Exchange::shared_ptr alternate = queue->getAlternateExchange();
+ if (alternate) {
+ DeliverableMessage delivery(msg.payload);
+ alternate->routeWithAlternate(delivery);
+ QPID_LOG(info, "Routed rejected message from " << queue->getName() << " to "
+ << alternate->getName());
+ } else {
+ //just drop it
+ QPID_LOG(info, "Dropping rejected message from " << queue->getName());
+ }
+ dequeue();
+ setEnded();
}
-
- dequeue();
}
uint32_t DeliveryRecord::getCredit() const
@@ -151,7 +153,7 @@ uint32_t DeliveryRecord::getCredit() const
}
void DeliveryRecord::acquire(DeliveryIds& results) {
- if (queue->acquire(msg)) {
+ if (queue->acquire(msg, tag)) {
acquired = true;
results.push_back(id);
if (!acceptExpected) {
diff --git a/cpp/src/qpid/broker/DeliveryRecord.h b/cpp/src/qpid/broker/DeliveryRecord.h
index d388ba94be..5a331357be 100644
--- a/cpp/src/qpid/broker/DeliveryRecord.h
+++ b/cpp/src/qpid/broker/DeliveryRecord.h
@@ -46,7 +46,7 @@ class DeliveryRecord
{
QueuedMessage msg;
mutable boost::shared_ptr<Queue> queue;
- std::string tag;
+ std::string tag; // name of consumer
DeliveryId id;
bool acquired : 1;
bool acceptExpected : 1;
@@ -90,7 +90,7 @@ class DeliveryRecord
bool isAcquired() const { return acquired; }
bool isComplete() const { return completed; }
- bool isRedundant() const { return ended && (!windowing || completed); }
+ bool isRedundant() const { return ended && (!windowing || completed || cancelled); }
bool isCancelled() const { return cancelled; }
bool isAccepted() const { return !acceptExpected; }
bool isEnded() const { return ended; }
diff --git a/cpp/src/qpid/broker/DirectExchange.cpp b/cpp/src/qpid/broker/DirectExchange.cpp
index 5b8104c77c..5591539853 100644
--- a/cpp/src/qpid/broker/DirectExchange.cpp
+++ b/cpp/src/qpid/broker/DirectExchange.cpp
@@ -94,7 +94,7 @@ bool DirectExchange::bind(Queue::shared_ptr queue, const string& routingKey, con
propagate = bk.fedBinding.delOrigin(queue->getName(), fedOrigin);
if (bk.fedBinding.countFedBindings(queue->getName()) == 0)
- unbind(queue, routingKey, 0);
+ unbind(queue, routingKey, args);
} else if (fedOp == fedOpReorigin) {
/** gather up all the keys that need rebinding in a local vector
@@ -124,20 +124,24 @@ bool DirectExchange::bind(Queue::shared_ptr queue, const string& routingKey, con
return true;
}
-bool DirectExchange::unbind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/)
+bool DirectExchange::unbind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* args)
{
+ string fedOrigin(args ? args->getAsString(qpidFedOrigin) : "");
bool propagate = false;
- QPID_LOG(debug, "Unbind key [" << routingKey << "] from queue " << queue->getName());
-
+ QPID_LOG(debug, "Unbinding key [" << routingKey << "] from queue " << queue->getName()
+ << " on exchange " << getName() << " origin=" << fedOrigin << ")" );
{
Mutex::ScopedLock l(lock);
BoundKey& bk = bindings[routingKey];
if (bk.queues.remove_if(MatchQueue(queue))) {
- propagate = bk.fedBinding.delOrigin();
+ propagate = bk.fedBinding.delOrigin(queue->getName(), fedOrigin);
if (mgmtExchange != 0) {
mgmtExchange->dec_bindingCount();
}
+ if (bk.queues.empty()) {
+ bindings.erase(routingKey);
+ }
} else {
return false;
}
diff --git a/cpp/src/qpid/broker/DtxAck.cpp b/cpp/src/qpid/broker/DtxAck.cpp
index bca3f90bbe..c558681d62 100644
--- a/cpp/src/qpid/broker/DtxAck.cpp
+++ b/cpp/src/qpid/broker/DtxAck.cpp
@@ -32,6 +32,10 @@ DtxAck::DtxAck(const qpid::framing::SequenceSet& acked, DeliveryRecords& unacked
not1(bind2nd(mem_fun_ref(&DeliveryRecord::coveredBy), &acked)));
}
+DtxAck::DtxAck(DeliveryRecords& unacked) {
+ pending = unacked;
+}
+
bool DtxAck::prepare(TransactionContext* ctxt) throw()
{
try{
diff --git a/cpp/src/qpid/broker/DtxAck.h b/cpp/src/qpid/broker/DtxAck.h
index 166147e58d..16c3ff8ba0 100644
--- a/cpp/src/qpid/broker/DtxAck.h
+++ b/cpp/src/qpid/broker/DtxAck.h
@@ -1,3 +1,6 @@
+#ifndef QPID_BROKER_DTXACK_H
+#define QPID_BROKER_DTXACK_H
+
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -18,9 +21,6 @@
* under the License.
*
*/
-#ifndef _DtxAck_
-#define _DtxAck_
-
#include <algorithm>
#include <functional>
#include <list>
@@ -29,20 +29,21 @@
#include "qpid/broker/TxOp.h"
namespace qpid {
- namespace broker {
- class DtxAck : public TxOp{
- DeliveryRecords pending;
+namespace broker {
+class DtxAck : public TxOp{
+ DeliveryRecords pending;
- public:
- DtxAck(const framing::SequenceSet& acked, DeliveryRecords& unacked);
- virtual bool prepare(TransactionContext* ctxt) throw();
- virtual void commit() throw();
- virtual void rollback() throw();
- virtual ~DtxAck(){}
- virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); }
- };
- }
-}
+ public:
+ DtxAck(const framing::SequenceSet& acked, DeliveryRecords& unacked);
+ DtxAck(DeliveryRecords& unacked);
+ virtual bool prepare(TransactionContext* ctxt) throw();
+ virtual void commit() throw();
+ virtual void rollback() throw();
+ virtual ~DtxAck(){}
+ virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); }
+ const DeliveryRecords& getPending() const { return pending; }
+};
+}} // qpid::broker
-#endif
+#endif /*!QPID_BROKER_DTXACK_H*/
diff --git a/cpp/src/qpid/broker/DtxBuffer.cpp b/cpp/src/qpid/broker/DtxBuffer.cpp
index f1b8169cf7..13177d3b72 100644
--- a/cpp/src/qpid/broker/DtxBuffer.cpp
+++ b/cpp/src/qpid/broker/DtxBuffer.cpp
@@ -23,8 +23,11 @@
using namespace qpid::broker;
using qpid::sys::Mutex;
-DtxBuffer::DtxBuffer(const std::string& _xid)
- : xid(_xid), ended(false), suspended(false), failed(false), expired(false) {}
+DtxBuffer::DtxBuffer(
+ const std::string& _xid,
+ bool ended_, bool suspended_, bool failed_, bool expired_)
+ : xid(_xid), ended(ended_), suspended(suspended_), failed(failed_), expired(expired_)
+{}
DtxBuffer::~DtxBuffer() {}
@@ -34,7 +37,7 @@ void DtxBuffer::markEnded()
ended = true;
}
-bool DtxBuffer::isEnded()
+bool DtxBuffer::isEnded() const
{
Mutex::ScopedLock locker(lock);
return ended;
@@ -45,7 +48,7 @@ void DtxBuffer::setSuspended(bool isSuspended)
suspended = isSuspended;
}
-bool DtxBuffer::isSuspended()
+bool DtxBuffer::isSuspended() const
{
return suspended;
}
@@ -58,13 +61,13 @@ void DtxBuffer::fail()
ended = true;
}
-bool DtxBuffer::isRollbackOnly()
+bool DtxBuffer::isRollbackOnly() const
{
Mutex::ScopedLock locker(lock);
return failed;
}
-const std::string& DtxBuffer::getXid()
+std::string DtxBuffer::getXid() const
{
return xid;
}
@@ -76,8 +79,13 @@ void DtxBuffer::timedout()
fail();
}
-bool DtxBuffer::isExpired()
+bool DtxBuffer::isExpired() const
{
Mutex::ScopedLock locker(lock);
return expired;
}
+
+bool DtxBuffer::isFailed() const
+{
+ return failed;
+}
diff --git a/cpp/src/qpid/broker/DtxBuffer.h b/cpp/src/qpid/broker/DtxBuffer.h
index 1511cb032f..cabd37647a 100644
--- a/cpp/src/qpid/broker/DtxBuffer.h
+++ b/cpp/src/qpid/broker/DtxBuffer.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
@@ -26,31 +26,34 @@
#include "qpid/sys/Mutex.h"
namespace qpid {
- namespace broker {
- class DtxBuffer : public TxBuffer{
- sys::Mutex lock;
- const std::string xid;
- bool ended;
- bool suspended;
- bool failed;
- bool expired;
+namespace broker {
+class DtxBuffer : public TxBuffer{
+ mutable sys::Mutex lock;
+ const std::string xid;
+ bool ended;
+ bool suspended;
+ bool failed;
+ bool expired;
- public:
- typedef boost::shared_ptr<DtxBuffer> shared_ptr;
+ public:
+ typedef boost::shared_ptr<DtxBuffer> shared_ptr;
- QPID_BROKER_EXTERN DtxBuffer(const std::string& xid = "");
- QPID_BROKER_EXTERN ~DtxBuffer();
- QPID_BROKER_EXTERN void markEnded();
- bool isEnded();
- void setSuspended(bool suspended);
- bool isSuspended();
- void fail();
- bool isRollbackOnly();
- void timedout();
- bool isExpired();
- const std::string& getXid();
- };
- }
+ QPID_BROKER_EXTERN DtxBuffer(
+ const std::string& xid = "",
+ bool ended=false, bool suspended=false, bool failed=false, bool expired=false);
+ QPID_BROKER_EXTERN ~DtxBuffer();
+ QPID_BROKER_EXTERN void markEnded();
+ bool isEnded() const;
+ void setSuspended(bool suspended);
+ bool isSuspended() const;
+ void fail();
+ bool isRollbackOnly() const;
+ void timedout();
+ bool isExpired() const;
+ bool isFailed() const;
+ std::string getXid() const;
+};
+}
}
diff --git a/cpp/src/qpid/broker/DtxManager.cpp b/cpp/src/qpid/broker/DtxManager.cpp
index 3caa41c3f4..febd547478 100644
--- a/cpp/src/qpid/broker/DtxManager.cpp
+++ b/cpp/src/qpid/broker/DtxManager.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
@@ -34,7 +34,7 @@ using qpid::ptr_map_ptr;
using namespace qpid::broker;
using namespace qpid::framing;
-DtxManager::DtxManager(qpid::sys::Timer& t) : store(0), timer(t) {}
+DtxManager::DtxManager(qpid::sys::Timer& t) : store(0), timer(&t) {}
DtxManager::~DtxManager() {}
@@ -53,8 +53,8 @@ void DtxManager::recover(const std::string& xid, std::auto_ptr<TPCTransactionCon
createWork(xid)->recover(txn, ops);
}
-bool DtxManager::prepare(const std::string& xid)
-{
+bool DtxManager::prepare(const std::string& xid)
+{
QPID_LOG(debug, "preparing: " << xid);
try {
return getWork(xid)->prepare();
@@ -64,8 +64,8 @@ bool DtxManager::prepare(const std::string& xid)
}
}
-bool DtxManager::commit(const std::string& xid, bool onePhase)
-{
+bool DtxManager::commit(const std::string& xid, bool onePhase)
+{
QPID_LOG(debug, "committing: " << xid);
try {
bool result = getWork(xid)->commit(onePhase);
@@ -77,8 +77,8 @@ bool DtxManager::commit(const std::string& xid, bool onePhase)
}
}
-void DtxManager::rollback(const std::string& xid)
-{
+void DtxManager::rollback(const std::string& xid)
+{
QPID_LOG(debug, "rolling back: " << xid);
try {
getWork(xid)->rollback();
@@ -91,7 +91,7 @@ void DtxManager::rollback(const std::string& xid)
DtxWorkRecord* DtxManager::getWork(const std::string& xid)
{
- Mutex::ScopedLock locker(lock);
+ Mutex::ScopedLock locker(lock);
WorkMap::iterator i = work.find(xid);
if (i == work.end()) {
throw NotFoundException(QPID_MSG("Unrecognised xid " << xid));
@@ -99,9 +99,14 @@ DtxWorkRecord* DtxManager::getWork(const std::string& xid)
return ptr_map_ptr(i);
}
+bool DtxManager::exists(const std::string& xid) {
+ Mutex::ScopedLock locker(lock);
+ return work.find(xid) != work.end();
+}
+
void DtxManager::remove(const std::string& xid)
{
- Mutex::ScopedLock locker(lock);
+ Mutex::ScopedLock locker(lock);
WorkMap::iterator i = work.find(xid);
if (i == work.end()) {
throw NotFoundException(QPID_MSG("Unrecognised xid " << xid));
@@ -110,14 +115,15 @@ void DtxManager::remove(const std::string& xid)
}
}
-DtxWorkRecord* DtxManager::createWork(std::string xid)
+DtxWorkRecord* DtxManager::createWork(const std::string& xid)
{
- Mutex::ScopedLock locker(lock);
+ Mutex::ScopedLock locker(lock);
WorkMap::iterator i = work.find(xid);
if (i != work.end()) {
throw NotAllowedException(QPID_MSG("Xid " << xid << " is already known (use 'join' to add work to an existing xid)"));
} else {
- return ptr_map_ptr(work.insert(xid, new DtxWorkRecord(xid, store)).first);
+ std::string ncxid = xid; // Work around const correctness problems in ptr_map.
+ return ptr_map_ptr(work.insert(ncxid, new DtxWorkRecord(ncxid, store)).first);
}
}
@@ -131,7 +137,7 @@ void DtxManager::setTimeout(const std::string& xid, uint32_t secs)
}
timeout = intrusive_ptr<DtxTimeout>(new DtxTimeout(secs, *this, xid));
record->setTimeout(timeout);
- timer.add(timeout);
+ timer->add(timeout);
}
uint32_t DtxManager::getTimeout(const std::string& xid)
@@ -142,7 +148,7 @@ uint32_t DtxManager::getTimeout(const std::string& xid)
void DtxManager::timedout(const std::string& xid)
{
- Mutex::ScopedLock locker(lock);
+ Mutex::ScopedLock locker(lock);
WorkMap::iterator i = work.find(xid);
if (i == work.end()) {
QPID_LOG(warning, "Transaction timeout failed: no record for xid");
@@ -153,7 +159,7 @@ void DtxManager::timedout(const std::string& xid)
}
}
-DtxManager::DtxCleanup::DtxCleanup(uint32_t _timeout, DtxManager& _mgr, const std::string& _xid)
+DtxManager::DtxCleanup::DtxCleanup(uint32_t _timeout, DtxManager& _mgr, const std::string& _xid)
: TimerTask(qpid::sys::Duration(_timeout * qpid::sys::TIME_SEC),"DtxCleanup"), mgr(_mgr), xid(_xid) {}
void DtxManager::DtxCleanup::fire()
diff --git a/cpp/src/qpid/broker/DtxManager.h b/cpp/src/qpid/broker/DtxManager.h
index 680b62eeb2..11895695a3 100644
--- a/cpp/src/qpid/broker/DtxManager.h
+++ b/cpp/src/qpid/broker/DtxManager.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
@@ -26,8 +26,8 @@
#include "qpid/broker/DtxWorkRecord.h"
#include "qpid/broker/TransactionalStore.h"
#include "qpid/framing/amqp_types.h"
-#include "qpid/sys/Timer.h"
#include "qpid/sys/Mutex.h"
+#include "qpid/ptr_map.h"
namespace qpid {
namespace broker {
@@ -39,22 +39,21 @@ class DtxManager{
{
DtxManager& mgr;
const std::string& xid;
-
- DtxCleanup(uint32_t timeout, DtxManager& mgr, const std::string& xid);
+
+ DtxCleanup(uint32_t timeout, DtxManager& mgr, const std::string& xid);
void fire();
};
WorkMap work;
TransactionalStore* store;
qpid::sys::Mutex lock;
- qpid::sys::Timer& timer;
+ qpid::sys::Timer* timer;
void remove(const std::string& xid);
- DtxWorkRecord* getWork(const std::string& xid);
- DtxWorkRecord* createWork(std::string xid);
+ DtxWorkRecord* createWork(const std::string& xid);
public:
- DtxManager(qpid::sys::Timer&);
+ DtxManager(sys::Timer&);
~DtxManager();
void start(const std::string& xid, DtxBuffer::shared_ptr work);
void join(const std::string& xid, DtxBuffer::shared_ptr work);
@@ -66,6 +65,15 @@ public:
uint32_t getTimeout(const std::string& xid);
void timedout(const std::string& xid);
void setStore(TransactionalStore* store);
+ void setTimer(sys::Timer& t) { timer = &t; }
+
+ // Used by cluster for replication.
+ template<class F> void each(F f) const {
+ for (WorkMap::const_iterator i = work.begin(); i != work.end(); ++i)
+ f(*ptr_map_ptr(i));
+ }
+ DtxWorkRecord* getWork(const std::string& xid);
+ bool exists(const std::string& xid);
};
}
diff --git a/cpp/src/qpid/broker/DtxTimeout.cpp b/cpp/src/qpid/broker/DtxTimeout.cpp
index c4c52ec40a..58700846ef 100644
--- a/cpp/src/qpid/broker/DtxTimeout.cpp
+++ b/cpp/src/qpid/broker/DtxTimeout.cpp
@@ -25,7 +25,7 @@
using namespace qpid::broker;
DtxTimeout::DtxTimeout(uint32_t _timeout, DtxManager& _mgr, const std::string& _xid)
- : TimerTask(qpid::sys::Duration(_timeout * qpid::sys::TIME_SEC),"DtxTimeout"), timeout(_timeout), mgr(_mgr), xid(_xid)
+ : TimerTask(qpid::sys::Duration(_timeout * qpid::sys::TIME_SEC),"DtxTimeout-"+_xid), timeout(_timeout), mgr(_mgr), xid(_xid)
{
}
diff --git a/cpp/src/qpid/broker/DtxTimeout.h b/cpp/src/qpid/broker/DtxTimeout.h
index 680a210e4f..1fcb4cee2a 100644
--- a/cpp/src/qpid/broker/DtxTimeout.h
+++ b/cpp/src/qpid/broker/DtxTimeout.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
@@ -29,7 +29,9 @@ namespace broker {
class DtxManager;
-struct DtxTimeoutException : public Exception {};
+struct DtxTimeoutException : public Exception {
+ DtxTimeoutException(const std::string& msg=std::string()) : Exception(msg) {}
+};
struct DtxTimeout : public sys::TimerTask
{
@@ -37,7 +39,7 @@ struct DtxTimeout : public sys::TimerTask
DtxManager& mgr;
const std::string xid;
- DtxTimeout(uint32_t timeout, DtxManager& mgr, const std::string& xid);
+ DtxTimeout(uint32_t timeout, DtxManager& mgr, const std::string& xid);
void fire();
};
diff --git a/cpp/src/qpid/broker/DtxWorkRecord.cpp b/cpp/src/qpid/broker/DtxWorkRecord.cpp
index 9f33e698db..a413fe418d 100644
--- a/cpp/src/qpid/broker/DtxWorkRecord.cpp
+++ b/cpp/src/qpid/broker/DtxWorkRecord.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
@@ -28,19 +28,19 @@ using qpid::sys::Mutex;
using namespace qpid::broker;
using namespace qpid::framing;
-DtxWorkRecord::DtxWorkRecord(const std::string& _xid, TransactionalStore* const _store) :
+DtxWorkRecord::DtxWorkRecord(const std::string& _xid, TransactionalStore* const _store) :
xid(_xid), store(_store), completed(false), rolledback(false), prepared(false), expired(false) {}
-DtxWorkRecord::~DtxWorkRecord()
+DtxWorkRecord::~DtxWorkRecord()
{
- if (timeout.get()) {
+ if (timeout.get()) {
timeout->cancel();
}
}
bool DtxWorkRecord::prepare()
{
- Mutex::ScopedLock locker(lock);
+ Mutex::ScopedLock locker(lock);
if (check()) {
txn = store->begin(xid);
if (prepare(txn.get())) {
@@ -68,7 +68,7 @@ bool DtxWorkRecord::prepare(TransactionContext* _txn)
bool DtxWorkRecord::commit(bool onePhase)
{
- Mutex::ScopedLock locker(lock);
+ Mutex::ScopedLock locker(lock);
if (check()) {
if (prepared) {
//already prepared i.e. 2pc
@@ -78,13 +78,13 @@ bool DtxWorkRecord::commit(bool onePhase)
store->commit(*txn);
txn.reset();
-
+
std::for_each(work.begin(), work.end(), mem_fn(&TxBuffer::commit));
return true;
} else {
//1pc commit optimisation, don't need a 2pc transaction context:
if (!onePhase) {
- throw IllegalStateException(QPID_MSG("Branch with xid " << xid << " has not been prepared, one-phase option required!"));
+ throw IllegalStateException(QPID_MSG("Branch with xid " << xid << " has not been prepared, one-phase option required!"));
}
std::auto_ptr<TransactionContext> localtxn = store->begin();
if (prepare(localtxn.get())) {
@@ -107,16 +107,16 @@ bool DtxWorkRecord::commit(bool onePhase)
void DtxWorkRecord::rollback()
{
- Mutex::ScopedLock locker(lock);
+ Mutex::ScopedLock locker(lock);
check();
abort();
}
void DtxWorkRecord::add(DtxBuffer::shared_ptr ops)
{
- Mutex::ScopedLock locker(lock);
+ Mutex::ScopedLock locker(lock);
if (expired) {
- throw DtxTimeoutException();
+ throw DtxTimeoutException(QPID_MSG("Branch with xid " << xid << " has timed out."));
}
if (completed) {
throw CommandInvalidException(QPID_MSG("Branch with xid " << xid << " has been completed!"));
@@ -163,7 +163,7 @@ void DtxWorkRecord::recover(std::auto_ptr<TPCTransactionContext> _txn, DtxBuffer
void DtxWorkRecord::timedout()
{
- Mutex::ScopedLock locker(lock);
+ Mutex::ScopedLock locker(lock);
expired = true;
rolledback = true;
if (!completed) {
@@ -175,3 +175,17 @@ void DtxWorkRecord::timedout()
}
abort();
}
+
+size_t DtxWorkRecord::indexOf(const DtxBuffer::shared_ptr& buf) {
+ Work::iterator i = std::find(work.begin(), work.end(), buf);
+ if (i == work.end()) throw NotFoundException(
+ QPID_MSG("Can't find DTX buffer for xid: " << buf->getXid()));
+ return i - work.begin();
+}
+
+DtxBuffer::shared_ptr DtxWorkRecord::operator[](size_t i) const {
+ if (i > work.size())
+ throw NotFoundException(
+ QPID_MSG("Can't find DTX buffer " << i << " for xid: " << xid));
+ return work[i];
+}
diff --git a/cpp/src/qpid/broker/DtxWorkRecord.h b/cpp/src/qpid/broker/DtxWorkRecord.h
index aec2d2aed4..331e42fefd 100644
--- a/cpp/src/qpid/broker/DtxWorkRecord.h
+++ b/cpp/src/qpid/broker/DtxWorkRecord.h
@@ -73,9 +73,19 @@ public:
void timedout();
void setTimeout(boost::intrusive_ptr<DtxTimeout> t) { timeout = t; }
boost::intrusive_ptr<DtxTimeout> getTimeout() { return timeout; }
+ std::string getXid() const { return xid; }
+ bool isCompleted() const { return completed; }
+ bool isRolledback() const { return rolledback; }
+ bool isPrepared() const { return prepared; }
+ bool isExpired() const { return expired; }
+
+ // Used by cluster update;
+ size_t size() const { return work.size(); }
+ DtxBuffer::shared_ptr operator[](size_t i) const;
+ uint32_t getTimeout() const { return timeout? timeout->timeout : 0; }
+ size_t indexOf(const DtxBuffer::shared_ptr&);
};
-}
-}
+}} // qpid::broker
#endif
diff --git a/cpp/src/qpid/broker/Exchange.cpp b/cpp/src/qpid/broker/Exchange.cpp
index d143471559..d68845062d 100644
--- a/cpp/src/qpid/broker/Exchange.cpp
+++ b/cpp/src/qpid/broker/Exchange.cpp
@@ -19,16 +19,18 @@
*
*/
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/DeliverableMessage.h"
#include "qpid/broker/Exchange.h"
#include "qpid/broker/ExchangeRegistry.h"
#include "qpid/broker/FedOps.h"
-#include "qpid/broker/Broker.h"
-#include "qpid/management/ManagementAgent.h"
#include "qpid/broker/Queue.h"
-#include "qpid/log/Statement.h"
#include "qpid/framing/MessageProperties.h"
#include "qpid/framing/reply_exceptions.h"
-#include "qpid/broker/DeliverableMessage.h"
+#include "qpid/log/Statement.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qpid/sys/ExceptionHolder.h"
+#include <stdexcept>
using namespace qpid::broker;
using namespace qpid::framing;
@@ -56,7 +58,7 @@ Exchange::PreRoute::PreRoute(Deliverable& msg, Exchange* _p):parent(_p) {
if (parent->sequence){
parent->sequenceNo++;
- msg.getMessage().getProperties<MessageProperties>()->getApplicationHeaders().setInt64(qpidMsgSequence,parent->sequenceNo);
+ msg.getMessage().insertCustomProperty(qpidMsgSequence,parent->sequenceNo);
}
if (parent->ive) {
parent->lastMsg = &( msg.getMessage());
@@ -70,6 +72,36 @@ Exchange::PreRoute::~PreRoute(){
}
}
+namespace {
+/** Store information about an exception to be thrown later.
+ * If multiple exceptions are stored, save the first of the "most severe"
+ * exceptions, SESSION is les sever than CONNECTION etc.
+ */
+class ExInfo {
+ public:
+ enum Type { NONE, SESSION, CONNECTION, OTHER };
+
+ ExInfo(string exchange) : type(NONE), exchange(exchange) {}
+ void store(Type type_, const qpid::sys::ExceptionHolder& exception_, const boost::shared_ptr<Queue>& queue) {
+ QPID_LOG(warning, "Exchange " << exchange << " cannot deliver to queue "
+ << queue->getName() << ": " << exception_.what());
+ if (type < type_) { // Replace less severe exception
+ type = type_;
+ exception = exception_;
+ }
+ }
+
+ void raise() {
+ exception.raise();
+ }
+
+ private:
+ Type type;
+ string exchange;
+ qpid::sys::ExceptionHolder exception;
+};
+}
+
void Exchange::doRoute(Deliverable& msg, ConstBindingList b)
{
int count = 0;
@@ -80,11 +112,25 @@ void Exchange::doRoute(Deliverable& msg, ConstBindingList b)
msg.getMessage().blockContentRelease();
}
+
+ ExInfo error(getName()); // Save exception to throw at the end.
for(std::vector<Binding::shared_ptr>::const_iterator i = b->begin(); i != b->end(); i++, count++) {
- msg.deliverTo((*i)->queue);
- if ((*i)->mgmtBinding != 0)
- (*i)->mgmtBinding->inc_msgMatched();
+ try {
+ msg.deliverTo((*i)->queue);
+ if ((*i)->mgmtBinding != 0)
+ (*i)->mgmtBinding->inc_msgMatched();
+ }
+ catch (const SessionException& e) {
+ error.store(ExInfo::SESSION, framing::createSessionException(e.code, e.what()),(*i)->queue);
+ }
+ catch (const ConnectionException& e) {
+ error.store(ExInfo::CONNECTION, framing::createConnectionException(e.code, e.what()), (*i)->queue);
+ }
+ catch (const std::exception& e) {
+ error.store(ExInfo::OTHER, qpid::sys::ExceptionHolder(new Exception(e.what())), (*i)->queue);
+ }
}
+ error.raise();
}
if (mgmtExchange != 0)
@@ -115,7 +161,7 @@ void Exchange::routeIVE(){
Exchange::Exchange (const string& _name, Manageable* parent, Broker* b) :
name(_name), durable(false), persistenceId(0), sequence(false),
- sequenceNo(0), ive(false), mgmtExchange(0), broker(b)
+ sequenceNo(0), ive(false), mgmtExchange(0), broker(b), destroyed(false)
{
if (parent != 0 && broker != 0)
{
@@ -133,7 +179,7 @@ Exchange::Exchange (const string& _name, Manageable* parent, Broker* b) :
Exchange::Exchange(const string& _name, bool _durable, const qpid::framing::FieldTable& _args,
Manageable* parent, Broker* b)
: name(_name), durable(_durable), alternateUsers(0), persistenceId(0),
- args(_args), sequence(false), sequenceNo(0), ive(false), mgmtExchange(0), broker(b)
+ args(_args), sequence(false), sequenceNo(0), ive(false), mgmtExchange(0), broker(b), destroyed(false)
{
if (parent != 0 && broker != 0)
{
@@ -155,7 +201,11 @@ Exchange::Exchange(const string& _name, bool _durable, const qpid::framing::Fiel
}
ive = _args.get(qpidIVE);
- if (ive) QPID_LOG(debug, "Configured exchange " << _name << " with Initial Value");
+ if (ive) {
+ if (broker && broker->isInCluster())
+ throw framing::NotImplementedException("Cannot use Initial Value Exchanges in a cluster");
+ QPID_LOG(debug, "Configured exchange " << _name << " with Initial Value");
+ }
}
Exchange::~Exchange ()
@@ -340,5 +390,14 @@ bool Exchange::MatchQueue::operator()(Exchange::Binding::shared_ptr b)
}
void Exchange::setProperties(const boost::intrusive_ptr<Message>& msg) {
- msg->getProperties<DeliveryProperties>()->setExchange(getName());
+ msg->setExchange(getName());
+}
+
+bool Exchange::routeWithAlternate(Deliverable& msg)
+{
+ route(msg, msg.getMessage().getRoutingKey(), msg.getMessage().getApplicationHeaders());
+ if (!msg.delivered && alternate) {
+ alternate->route(msg, msg.getMessage().getRoutingKey(), msg.getMessage().getApplicationHeaders());
+ }
+ return msg.delivered;
}
diff --git a/cpp/src/qpid/broker/Exchange.h b/cpp/src/qpid/broker/Exchange.h
index 3c8b5ca2cd..b12af9a1dd 100644
--- a/cpp/src/qpid/broker/Exchange.h
+++ b/cpp/src/qpid/broker/Exchange.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -39,7 +39,7 @@ namespace broker {
class Broker;
class ExchangeRegistry;
-class Exchange : public PersistableExchange, public management::Manageable {
+class QPID_BROKER_CLASS_EXTERN Exchange : public PersistableExchange, public management::Manageable {
public:
struct Binding : public management::Manageable {
typedef boost::shared_ptr<Binding> shared_ptr;
@@ -82,15 +82,15 @@ protected:
private:
Exchange* parent;
};
-
+
typedef boost::shared_ptr<const std::vector<boost::shared_ptr<qpid::broker::Exchange::Binding> > > ConstBindingList;
typedef boost::shared_ptr< std::vector<boost::shared_ptr<qpid::broker::Exchange::Binding> > > BindingList;
void doRoute(Deliverable& msg, ConstBindingList b);
void routeIVE();
-
+
struct MatchQueue {
- const boost::shared_ptr<Queue> queue;
+ const boost::shared_ptr<Queue> queue;
MatchQueue(boost::shared_ptr<Queue> q);
bool operator()(Exchange::Binding::shared_ptr b);
};
@@ -133,15 +133,15 @@ protected:
/** Returns true if propagation is needed. */
bool delOrigin(const std::string& queueName, const std::string& origin){
- fedBindings[queueName].erase(origin);
- return true;
- }
-
- /** Returns true if propagation is needed. */
- bool delOrigin() {
- if (localBindings > 0)
- localBindings--;
- return localBindings == 0;
+ if (origin.empty()) { // no remote == local binding
+ if (localBindings > 0)
+ localBindings--;
+ return localBindings == 0;
+ }
+ size_t match = fedBindings[queueName].erase(origin);
+ if (fedBindings[queueName].empty())
+ fedBindings.erase(queueName);
+ return match != 0;
}
uint32_t count() {
@@ -149,7 +149,11 @@ protected:
}
uint32_t countFedBindings(const std::string& queueName) {
- return fedBindings[queueName].size();
+ // don't use '[]' - it may increase size of fedBindings!
+ std::map<std::string, originSet>::iterator i;
+ if ((i = fedBindings.find(queueName)) != fedBindings.end())
+ return i->second.size();
+ return 0;
}
};
@@ -162,7 +166,7 @@ public:
Broker* broker = 0);
QPID_BROKER_EXTERN Exchange(const std::string& _name, bool _durable, const qpid::framing::FieldTable& _args,
management::Manageable* parent = 0, Broker* broker = 0);
- QPID_BROKER_EXTERN virtual ~Exchange();
+ QPID_BROKER_INLINE_EXTERN virtual ~Exchange();
const std::string& getName() const { return name; }
bool isDurable() { return durable; }
@@ -191,7 +195,7 @@ public:
virtual bool isBound(boost::shared_ptr<Queue> queue, const std::string* const routingKey, const qpid::framing::FieldTable* const args) = 0;
QPID_BROKER_EXTERN virtual void setProperties(const boost::intrusive_ptr<Message>&);
virtual void route(Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args) = 0;
-
+
//PersistableExchange:
QPID_BROKER_EXTERN void setPersistenceId(uint64_t id) const;
uint64_t getPersistenceId() const { return persistenceId; }
@@ -222,14 +226,20 @@ public:
*/
void recoveryComplete(ExchangeRegistry& exchanges);
+ bool routeWithAlternate(Deliverable& message);
+
+ void destroy() { destroyed = true; }
+ bool isDestroyed() const { return destroyed; }
+
protected:
qpid::sys::Mutex bridgeLock;
std::vector<DynamicBridge*> bridgeVector;
Broker* broker;
+ bool destroyed;
QPID_BROKER_EXTERN virtual void handleHelloRequest();
void propagateFedOp(const std::string& routingKey, const std::string& tags,
- const std::string& op, const std::string& origin,
+ const std::string& op, const std::string& origin,
qpid::framing::FieldTable* extra_args=0);
};
diff --git a/cpp/src/qpid/broker/ExchangeRegistry.cpp b/cpp/src/qpid/broker/ExchangeRegistry.cpp
index 99b121cbce..1c8d26c4f7 100644
--- a/cpp/src/qpid/broker/ExchangeRegistry.cpp
+++ b/cpp/src/qpid/broker/ExchangeRegistry.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
@@ -39,7 +39,7 @@ pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, c
return declare(name, type, false, FieldTable());
}
-pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, const string& type,
+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);
@@ -61,7 +61,7 @@ pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, c
}else{
FunctionMap::iterator i = factory.find(type);
if (i == factory.end()) {
- throw UnknownExchangeTypeException();
+ throw UnknownExchangeTypeException();
} else {
exchange = i->second(name, durable, args, parent, broker);
}
@@ -82,6 +82,7 @@ void ExchangeRegistry::destroy(const string& name){
RWlock::ScopedWlock locker(lock);
ExchangeMap::iterator i = exchanges.find(name);
if (i != exchanges.end()) {
+ i->second->destroy();
exchanges.erase(i);
}
}
@@ -104,7 +105,7 @@ void ExchangeRegistry::registerType(const std::string& type, FactoryFunction f)
}
-namespace
+namespace
{
const std::string empty;
}
diff --git a/cpp/src/qpid/broker/ExpiryPolicy.cpp b/cpp/src/qpid/broker/ExpiryPolicy.cpp
index 64a12d918a..62cb3fc116 100644
--- a/cpp/src/qpid/broker/ExpiryPolicy.cpp
+++ b/cpp/src/qpid/broker/ExpiryPolicy.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,12 +27,12 @@ namespace broker {
ExpiryPolicy::~ExpiryPolicy() {}
-void ExpiryPolicy::willExpire(Message&) {}
-
bool ExpiryPolicy::hasExpired(Message& m) {
return m.getExpiration() < sys::AbsTime::now();
}
-void ExpiryPolicy::forget(Message&) {}
+sys::AbsTime ExpiryPolicy::getCurrentTime() {
+ return sys::AbsTime::now();
+}
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/ExpiryPolicy.h b/cpp/src/qpid/broker/ExpiryPolicy.h
index 40e793bf2c..2caf00ce00 100644
--- a/cpp/src/qpid/broker/ExpiryPolicy.h
+++ b/cpp/src/qpid/broker/ExpiryPolicy.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -26,6 +26,11 @@
#include "qpid/broker/BrokerImportExport.h"
namespace qpid {
+
+namespace sys {
+class AbsTime;
+}
+
namespace broker {
class Message;
@@ -33,13 +38,12 @@ class Message;
/**
* Default expiry policy.
*/
-class ExpiryPolicy : public RefCounted
+class QPID_BROKER_CLASS_EXTERN ExpiryPolicy : public RefCounted
{
public:
QPID_BROKER_EXTERN virtual ~ExpiryPolicy();
- QPID_BROKER_EXTERN virtual void willExpire(Message&);
QPID_BROKER_EXTERN virtual bool hasExpired(Message&);
- QPID_BROKER_EXTERN virtual void forget(Message&);
+ QPID_BROKER_EXTERN virtual qpid::sys::AbsTime getCurrentTime();
};
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/Fairshare.cpp b/cpp/src/qpid/broker/Fairshare.cpp
index e6bbf86691..313aa746f1 100644
--- a/cpp/src/qpid/broker/Fairshare.cpp
+++ b/cpp/src/qpid/broker/Fairshare.cpp
@@ -24,6 +24,7 @@
#include "qpid/log/Statement.h"
#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
+#include <boost/assign/list_of.hpp>
namespace qpid {
namespace broker {
@@ -104,51 +105,80 @@ bool Fairshare::setState(Messages& m, uint priority, uint count)
return fairshare && fairshare->setState(priority, count);
}
-int getIntegerSetting(const qpid::framing::FieldTable& settings, const std::string& key)
+int getIntegerSetting(const qpid::framing::FieldTable& settings, const std::vector<std::string>& keys)
{
- qpid::framing::FieldTable::ValuePtr v = settings.get(key);
+ qpid::framing::FieldTable::ValuePtr v;
+ std::vector<std::string>::const_iterator i = keys.begin();
+ while (!v && i != keys.end()) {
+ v = settings.get(*i++);
+ }
+
if (!v) {
return 0;
} else if (v->convertsTo<int>()) {
return v->get<int>();
} else if (v->convertsTo<std::string>()){
std::string s = v->get<std::string>();
- try {
- return boost::lexical_cast<int>(s);
+ try {
+ return boost::lexical_cast<int>(s);
} catch(const boost::bad_lexical_cast&) {
- QPID_LOG(warning, "Ignoring invalid integer value for " << key << ": " << s);
+ QPID_LOG(warning, "Ignoring invalid integer value for " << *i << ": " << s);
return 0;
}
} else {
- QPID_LOG(warning, "Ignoring invalid integer value for " << key << ": " << *v);
+ QPID_LOG(warning, "Ignoring invalid integer value for " << *i << ": " << *v);
return 0;
}
}
-int getSetting(const qpid::framing::FieldTable& settings, const std::string& key, int minvalue, int maxvalue)
+int getIntegerSettingForKey(const qpid::framing::FieldTable& settings, const std::string& key)
+{
+ return getIntegerSetting(settings, boost::assign::list_of<std::string>(key));
+}
+
+int getSetting(const qpid::framing::FieldTable& settings, const std::vector<std::string>& keys, int minvalue, int maxvalue)
+{
+ return std::max(minvalue,std::min(getIntegerSetting(settings, keys), maxvalue));
+}
+
+std::auto_ptr<Fairshare> getFairshareForKey(const qpid::framing::FieldTable& settings, uint levels, const std::string& key)
+{
+ uint defaultLimit = getIntegerSettingForKey(settings, key);
+ std::auto_ptr<Fairshare> fairshare(new Fairshare(levels, defaultLimit));
+ for (uint i = 0; i < levels; i++) {
+ std::string levelKey = (boost::format("%1%-%2%") % key % i).str();
+ if(settings.isSet(levelKey)) {
+ fairshare->setLimit(i, getIntegerSettingForKey(settings, levelKey));
+ }
+ }
+ if (!fairshare->isNull()) {
+ return fairshare;
+ } else {
+ return std::auto_ptr<Fairshare>();
+ }
+}
+
+std::auto_ptr<Fairshare> getFairshare(const qpid::framing::FieldTable& settings,
+ uint levels,
+ const std::vector<std::string>& keys)
{
- return std::max(minvalue,std::min(getIntegerSetting(settings, key), maxvalue));
+ std::auto_ptr<Fairshare> fairshare;
+ for (std::vector<std::string>::const_iterator i = keys.begin(); i != keys.end() && !fairshare.get(); ++i) {
+ fairshare = getFairshareForKey(settings, levels, *i);
+ }
+ return fairshare;
}
std::auto_ptr<Messages> Fairshare::create(const qpid::framing::FieldTable& settings)
{
+ using boost::assign::list_of;
std::auto_ptr<Messages> result;
- size_t levels = getSetting(settings, "x-qpid-priorities", 1, 100);
+ size_t levels = getSetting(settings, list_of<std::string>("qpid.priorities")("x-qpid-priorities"), 0, 100);
if (levels) {
- uint defaultLimit = getIntegerSetting(settings, "x-qpid-fairshare");
- std::auto_ptr<Fairshare> fairshare(new Fairshare(levels, defaultLimit));
- for (uint i = 0; i < levels; i++) {
- std::string key = (boost::format("x-qpid-fairshare-%1%") % i).str();
- if(settings.isSet(key)) {
- fairshare->setLimit(i, getIntegerSetting(settings, key));
- }
- }
-
- if (fairshare->isNull()) {
- result = std::auto_ptr<Messages>(new PriorityQueue(levels));
- } else {
- result = fairshare;
- }
+ std::auto_ptr<Fairshare> fairshare =
+ getFairshare(settings, levels, list_of<std::string>("qpid.fairshare")("x-qpid-fairshare"));
+ if (fairshare.get()) result = fairshare;
+ else result = std::auto_ptr<Messages>(new PriorityQueue(levels));
}
return result;
}
diff --git a/cpp/src/qpid/broker/Fairshare.h b/cpp/src/qpid/broker/Fairshare.h
index 6c4b87f857..1b25721e0c 100644
--- a/cpp/src/qpid/broker/Fairshare.h
+++ b/cpp/src/qpid/broker/Fairshare.h
@@ -41,18 +41,18 @@ class Fairshare : public PriorityQueue
bool getState(uint& priority, uint& count) const;
bool setState(uint priority, uint count);
void setLimit(size_t level, uint limit);
+ bool isNull();
static std::auto_ptr<Messages> create(const qpid::framing::FieldTable& settings);
static bool getState(const Messages&, uint& priority, uint& count);
static bool setState(Messages&, uint priority, uint count);
private:
std::vector<uint> limits;
-
+
uint priority;
uint count;
-
+
uint currentLevel();
uint nextLevel();
- bool isNull();
bool limitReached();
bool findFrontLevel(uint& p, PriorityLevels&);
};
diff --git a/cpp/src/qpid/broker/FanOutExchange.cpp b/cpp/src/qpid/broker/FanOutExchange.cpp
index ac2c914a97..5879fa0892 100644
--- a/cpp/src/qpid/broker/FanOutExchange.cpp
+++ b/cpp/src/qpid/broker/FanOutExchange.cpp
@@ -18,6 +18,7 @@
* under the License.
*
*/
+#include "qpid/log/Statement.h"
#include "qpid/broker/FanOutExchange.h"
#include "qpid/broker/FedOps.h"
#include <algorithm>
@@ -65,7 +66,7 @@ bool FanOutExchange::bind(Queue::shared_ptr queue, const string& /*key*/, const
} else if (fedOp == fedOpUnbind) {
propagate = fedBinding.delOrigin(queue->getName(), fedOrigin);
if (fedBinding.countFedBindings(queue->getName()) == 0)
- unbind(queue, "", 0);
+ unbind(queue, "", args);
} else if (fedOp == fedOpReorigin) {
if (fedBinding.hasLocal()) {
propagateFedOp(string(), string(), fedOpBind, string());
@@ -78,12 +79,16 @@ bool FanOutExchange::bind(Queue::shared_ptr queue, const string& /*key*/, const
return true;
}
-bool FanOutExchange::unbind(Queue::shared_ptr queue, const string& /*key*/, const FieldTable* /*args*/)
+bool FanOutExchange::unbind(Queue::shared_ptr queue, const string& /*key*/, const FieldTable* args)
{
+ string fedOrigin(args ? args->getAsString(qpidFedOrigin) : "");
bool propagate = false;
+ QPID_LOG(debug, "Unbinding queue " << queue->getName()
+ << " from exchange " << getName() << " origin=" << fedOrigin << ")" );
+
if (bindings.remove_if(MatchQueue(queue))) {
- propagate = fedBinding.delOrigin();
+ propagate = fedBinding.delOrigin(queue->getName(), fedOrigin);
if (mgmtExchange != 0) {
mgmtExchange->dec_bindingCount();
}
diff --git a/cpp/src/qpid/broker/FifoDistributor.cpp b/cpp/src/qpid/broker/FifoDistributor.cpp
new file mode 100644
index 0000000000..cdb32d8c8c
--- /dev/null
+++ b/cpp/src/qpid/broker/FifoDistributor.cpp
@@ -0,0 +1,58 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/FifoDistributor.h"
+
+using namespace qpid::broker;
+
+FifoDistributor::FifoDistributor(Messages& container)
+ : messages(container) {}
+
+bool FifoDistributor::nextConsumableMessage( Consumer::shared_ptr&, QueuedMessage& next )
+{
+ if (!messages.empty()) {
+ next = messages.front(); // by default, consume oldest msg
+ return true;
+ }
+ return false;
+}
+
+bool FifoDistributor::allocate(const std::string&, const QueuedMessage& )
+{
+ // by default, all messages present on the queue may be allocated as they have yet to
+ // be acquired.
+ return true;
+}
+
+bool FifoDistributor::nextBrowsableMessage( Consumer::shared_ptr& c, QueuedMessage& next )
+{
+ if (!messages.empty() && messages.next(c->position, next))
+ return true;
+ return false;
+}
+
+void FifoDistributor::query(qpid::types::Variant::Map&) const
+{
+ // nothing to see here....
+}
+
diff --git a/cpp/src/qpid/broker/IncompleteMessageList.h b/cpp/src/qpid/broker/FifoDistributor.h
index a4debd1233..245537ed12 100644
--- a/cpp/src/qpid/broker/IncompleteMessageList.h
+++ b/cpp/src/qpid/broker/FifoDistributor.h
@@ -1,3 +1,6 @@
+#ifndef _broker_FifoDistributor_h
+#define _broker_FifoDistributor_h
+
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -7,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -18,41 +21,38 @@
* under the License.
*
*/
-#ifndef _IncompleteMessageList_
-#define _IncompleteMessageList_
-#include "qpid/broker/BrokerImportExport.h"
-#include "qpid/sys/Monitor.h"
-#include "qpid/broker/Message.h"
-#include <boost/intrusive_ptr.hpp>
-#include <boost/function.hpp>
-#include <list>
+/** Simple MessageDistributor for FIFO Queues - the HEAD message is always the next
+ * available message for consumption.
+ */
+
+#include "qpid/broker/MessageDistributor.h"
namespace qpid {
namespace broker {
-class IncompleteMessageList
+class Messages;
+
+class FifoDistributor : public MessageDistributor
{
- typedef std::list< boost::intrusive_ptr<Message> > Messages;
+ public:
+ FifoDistributor(Messages& container);
- void enqueueComplete(const boost::intrusive_ptr<Message>&);
+ /** Locking Note: all methods assume the caller is holding the Queue::messageLock
+ * during the method call.
+ */
- sys::Monitor lock;
- Messages incomplete;
- Message::MessageCallback callback;
+ /** MessageDistributor interface */
-public:
- typedef Message::MessageCallback CompletionListener;
+ bool nextConsumableMessage( Consumer::shared_ptr& consumer, QueuedMessage& next );
+ bool allocate(const std::string& consumer, const QueuedMessage& target);
+ bool nextBrowsableMessage( Consumer::shared_ptr& consumer, QueuedMessage& next );
+ void query(qpid::types::Variant::Map&) const;
- QPID_BROKER_EXTERN IncompleteMessageList();
- QPID_BROKER_EXTERN ~IncompleteMessageList();
-
- QPID_BROKER_EXTERN void add(boost::intrusive_ptr<Message> msg);
- QPID_BROKER_EXTERN void process(const CompletionListener& l, bool sync);
- void each(const CompletionListener& l);
+ private:
+ Messages& messages;
};
-
}}
#endif
diff --git a/cpp/src/qpid/broker/HeadersExchange.cpp b/cpp/src/qpid/broker/HeadersExchange.cpp
index 82ac5911ee..4bda70d313 100644
--- a/cpp/src/qpid/broker/HeadersExchange.cpp
+++ b/cpp/src/qpid/broker/HeadersExchange.cpp
@@ -112,9 +112,14 @@ bool HeadersExchange::bind(Queue::shared_ptr queue, const string& bindingKey, co
{
Mutex::ScopedLock l(lock);
- Binding::shared_ptr binding (new Binding (bindingKey, queue, this, *args));
+ //NOTE: do not include the fed op/tags/origin in the
+ //arguments as when x-match is 'all' these would prevent
+ //matching (they are internally added properties
+ //controlling binding propagation but not relevant to
+ //actual routing)
+ Binding::shared_ptr binding (new Binding (bindingKey, queue, this, extra_args));
BoundKey bk(binding);
- if (bindings.add_unless(bk, MatchArgs(queue, args))) {
+ if (bindings.add_unless(bk, MatchArgs(queue, &extra_args))) {
binding->startManagement();
propagate = bk.fedBinding.addOrigin(queue->getName(), fedOrigin);
if (mgmtExchange != 0) {
@@ -158,12 +163,13 @@ bool HeadersExchange::bind(Queue::shared_ptr queue, const string& bindingKey, co
return true;
}
-bool HeadersExchange::unbind(Queue::shared_ptr queue, const string& bindingKey, const FieldTable*){
+bool HeadersExchange::unbind(Queue::shared_ptr queue, const string& bindingKey, const FieldTable *args){
bool propagate = false;
+ string fedOrigin(args ? args->getAsString(qpidFedOrigin) : "");
{
Mutex::ScopedLock l(lock);
- FedUnbindModifier modifier;
+ FedUnbindModifier modifier(queue->getName(), fedOrigin);
MatchKey match_key(queue, bindingKey);
bindings.modify_if(match_key, modifier);
propagate = modifier.shouldPropagate;
@@ -330,11 +336,7 @@ HeadersExchange::FedUnbindModifier::FedUnbindModifier() : shouldUnbind(false), s
bool HeadersExchange::FedUnbindModifier::operator()(BoundKey & bk)
{
- if ("" == fedOrigin) {
- shouldPropagate = bk.fedBinding.delOrigin();
- } else {
- shouldPropagate = bk.fedBinding.delOrigin(queueName, fedOrigin);
- }
+ shouldPropagate = bk.fedBinding.delOrigin(queueName, fedOrigin);
if (bk.fedBinding.countFedBindings(queueName) == 0)
{
shouldUnbind = true;
diff --git a/cpp/src/qpid/broker/IncompleteMessageList.cpp b/cpp/src/qpid/broker/IncompleteMessageList.cpp
deleted file mode 100644
index 34d92fa752..0000000000
--- a/cpp/src/qpid/broker/IncompleteMessageList.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-#include "qpid/broker/IncompleteMessageList.h"
-
-namespace qpid {
-namespace broker {
-
-IncompleteMessageList::IncompleteMessageList() :
- callback(boost::bind(&IncompleteMessageList::enqueueComplete, this, _1))
-{}
-
-IncompleteMessageList::~IncompleteMessageList()
-{
- // No lock here. We are relying on Messsag::reset*CompleteCallback
- // to ensure no callbacks are in progress before they return.
- for (Messages::iterator i = incomplete.begin(); i != incomplete.end(); ++i) {
- (*i)->resetEnqueueCompleteCallback();
- (*i)->resetDequeueCompleteCallback();
- }
-}
-
-void IncompleteMessageList::add(boost::intrusive_ptr<Message> msg)
-{
- sys::Mutex::ScopedLock l(lock);
- msg->setEnqueueCompleteCallback(callback);
- incomplete.push_back(msg);
-}
-
-void IncompleteMessageList::enqueueComplete(const boost::intrusive_ptr<Message>& ) {
- sys::Mutex::ScopedLock l(lock);
- lock.notify();
-}
-
-void IncompleteMessageList::process(const CompletionListener& listen, bool sync)
-{
- sys::Mutex::ScopedLock l(lock);
- while (!incomplete.empty()) {
- boost::intrusive_ptr<Message>& msg = incomplete.front();
- if (!msg->isEnqueueComplete()) {
- if (sync){
- {
- sys::Mutex::ScopedUnlock u(lock);
- msg->flush(); // Can re-enter IncompleteMessageList::enqueueComplete
- }
- while (!msg->isEnqueueComplete())
- lock.wait();
- } else {
- //leave the message as incomplete for now
- return;
- }
- }
- listen(msg);
- incomplete.pop_front();
- }
-}
-
-void IncompleteMessageList::each(const CompletionListener& listen) {
- Messages snapshot;
- {
- sys::Mutex::ScopedLock l(lock);
- snapshot = incomplete;
- }
- std::for_each(incomplete.begin(), incomplete.end(), listen);
-}
-
-}}
diff --git a/cpp/src/qpid/broker/LegacyLVQ.cpp b/cpp/src/qpid/broker/LegacyLVQ.cpp
index a811a86492..3262e343a3 100644
--- a/cpp/src/qpid/broker/LegacyLVQ.cpp
+++ b/cpp/src/qpid/broker/LegacyLVQ.cpp
@@ -93,11 +93,7 @@ void LegacyLVQ::removeIf(Predicate p)
//purging of an LVQ is not enabled if the broker is clustered
//(expired messages will be removed on delivery and consolidated
//by key as part of normal LVQ operation).
-
- //TODO: Is there a neater way to check whether broker is
- //clustered? Here we assume that if the clustered timer is the
- //same as the regular timer, we are not clustered:
- if (!broker || &(broker->getClusterTimer()) == &(broker->getTimer()))
+ if (!broker || !broker->isInCluster())
MessageMap::removeIf(p);
}
diff --git a/cpp/src/qpid/broker/Link.cpp b/cpp/src/qpid/broker/Link.cpp
index e1091df724..8010bf43e7 100644
--- a/cpp/src/qpid/broker/Link.cpp
+++ b/cpp/src/qpid/broker/Link.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
@@ -30,7 +30,6 @@
#include "qpid/framing/enum.h"
#include "qpid/framing/reply_exceptions.h"
#include "qpid/broker/AclModule.h"
-#include "qpid/sys/ClusterSafe.h"
using namespace qpid::broker;
using qpid::framing::Buffer;
@@ -57,8 +56,8 @@ Link::Link(LinkRegistry* _links,
string& _password,
Broker* _broker,
Manageable* parent)
- : links(_links), store(_store), host(_host), port(_port),
- transport(_transport),
+ : links(_links), store(_store), host(_host), port(_port),
+ transport(_transport),
durable(_durable),
authMechanism(_authMechanism), username(_username), password(_password),
persistenceId(0), mgmtObject(0), broker(_broker), state(0),
@@ -97,7 +96,8 @@ void Link::setStateLH (int newState)
return;
state = newState;
- if (mgmtObject == 0)
+
+ if (hideManagement())
return;
switch (state)
@@ -117,12 +117,12 @@ void Link::startConnectionLH ()
// Set the state before calling connect. It is possible that connect
// will fail synchronously and call Link::closed before returning.
setStateLH(STATE_CONNECTING);
- broker->connect (host, port, transport,
+ broker->connect (host, boost::lexical_cast<std::string>(port), transport,
boost::bind (&Link::closed, this, _1, _2));
QPID_LOG (debug, "Inter-broker link connecting to " << host << ":" << port);
} catch(std::exception& e) {
setStateLH(STATE_WAITING);
- if (mgmtObject != 0)
+ if (!hideManagement())
mgmtObject->set_lastError (e.what());
}
}
@@ -133,8 +133,7 @@ void Link::established ()
addr << host << ":" << port;
QPID_LOG (info, "Inter-broker link established to " << addr.str());
- // Don't raise the management event in a cluster, other members wont't get this call.
- if (!sys::isCluster())
+ if (!hideManagement() && agent)
agent->raiseEvent(_qmf::EventBrokerLinkUp(addr.str()));
{
@@ -154,12 +153,11 @@ void Link::closed (int, std::string text)
connection = 0;
- // Don't raise the management event in a cluster, other members wont't get this call.
if (state == STATE_OPERATIONAL) {
stringstream addr;
addr << host << ":" << port;
QPID_LOG (warning, "Inter-broker link disconnected from " << addr.str());
- if (!sys::isCluster())
+ if (!hideManagement() && agent)
agent->raiseEvent(_qmf::EventBrokerLinkDown(addr.str()));
}
@@ -172,7 +170,7 @@ void Link::closed (int, std::string text)
if (state != STATE_FAILED)
{
setStateLH(STATE_WAITING);
- if (mgmtObject != 0)
+ if (!hideManagement())
mgmtObject->set_lastError (text);
}
@@ -221,7 +219,7 @@ void Link::cancel(Bridge::shared_ptr bridge)
{
{
Mutex::ScopedLock mutex(lock);
-
+
for (Bridges::iterator i = created.begin(); i != created.end(); i++) {
if ((*i).get() == bridge.get()) {
created.erase(i);
@@ -250,6 +248,19 @@ void Link::ioThreadProcessing()
return;
QPID_LOG(debug, "Link::ioThreadProcessing()");
+ // check for bridge session errors and recover
+ if (!active.empty()) {
+ Bridges::iterator removed = std::remove_if(
+ active.begin(), active.end(), !boost::bind(&Bridge::isSessionReady, _1));
+ for (Bridges::iterator i = removed; i != active.end(); ++i) {
+ Bridge::shared_ptr bridge = *i;
+ bridge->closed();
+ bridge->cancel(*connection);
+ created.push_back(bridge);
+ }
+ active.erase(removed, active.end());
+ }
+
//process any pending creates and/or cancellations
if (!created.empty()) {
for (Bridges::iterator i = created.begin(); i != created.end(); ++i) {
@@ -277,9 +288,9 @@ void Link::maintenanceVisit ()
{
Mutex::ScopedLock mutex(lock);
- if (connection && updateUrls) {
+ if (connection && updateUrls) {
urls.reset(connection->getKnownHosts());
- QPID_LOG(debug, "Known hosts for peer of inter-broker link: " << urls);
+ QPID_LOG(debug, "Known hosts for peer of inter-broker link: " << urls);
updateUrls = false;
}
@@ -298,7 +309,7 @@ void Link::maintenanceVisit ()
}
}
}
- else if (state == STATE_OPERATIONAL && (!created.empty() || !cancellations.empty()) && connection != 0)
+ else if (state == STATE_OPERATIONAL && (!active.empty() || !created.empty() || !cancellations.empty()) && connection != 0)
connection->requestIOProcessing (boost::bind(&Link::ioThreadProcessing, this));
}
@@ -309,7 +320,7 @@ void Link::reconnect(const qpid::Address& a)
port = a.port;
transport = a.protocol;
startConnectionLH();
- if (mgmtObject != 0) {
+ if (!hideManagement()) {
stringstream errorString;
errorString << "Failed over to " << a;
mgmtObject->set_lastError(errorString.str());
@@ -319,7 +330,7 @@ void Link::reconnect(const qpid::Address& a)
bool Link::tryFailover()
{
Address next;
- if (urls.next(next) &&
+ if (urls.next(next) &&
(next.host != host || next.port != port || next.protocol != transport)) {
links->changeAddress(Address(transport, host, port), next);
QPID_LOG(debug, "Link failing over to " << host << ":" << port);
@@ -329,6 +340,12 @@ bool Link::tryFailover()
}
}
+// Management updates for a linke are inconsistent in a cluster, so they are
+// suppressed.
+bool Link::hideManagement() const {
+ return !mgmtObject || ( broker && broker->isInCluster());
+}
+
uint Link::nextChannel()
{
Mutex::ScopedLock mutex(lock);
@@ -341,7 +358,7 @@ void Link::notifyConnectionForced(const string text)
Mutex::ScopedLock mutex(lock);
setStateLH(STATE_FAILED);
- if (mgmtObject != 0)
+ if (!hideManagement())
mgmtObject->set_lastError(text);
}
@@ -363,7 +380,7 @@ Link::shared_ptr Link::decode(LinkRegistry& links, Buffer& buffer)
string authMechanism;
string username;
string password;
-
+
buffer.getShortString(host);
port = buffer.getShort();
buffer.getShortString(transport);
@@ -375,7 +392,7 @@ Link::shared_ptr Link::decode(LinkRegistry& links, Buffer& buffer)
return links.declare(host, port, transport, durable, authMechanism, username, password).first;
}
-void Link::encode(Buffer& buffer) const
+void Link::encode(Buffer& buffer) const
{
buffer.putShortString(string("link"));
buffer.putShortString(host);
@@ -387,8 +404,8 @@ void Link::encode(Buffer& buffer) const
buffer.putShortString(password);
}
-uint32_t Link::encodedSize() const
-{
+uint32_t Link::encodedSize() const
+{
return host.size() + 1 // short-string (host)
+ 5 // short-string ("link")
+ 2 // port
diff --git a/cpp/src/qpid/broker/Link.h b/cpp/src/qpid/broker/Link.h
index 75a680ff5d..4badd8b3a1 100644
--- a/cpp/src/qpid/broker/Link.h
+++ b/cpp/src/qpid/broker/Link.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -85,6 +85,7 @@ namespace qpid {
void destroy(); // Called when mgmt deletes this link
void ioThreadProcessing(); // Called on connection's IO thread by request
bool tryFailover(); // Called during maintenance visit
+ bool hideManagement() const;
public:
typedef boost::shared_ptr<Link> shared_ptr;
@@ -122,12 +123,12 @@ namespace qpid {
void notifyConnectionForced(const std::string text);
void setPassive(bool p);
-
+
// PersistableConfig:
void setPersistenceId(uint64_t id) const;
uint64_t getPersistenceId() const { return persistenceId; }
uint32_t encodedSize() const;
- void encode(framing::Buffer& buffer) const;
+ void encode(framing::Buffer& buffer) const;
const std::string& getName() const;
static Link::shared_ptr decode(LinkRegistry& links, framing::Buffer& buffer);
@@ -135,6 +136,7 @@ namespace qpid {
// Manageable entry points
management::ManagementObject* GetManagementObject(void) const;
management::Manageable::status_t ManagementMethod(uint32_t, management::Args&, std::string&);
+
};
}
}
diff --git a/cpp/src/qpid/broker/LinkRegistry.cpp b/cpp/src/qpid/broker/LinkRegistry.cpp
index 7b1c75db74..e9885f5462 100644
--- a/cpp/src/qpid/broker/LinkRegistry.cpp
+++ b/cpp/src/qpid/broker/LinkRegistry.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
@@ -381,7 +381,7 @@ std::string LinkRegistry::createKey(const std::string& host, uint16_t port) {
return keystream.str();
}
-void LinkRegistry::setPassive(bool p)
+void LinkRegistry::setPassive(bool p)
{
Mutex::ScopedLock locker(lock);
passiveChanged = p != passive;
diff --git a/cpp/src/qpid/broker/Message.cpp b/cpp/src/qpid/broker/Message.cpp
index c589669e5a..d13109dad1 100644
--- a/cpp/src/qpid/broker/Message.cpp
+++ b/cpp/src/qpid/broker/Message.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
@@ -30,6 +30,7 @@
#include "qpid/framing/SendContent.h"
#include "qpid/framing/SequenceNumber.h"
#include "qpid/framing/TypeFilter.h"
+#include "qpid/framing/reply_exceptions.h"
#include "qpid/log/Statement.h"
#include <time.h>
@@ -49,27 +50,16 @@ TransferAdapter Message::TRANSFER;
Message::Message(const framing::SequenceNumber& id) :
frames(id), persistenceId(0), redelivered(false), loaded(false),
- staged(false), forcePersistentPolicy(false), publisher(0), adapter(0),
- expiration(FAR_FUTURE), enqueueCallback(0), dequeueCallback(0),
- inCallback(false), requiredCredit(0) {}
+ staged(false), forcePersistentPolicy(false), publisher(0), adapter(0),
+ expiration(FAR_FUTURE), dequeueCallback(0),
+ inCallback(false), requiredCredit(0), isManagementMessage(false), copyHeaderOnWrite(false)
+{}
-Message::Message(const Message& original) :
- PersistableMessage(), frames(original.frames), persistenceId(0), redelivered(false), loaded(false),
- staged(false), forcePersistentPolicy(false), publisher(0), adapter(0),
- expiration(original.expiration), enqueueCallback(0), dequeueCallback(0),
- inCallback(false), requiredCredit(0)
-{
- setExpiryPolicy(original.expiryPolicy);
-}
-
-Message::~Message()
-{
- if (expiryPolicy)
- expiryPolicy->forget(*this);
-}
+Message::~Message() {}
void Message::forcePersistent()
{
+ sys::Mutex::ScopedLock l(lock);
// only set forced bit if we actually need to force.
if (! getAdapter().isPersistent(frames) ){
forcePersistentPolicy = true;
@@ -86,7 +76,7 @@ std::string Message::getRoutingKey() const
return getAdapter().getRoutingKey(frames);
}
-std::string Message::getExchangeName() const
+std::string Message::getExchangeName() const
{
return getAdapter().getExchange(frames);
}
@@ -95,7 +85,7 @@ const boost::shared_ptr<Exchange> Message::getExchange(ExchangeRegistry& registr
{
if (!exchange) {
exchange = registry.get(getExchangeName());
- }
+ }
return exchange;
}
@@ -106,16 +96,19 @@ bool Message::isImmediate() const
const FieldTable* Message::getApplicationHeaders() const
{
+ sys::Mutex::ScopedLock l(lock);
return getAdapter().getApplicationHeaders(frames);
}
std::string Message::getAppId() const
{
+ sys::Mutex::ScopedLock l(lock);
return getAdapter().getAppId(frames);
}
bool Message::isPersistent() const
{
+ sys::Mutex::ScopedLock l(lock);
return (getAdapter().isPersistent(frames) || forcePersistentPolicy);
}
@@ -195,7 +188,7 @@ void Message::decodeContent(framing::Buffer& buffer)
} else {
//adjust header flags
MarkLastSegment f;
- frames.map_if(f, TypeFilter<HEADER_BODY>());
+ frames.map_if(f, TypeFilter<HEADER_BODY>());
}
//mark content loaded
loaded = true;
@@ -247,7 +240,7 @@ void Message::destroy()
bool Message::getContentFrame(const Queue& queue, AMQFrame& frame, uint16_t maxContentSize, uint64_t offset) const
{
intrusive_ptr<const PersistableMessage> pmsg(this);
-
+
bool done = false;
string& data = frame.castBody<AMQContentBody>()->getData();
store->loadContent(queue, pmsg, data, offset, maxContentSize);
@@ -272,7 +265,7 @@ void Message::sendContent(const Queue& queue, framing::FrameHandler& out, uint16
uint16_t maxContentSize = maxFrameSize - AMQFrame::frameOverhead();
bool morecontent = true;
for (uint64_t offset = 0; morecontent; offset += maxContentSize)
- {
+ {
AMQFrame frame((AMQContentBody()));
morecontent = getContentFrame(queue, frame, maxContentSize, offset);
out.handle(frame);
@@ -290,7 +283,10 @@ void Message::sendHeader(framing::FrameHandler& out, uint16_t /*maxFrameSize*/)
{
sys::Mutex::ScopedLock l(lock);
Relay f(out);
- frames.map_if(f, TypeFilter<HEADER_BODY>());
+ frames.map_if(f, TypeFilter<HEADER_BODY>());
+ //as frame (and pointer to body) has now been passed to handler,
+ //subsequent modifications should use a copy
+ copyHeaderOnWrite = true;
}
// TODO aconway 2007-11-09: Obsolete, remove. Was used to cover over
@@ -320,13 +316,14 @@ bool Message::isContentLoaded() const
}
-namespace
+namespace
{
const std::string X_QPID_TRACE("x-qpid.trace");
}
bool Message::isExcluded(const std::vector<std::string>& excludes) const
{
+ sys::Mutex::ScopedLock l(lock);
const FieldTable* headers = getApplicationHeaders();
if (headers) {
std::string traceStr = headers->getAsString(X_QPID_TRACE);
@@ -345,11 +342,30 @@ bool Message::isExcluded(const std::vector<std::string>& excludes) const
return false;
}
+class CloneHeaderBody
+{
+public:
+ void operator()(AMQFrame& f)
+ {
+ f.cloneBody();
+ }
+};
+
+AMQHeaderBody* Message::getHeaderBody()
+{
+ if (copyHeaderOnWrite) {
+ CloneHeaderBody f;
+ frames.map_if(f, TypeFilter<HEADER_BODY>());
+ copyHeaderOnWrite = false;
+ }
+ return frames.getHeaders();
+}
+
void Message::addTraceId(const std::string& id)
{
sys::Mutex::ScopedLock l(lock);
if (isA<MessageTransferBody>()) {
- FieldTable& headers = getProperties<MessageProperties>()->getApplicationHeaders();
+ FieldTable& headers = getModifiableProperties<MessageProperties>()->getApplicationHeaders();
std::string trace = headers.getAsString(X_QPID_TRACE);
if (trace.empty()) {
headers.setString(X_QPID_TRACE, id);
@@ -357,13 +373,22 @@ void Message::addTraceId(const std::string& id)
trace += ",";
trace += id;
headers.setString(X_QPID_TRACE, trace);
- }
+ }
}
}
-void Message::setTimestamp(const boost::intrusive_ptr<ExpiryPolicy>& e)
+void Message::setTimestamp()
+{
+ sys::Mutex::ScopedLock l(lock);
+ DeliveryProperties* props = getModifiableProperties<DeliveryProperties>();
+ time_t now = ::time(0);
+ props->setTimestamp(now); // AMQP-0.10: posix time_t - secs since Epoch
+}
+
+void Message::computeExpiration(const boost::intrusive_ptr<ExpiryPolicy>& e)
{
- DeliveryProperties* props = getProperties<DeliveryProperties>();
+ sys::Mutex::ScopedLock l(lock);
+ DeliveryProperties* props = getModifiableProperties<DeliveryProperties>();
if (props->getTtl()) {
// AMQP requires setting the expiration property to be posix
// time_t in seconds. TTL is in milliseconds
@@ -372,26 +397,70 @@ void Message::setTimestamp(const boost::intrusive_ptr<ExpiryPolicy>& e)
time_t now = ::time(0);
props->setExpiration(now + (props->getTtl()/1000));
}
- // Use higher resolution time for the internal expiry calculation.
- expiration = AbsTime(AbsTime::now(), Duration(props->getTtl() * TIME_MSEC));
- setExpiryPolicy(e);
+ if (e) {
+ // Use higher resolution time for the internal expiry calculation.
+ // Prevent overflow as a signed int64_t
+ Duration ttl(std::min(props->getTtl() * TIME_MSEC,
+ (uint64_t) std::numeric_limits<int64_t>::max()));
+ expiration = AbsTime(e->getCurrentTime(), ttl);
+ setExpiryPolicy(e);
+ }
}
}
void Message::adjustTtl()
{
- DeliveryProperties* props = getProperties<DeliveryProperties>();
+ sys::Mutex::ScopedLock l(lock);
+ DeliveryProperties* props = getModifiableProperties<DeliveryProperties>();
if (props->getTtl()) {
- sys::Mutex::ScopedLock l(lock);
- sys::Duration d(sys::AbsTime::now(), getExpiration());
- props->setTtl(int64_t(d) > 0 ? int64_t(d)/1000000 : 1); // convert from ns to ms; set to 1 if expired
+ if (expiration < FAR_FUTURE) {
+ sys::AbsTime current(
+ expiryPolicy ? expiryPolicy->getCurrentTime() : sys::AbsTime::now());
+ sys::Duration ttl(current, getExpiration());
+ // convert from ns to ms; set to 1 if expired
+ props->setTtl(int64_t(ttl) >= 1000000 ? int64_t(ttl)/1000000 : 1);
+ }
}
}
+void Message::setRedelivered()
+{
+ sys::Mutex::ScopedLock l(lock);
+ getModifiableProperties<framing::DeliveryProperties>()->setRedelivered(true);
+}
+
+void Message::insertCustomProperty(const std::string& key, int64_t value)
+{
+ sys::Mutex::ScopedLock l(lock);
+ getModifiableProperties<MessageProperties>()->getApplicationHeaders().setInt64(key,value);
+}
+
+void Message::insertCustomProperty(const std::string& key, const std::string& value)
+{
+ sys::Mutex::ScopedLock l(lock);
+ getModifiableProperties<MessageProperties>()->getApplicationHeaders().setString(key,value);
+}
+
+void Message::removeCustomProperty(const std::string& key)
+{
+ sys::Mutex::ScopedLock l(lock);
+ getModifiableProperties<MessageProperties>()->getApplicationHeaders().erase(key);
+}
+
+void Message::setExchange(const std::string& exchange)
+{
+ sys::Mutex::ScopedLock l(lock);
+ getModifiableProperties<DeliveryProperties>()->setExchange(exchange);
+}
+
+void Message::clearApplicationHeadersFlag()
+{
+ sys::Mutex::ScopedLock l(lock);
+ getModifiableProperties<MessageProperties>()->clearApplicationHeadersFlag();
+}
+
void Message::setExpiryPolicy(const boost::intrusive_ptr<ExpiryPolicy>& e) {
expiryPolicy = e;
- if (expiryPolicy)
- expiryPolicy->willExpire(*this);
}
bool Message::hasExpired()
@@ -415,30 +484,12 @@ struct ScopedSet {
};
}
-void Message::allEnqueuesComplete() {
- ScopedSet ss(callbackLock, inCallback);
- MessageCallback* cb = enqueueCallback;
- if (cb && *cb) (*cb)(intrusive_ptr<Message>(this));
-}
-
void Message::allDequeuesComplete() {
ScopedSet ss(callbackLock, inCallback);
MessageCallback* cb = dequeueCallback;
if (cb && *cb) (*cb)(intrusive_ptr<Message>(this));
}
-void Message::setEnqueueCompleteCallback(MessageCallback& cb) {
- sys::Mutex::ScopedLock l(callbackLock);
- while (inCallback) callbackLock.wait();
- enqueueCallback = &cb;
-}
-
-void Message::resetEnqueueCompleteCallback() {
- sys::Mutex::ScopedLock l(callbackLock);
- while (inCallback) callbackLock.wait();
- enqueueCallback = 0;
-}
-
void Message::setDequeueCompleteCallback(MessageCallback& cb) {
sys::Mutex::ScopedLock l(callbackLock);
while (inCallback) callbackLock.wait();
@@ -452,12 +503,11 @@ void Message::resetDequeueCompleteCallback() {
}
uint8_t Message::getPriority() const {
+ sys::Mutex::ScopedLock l(lock);
return getAdapter().getPriority(frames);
}
-framing::FieldTable& Message::getOrInsertHeaders()
-{
- return getProperties<MessageProperties>()->getApplicationHeaders();
-}
+bool Message::getIsManagementMessage() const { return isManagementMessage; }
+void Message::setIsManagementMessage(bool b) { isManagementMessage = b; }
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/Message.h b/cpp/src/qpid/broker/Message.h
index f7dd2734b6..dda45d73e6 100644
--- a/cpp/src/qpid/broker/Message.h
+++ b/cpp/src/qpid/broker/Message.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -29,17 +29,21 @@
#include "qpid/sys/Monitor.h"
#include "qpid/sys/Time.h"
#include <boost/function.hpp>
+#include <boost/intrusive_ptr.hpp>
#include <boost/shared_ptr.hpp>
+#include <memory>
#include <string>
#include <vector>
namespace qpid {
-
+
namespace framing {
+class AMQBody;
+class AMQHeaderBody;
class FieldTable;
class SequenceNumber;
}
-
+
namespace broker {
class ConnectionToken;
class Exchange;
@@ -51,11 +55,10 @@ class ExpiryPolicy;
class Message : public PersistableMessage {
public:
typedef boost::function<void (const boost::intrusive_ptr<Message>&)> MessageCallback;
-
+
QPID_BROKER_EXTERN Message(const framing::SequenceNumber& id = framing::SequenceNumber());
- QPID_BROKER_EXTERN Message(const Message&);
QPID_BROKER_EXTERN ~Message();
-
+
uint64_t getPersistenceId() const { return persistenceId; }
void setPersistenceId(uint64_t _persistenceId) const { persistenceId = _persistenceId; }
@@ -75,27 +78,31 @@ public:
bool isImmediate() const;
QPID_BROKER_EXTERN const framing::FieldTable* getApplicationHeaders() const;
QPID_BROKER_EXTERN std::string getAppId() const;
- framing::FieldTable& getOrInsertHeaders();
QPID_BROKER_EXTERN bool isPersistent() const;
bool requiresAccept();
- QPID_BROKER_EXTERN void setTimestamp(const boost::intrusive_ptr<ExpiryPolicy>& e);
+ /** determine msg expiration time using the TTL value if present */
+ QPID_BROKER_EXTERN void computeExpiration(const boost::intrusive_ptr<ExpiryPolicy>& e);
void setExpiryPolicy(const boost::intrusive_ptr<ExpiryPolicy>& e);
bool hasExpired();
sys::AbsTime getExpiration() const { return expiration; }
+ void setExpiration(sys::AbsTime exp) { expiration = exp; }
void adjustTtl();
-
- framing::FrameSet& getFrames() { return frames; }
- const framing::FrameSet& getFrames() const { return frames; }
-
- template <class T> T* getProperties() {
- qpid::framing::AMQHeaderBody* p = frames.getHeaders();
- return p->get<T>(true);
- }
+ void setRedelivered();
+ QPID_BROKER_EXTERN void insertCustomProperty(const std::string& key, int64_t value);
+ QPID_BROKER_EXTERN void insertCustomProperty(const std::string& key, const std::string& value);
+ QPID_BROKER_EXTERN void removeCustomProperty(const std::string& key);
+ void setExchange(const std::string&);
+ void clearApplicationHeadersFlag();
+ /** set the timestamp delivery property to the current time-of-day */
+ QPID_BROKER_EXTERN void setTimestamp();
+
+ framing::FrameSet& getFrames() { return frames; }
+ const framing::FrameSet& getFrames() const { return frames; }
template <class T> const T* getProperties() const {
- qpid::framing::AMQHeaderBody* p = frames.getHeaders();
- return p->get<T>(true);
+ const qpid::framing::AMQHeaderBody* p = frames.getHeaders();
+ return p->get<T>();
}
template <class T> const T* hasProperties() const {
@@ -103,6 +110,11 @@ public:
return p->get<T>();
}
+ template <class T> void eraseProperties() {
+ qpid::framing::AMQHeaderBody* p = frames.getHeaders();
+ p->erase<T>();
+ }
+
template <class T> const T* getMethod() const {
return frames.as<T>();
}
@@ -135,7 +147,7 @@ public:
QPID_BROKER_EXTERN void decodeHeader(framing::Buffer& buffer);
QPID_BROKER_EXTERN void decodeContent(framing::Buffer& buffer);
-
+
void QPID_BROKER_EXTERN tryReleaseContent();
void releaseContent();
void releaseContent(MessageStore* s);//deprecated, use 'setStore(store); releaseContent();' instead
@@ -149,24 +161,19 @@ public:
bool isExcluded(const std::vector<std::string>& excludes) const;
void addTraceId(const std::string& id);
-
- void forcePersistent();
- bool isForcedPersistent();
-
- /** Call cb when enqueue is complete, may call immediately. Holds cb by reference. */
- void setEnqueueCompleteCallback(MessageCallback& cb);
- void resetEnqueueCompleteCallback();
+ void forcePersistent();
+ bool isForcedPersistent();
/** Call cb when dequeue is complete, may call immediately. Holds cb by reference. */
void setDequeueCompleteCallback(MessageCallback& cb);
void resetDequeueCompleteCallback();
uint8_t getPriority() const;
-
+ bool getIsManagementMessage() const;
+ void setIsManagementMessage(bool b);
private:
MessageAdapter& getAdapter() const;
- void allEnqueuesComplete();
void allDequeuesComplete();
mutable sys::Mutex lock;
@@ -176,7 +183,7 @@ public:
bool redelivered;
bool loaded;
bool staged;
- bool forcePersistentPolicy; // used to force message as durable, via a broker policy
+ bool forcePersistentPolicy; // used to force message as durable, via a broker policy
ConnectionToken* publisher;
mutable MessageAdapter* adapter;
qpid::sys::AbsTime expiration;
@@ -187,11 +194,20 @@ public:
mutable boost::intrusive_ptr<Message> empty;
sys::Monitor callbackLock;
- MessageCallback* enqueueCallback;
MessageCallback* dequeueCallback;
bool inCallback;
uint32_t requiredCredit;
+ bool isManagementMessage;
+ mutable bool copyHeaderOnWrite;
+
+ /**
+ * Expects lock to be held
+ */
+ template <class T> T* getModifiableProperties() {
+ return getHeaderBody()->get<T>(true);
+ }
+ qpid::framing::AMQHeaderBody* getHeaderBody();
};
}}
diff --git a/cpp/src/qpid/broker/MessageBuilder.h b/cpp/src/qpid/broker/MessageBuilder.h
index 75dfd6781d..b99b8efee6 100644
--- a/cpp/src/qpid/broker/MessageBuilder.h
+++ b/cpp/src/qpid/broker/MessageBuilder.h
@@ -33,7 +33,7 @@ namespace qpid {
class Message;
class MessageStore;
- class MessageBuilder : public framing::FrameHandler{
+ class QPID_BROKER_CLASS_EXTERN MessageBuilder : public framing::FrameHandler{
public:
QPID_BROKER_EXTERN MessageBuilder(MessageStore* const store);
QPID_BROKER_EXTERN void handle(framing::AMQFrame& frame);
diff --git a/cpp/src/qpid/broker/MessageDistributor.h b/cpp/src/qpid/broker/MessageDistributor.h
new file mode 100644
index 0000000000..090393c160
--- /dev/null
+++ b/cpp/src/qpid/broker/MessageDistributor.h
@@ -0,0 +1,76 @@
+#ifndef _broker_MessageDistributor_h
+#define _broker_MessageDistributor_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.
+ *
+ */
+
+/** Abstraction used by Queue to determine the next "most desirable" message to provide to
+ * a particular consuming client
+ */
+
+
+#include "qpid/broker/Consumer.h"
+
+namespace qpid {
+namespace broker {
+
+struct QueuedMessage;
+
+class MessageDistributor
+{
+ public:
+ virtual ~MessageDistributor() {};
+
+ /** Locking Note: all methods assume the caller is holding the Queue::messageLock
+ * during the method call.
+ */
+
+ /** Determine the next message available for consumption by the consumer
+ * @param consumer the consumer that needs a message to consume
+ * @param next set to the next message that the consumer may consume.
+ * @return true if message is available and next is set
+ */
+ virtual bool nextConsumableMessage( Consumer::shared_ptr& consumer,
+ QueuedMessage& next ) = 0;
+
+ /** Allow the comsumer to take ownership of the given message.
+ * @param consumer the name of the consumer that is attempting to acquire the message
+ * @param qm the message to be acquired, previously returned from nextConsumableMessage()
+ * @return true if ownership is permitted, false if ownership cannot be assigned.
+ */
+ virtual bool allocate( const std::string& consumer,
+ const QueuedMessage& target) = 0;
+
+ /** Determine the next message available for browsing by the consumer
+ * @param consumer the consumer that is browsing the queue
+ * @param next set to the next message that the consumer may browse.
+ * @return true if a message is available and next is returned
+ */
+ virtual bool nextBrowsableMessage( Consumer::shared_ptr& consumer,
+ QueuedMessage& next ) = 0;
+
+ /** hook to add any interesting management state to the status map */
+ virtual void query(qpid::types::Variant::Map&) const = 0;
+};
+
+}}
+
+#endif
diff --git a/cpp/src/qpid/broker/MessageGroupManager.cpp b/cpp/src/qpid/broker/MessageGroupManager.cpp
new file mode 100644
index 0000000000..07b05f3b92
--- /dev/null
+++ b/cpp/src/qpid/broker/MessageGroupManager.cpp
@@ -0,0 +1,411 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "qpid/framing/FieldTable.h"
+#include "qpid/types/Variant.h"
+#include "qpid/log/Statement.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/broker/MessageGroupManager.h"
+
+using namespace qpid::broker;
+
+namespace {
+ const std::string GROUP_QUERY_KEY("qpid.message_group_queue");
+ const std::string GROUP_HEADER_KEY("group_header_key");
+ const std::string GROUP_STATE_KEY("group_state");
+ const std::string GROUP_ID_KEY("group_id");
+ const std::string GROUP_MSG_COUNT("msg_count");
+ const std::string GROUP_TIMESTAMP("timestamp");
+ const std::string GROUP_CONSUMER("consumer");
+}
+
+
+const std::string MessageGroupManager::qpidMessageGroupKey("qpid.group_header_key");
+const std::string MessageGroupManager::qpidSharedGroup("qpid.shared_msg_group");
+const std::string MessageGroupManager::qpidMessageGroupTimestamp("qpid.group_timestamp");
+
+
+void MessageGroupManager::unFree( const GroupState& state )
+{
+ GroupFifo::iterator pos = freeGroups.find( state.members.front() );
+ assert( pos != freeGroups.end() && pos->second == &state );
+ freeGroups.erase( pos );
+}
+
+void MessageGroupManager::own( GroupState& state, const std::string& owner )
+{
+ state.owner = owner;
+ unFree( state );
+}
+
+void MessageGroupManager::disown( GroupState& state )
+{
+ state.owner.clear();
+ assert(state.members.size());
+ assert(freeGroups.find(state.members.front()) == freeGroups.end());
+ freeGroups[state.members.front()] = &state;
+}
+
+const std::string MessageGroupManager::getGroupId( const QueuedMessage& qm ) const
+{
+ const qpid::framing::FieldTable* headers = qm.payload->getApplicationHeaders();
+ if (!headers) return defaultGroupId;
+ qpid::framing::FieldTable::ValuePtr id = headers->get( groupIdHeader );
+ if (!id || !id->convertsTo<std::string>()) return defaultGroupId;
+ return id->get<std::string>();
+}
+
+
+void MessageGroupManager::enqueued( const QueuedMessage& qm )
+{
+ // @todo KAG optimization - store reference to group state in QueuedMessage
+ // issue: const-ness??
+ std::string group( getGroupId(qm) );
+ GroupState &state(messageGroups[group]);
+ state.members.push_back(qm.position);
+ uint32_t total = state.members.size();
+ QPID_LOG( trace, "group queue " << qName <<
+ ": added message to group id=" << group << " total=" << total );
+ if (total == 1) {
+ // newly created group, no owner
+ state.group = group;
+ assert(freeGroups.find(qm.position) == freeGroups.end());
+ freeGroups[qm.position] = &state;
+ }
+}
+
+
+void MessageGroupManager::acquired( const QueuedMessage& qm )
+{
+ // @todo KAG avoid lookup: retrieve direct reference to group state from QueuedMessage
+ // issue: const-ness??
+ std::string group( getGroupId(qm) );
+ GroupMap::iterator gs = messageGroups.find( group );
+ assert( gs != messageGroups.end() );
+ GroupState& state( gs->second );
+ state.acquired += 1;
+ QPID_LOG( trace, "group queue " << qName <<
+ ": acquired message in group id=" << group << " acquired=" << state.acquired );
+}
+
+
+void MessageGroupManager::requeued( const QueuedMessage& qm )
+{
+ // @todo KAG avoid lookup: retrieve direct reference to group state from QueuedMessage
+ // issue: const-ness??
+ std::string group( getGroupId(qm) );
+ GroupMap::iterator gs = messageGroups.find( group );
+ assert( gs != messageGroups.end() );
+ GroupState& state( gs->second );
+ assert( state.acquired != 0 );
+ state.acquired -= 1;
+ if (state.acquired == 0 && state.owned()) {
+ QPID_LOG( trace, "group queue " << qName <<
+ ": consumer name=" << state.owner << " released group id=" << gs->first);
+ disown(state);
+ }
+ QPID_LOG( trace, "group queue " << qName <<
+ ": requeued message to group id=" << group << " acquired=" << state.acquired );
+}
+
+
+void MessageGroupManager::dequeued( const QueuedMessage& qm )
+{
+ // @todo KAG avoid lookup: retrieve direct reference to group state from QueuedMessage
+ // issue: const-ness??
+ std::string group( getGroupId(qm) );
+ GroupMap::iterator gs = messageGroups.find( group );
+ assert( gs != messageGroups.end() );
+ GroupState& state( gs->second );
+ assert( state.members.size() != 0 );
+ assert( state.acquired != 0 );
+ state.acquired -= 1;
+
+ // likely to be at or near begin() if dequeued in order
+ bool reFreeNeeded = false;
+ if (state.members.front() == qm.position) {
+ if (!state.owned()) {
+ // will be on the freeGroups list if mgmt is dequeueing rather than a consumer!
+ // if on freelist, it is indexed by first member, which is about to be removed!
+ unFree(state);
+ reFreeNeeded = true;
+ }
+ state.members.pop_front();
+ } else {
+ GroupState::PositionFifo::iterator pos = state.members.begin() + 1;
+ GroupState::PositionFifo::iterator end = state.members.end();
+ while (pos != end) {
+ if (*pos == qm.position) {
+ state.members.erase(pos);
+ break;
+ }
+ ++pos;
+ }
+ }
+
+ uint32_t total = state.members.size();
+ if (total == 0) {
+ QPID_LOG( trace, "group queue " << qName << ": deleting group id=" << gs->first);
+ messageGroups.erase( gs );
+ } else if (state.acquired == 0 && state.owned()) {
+ QPID_LOG( trace, "group queue " << qName <<
+ ": consumer name=" << state.owner << " released group id=" << gs->first);
+ disown(state);
+ } else if (reFreeNeeded) {
+ disown(state);
+ }
+ QPID_LOG( trace, "group queue " << qName <<
+ ": dequeued message from group id=" << group << " total=" << total );
+}
+
+bool MessageGroupManager::nextConsumableMessage( Consumer::shared_ptr& c, QueuedMessage& next )
+{
+ if (messages.empty())
+ return false;
+
+ if (!freeGroups.empty()) {
+ framing::SequenceNumber nextFree = freeGroups.begin()->first;
+ if (nextFree < c->position) { // next free group's msg is older than current position
+ bool ok = messages.find(nextFree, next);
+ (void) ok; assert( ok );
+ } else {
+ if (!messages.next( c->position, next ))
+ return false; // shouldn't happen - should find nextFree
+ }
+ } else { // no free groups available
+ if (!messages.next( c->position, next ))
+ return false;
+ }
+
+ do {
+ // @todo KAG avoid lookup: retrieve direct reference to group state from QueuedMessage
+ std::string group( getGroupId( next ) );
+ GroupMap::iterator gs = messageGroups.find( group );
+ assert( gs != messageGroups.end() );
+ GroupState& state( gs->second );
+ if (!state.owned() || state.owner == c->getName()) {
+ return true;
+ }
+ } while (messages.next( next.position, next ));
+ return false;
+}
+
+
+bool MessageGroupManager::allocate(const std::string& consumer, const QueuedMessage& qm)
+{
+ // @todo KAG avoid lookup: retrieve direct reference to group state from QueuedMessage
+ std::string group( getGroupId(qm) );
+ GroupMap::iterator gs = messageGroups.find( group );
+ assert( gs != messageGroups.end() );
+ GroupState& state( gs->second );
+
+ if (!state.owned()) {
+ own( state, consumer );
+ QPID_LOG( trace, "group queue " << qName <<
+ ": consumer name=" << consumer << " has acquired group id=" << gs->first);
+ return true;
+ }
+ return state.owner == consumer;
+}
+
+bool MessageGroupManager::nextBrowsableMessage( Consumer::shared_ptr& c, QueuedMessage& next )
+{
+ // browse: allow access to any available msg, regardless of group ownership (?ok?)
+ if (!messages.empty() && messages.next(c->position, next))
+ return true;
+ return false;
+}
+
+void MessageGroupManager::query(qpid::types::Variant::Map& status) const
+{
+ /** Add a description of the current state of the message groups for this queue.
+ FORMAT:
+ { "qpid.message_group_queue":
+ { "group_header_key" : "<KEY>",
+ "group_state" :
+ [ { "group_id" : "<name>",
+ "msg_count" : <int>,
+ "timestamp" : <absTime>,
+ "consumer" : <consumer name> },
+ {...} // one for each known group
+ ]
+ }
+ }
+ **/
+
+ assert(status.find(GROUP_QUERY_KEY) == status.end());
+ qpid::types::Variant::Map state;
+ qpid::types::Variant::List groups;
+
+ state[GROUP_HEADER_KEY] = groupIdHeader;
+ for (GroupMap::const_iterator g = messageGroups.begin();
+ g != messageGroups.end(); ++g) {
+ qpid::types::Variant::Map info;
+ info[GROUP_ID_KEY] = g->first;
+ info[GROUP_MSG_COUNT] = g->second.members.size();
+ info[GROUP_TIMESTAMP] = 0; /** @todo KAG - NEED HEAD MSG TIMESTAMP */
+ info[GROUP_CONSUMER] = g->second.owner;
+ groups.push_back(info);
+ }
+ state[GROUP_STATE_KEY] = groups;
+ status[GROUP_QUERY_KEY] = state;
+}
+
+
+boost::shared_ptr<MessageGroupManager> MessageGroupManager::create( const std::string& qName,
+ Messages& messages,
+ const qpid::framing::FieldTable& settings )
+{
+ boost::shared_ptr<MessageGroupManager> empty;
+
+ if (settings.isSet(qpidMessageGroupKey)) {
+
+ // @todo: remove once "sticky" consumers are supported - see QPID-3347
+ if (!settings.isSet(qpidSharedGroup)) {
+ QPID_LOG( error, "Only shared groups are supported in this version of the broker. Use '--shared-groups' in qpid-config." );
+ return empty;
+ }
+
+ std::string headerKey = settings.getAsString(qpidMessageGroupKey);
+ if (headerKey.empty()) {
+ QPID_LOG( error, "A Message Group header key must be configured, queue=" << qName);
+ return empty;
+ }
+ unsigned int timestamp = settings.getAsInt(qpidMessageGroupTimestamp);
+
+ boost::shared_ptr<MessageGroupManager> manager( new MessageGroupManager( headerKey, qName, messages, timestamp ) );
+
+ QPID_LOG( debug, "Configured Queue '" << qName <<
+ "' for message grouping using header key '" << headerKey << "'" <<
+ " (timestamp=" << timestamp << ")");
+ return manager;
+ }
+ return empty;
+}
+
+std::string MessageGroupManager::defaultGroupId;
+void MessageGroupManager::setDefaults(const std::string& groupId) // static
+{
+ defaultGroupId = groupId;
+}
+
+/** Cluster replication:
+
+ state map format:
+
+ { "group-state": [ {"name": <group-name>,
+ "owner": <consumer-name>-or-empty,
+ "acquired-ct": <acquired count>,
+ "positions": [Seqnumbers, ... ]},
+ {...}
+ ]
+ }
+*/
+
+namespace {
+ const std::string GROUP_NAME("name");
+ const std::string GROUP_OWNER("owner");
+ const std::string GROUP_ACQUIRED_CT("acquired-ct");
+ const std::string GROUP_POSITIONS("positions");
+ const std::string GROUP_STATE("group-state");
+}
+
+
+/** Runs on UPDATER to snapshot current state */
+void MessageGroupManager::getState(qpid::framing::FieldTable& state ) const
+{
+ using namespace qpid::framing;
+ state.clear();
+ framing::Array groupState(TYPE_CODE_MAP);
+ for (GroupMap::const_iterator g = messageGroups.begin();
+ g != messageGroups.end(); ++g) {
+
+ framing::FieldTable group;
+ group.setString(GROUP_NAME, g->first);
+ group.setString(GROUP_OWNER, g->second.owner);
+ group.setInt(GROUP_ACQUIRED_CT, g->second.acquired);
+ framing::Array positions(TYPE_CODE_UINT32);
+ for (GroupState::PositionFifo::const_iterator p = g->second.members.begin();
+ p != g->second.members.end(); ++p)
+ positions.push_back(framing::Array::ValuePtr(new IntegerValue( *p )));
+ group.setArray(GROUP_POSITIONS, positions);
+ groupState.push_back(framing::Array::ValuePtr(new FieldTableValue(group)));
+ }
+ state.setArray(GROUP_STATE, groupState);
+
+ QPID_LOG(debug, "Queue \"" << qName << "\": replicating message group state, key=" << groupIdHeader);
+}
+
+
+/** called on UPDATEE to set state from snapshot */
+void MessageGroupManager::setState(const qpid::framing::FieldTable& state)
+{
+ using namespace qpid::framing;
+ messageGroups.clear();
+ //consumers.clear();
+ freeGroups.clear();
+
+ framing::Array groupState(TYPE_CODE_MAP);
+
+ bool ok = state.getArray(GROUP_STATE, groupState);
+ if (!ok) {
+ QPID_LOG(error, "Unable to find message group state information for queue \"" <<
+ qName << "\": cluster inconsistency error!");
+ return;
+ }
+
+ for (framing::Array::const_iterator g = groupState.begin();
+ g != groupState.end(); ++g) {
+ framing::FieldTable group;
+ ok = framing::getEncodedValue<FieldTable>(*g, group);
+ if (!ok) {
+ QPID_LOG(error, "Invalid message group state information for queue \"" <<
+ qName << "\": table encoding error!");
+ return;
+ }
+ MessageGroupManager::GroupState state;
+ if (!group.isSet(GROUP_NAME) || !group.isSet(GROUP_OWNER) || !group.isSet(GROUP_ACQUIRED_CT)) {
+ QPID_LOG(error, "Invalid message group state information for queue \"" <<
+ qName << "\": fields missing error!");
+ return;
+ }
+ state.group = group.getAsString(GROUP_NAME);
+ state.owner = group.getAsString(GROUP_OWNER);
+ state.acquired = group.getAsInt(GROUP_ACQUIRED_CT);
+ framing::Array positions(TYPE_CODE_UINT32);
+ ok = group.getArray(GROUP_POSITIONS, positions);
+ if (!ok) {
+ QPID_LOG(error, "Invalid message group state information for queue \"" <<
+ qName << "\": position encoding error!");
+ return;
+ }
+
+ for (Array::const_iterator p = positions.begin(); p != positions.end(); ++p)
+ state.members.push_back((*p)->getIntegerValue<uint32_t, 4>());
+ messageGroups[state.group] = state;
+ if (!state.owned()) {
+ assert(state.members.size());
+ freeGroups[state.members.front()] = &messageGroups[state.group];
+ }
+ }
+
+ QPID_LOG(debug, "Queue \"" << qName << "\": message group state replicated, key =" << groupIdHeader)
+}
diff --git a/cpp/src/qpid/broker/MessageGroupManager.h b/cpp/src/qpid/broker/MessageGroupManager.h
new file mode 100644
index 0000000000..6c81ec14d1
--- /dev/null
+++ b/cpp/src/qpid/broker/MessageGroupManager.h
@@ -0,0 +1,107 @@
+#ifndef _broker_MessageGroupManager_h
+#define _broker_MessageGroupManager_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.
+ *
+ */
+
+/* for managing message grouping on Queues */
+
+#include "qpid/broker/StatefulQueueObserver.h"
+#include "qpid/broker/MessageDistributor.h"
+
+
+namespace qpid {
+namespace broker {
+
+class QueueObserver;
+class MessageDistributor;
+
+class MessageGroupManager : public StatefulQueueObserver, public MessageDistributor
+{
+ static std::string defaultGroupId; // assigned if no group id header present
+
+ const std::string groupIdHeader; // msg header holding group identifier
+ const unsigned int timestamp; // mark messages with timestamp if set
+ Messages& messages; // parent Queue's in memory message container
+ const std::string qName; // name of parent queue (for logs)
+
+ struct GroupState {
+ // note: update getState()/setState() when changing this object's state implementation
+ typedef std::deque<framing::SequenceNumber> PositionFifo;
+
+ std::string group; // group identifier
+ std::string owner; // consumer with outstanding acquired messages
+ uint32_t acquired; // count of outstanding acquired messages
+ PositionFifo members; // msgs belonging to this group
+
+ GroupState() : acquired(0) {}
+ bool owned() const {return !owner.empty();}
+ };
+ typedef std::map<std::string, struct GroupState> GroupMap;
+ typedef std::map<framing::SequenceNumber, struct GroupState *> GroupFifo;
+
+ GroupMap messageGroups; // index: group name
+ GroupFifo freeGroups; // ordered by oldest free msg
+ //Consumers consumers; // index: consumer name
+
+ static const std::string qpidMessageGroupKey;
+ static const std::string qpidSharedGroup; // if specified, one group can be consumed by multiple receivers
+ static const std::string qpidMessageGroupTimestamp;
+
+ const std::string getGroupId( const QueuedMessage& qm ) const;
+ void unFree( const GroupState& state );
+ void own( GroupState& state, const std::string& owner );
+ void disown( GroupState& state );
+
+ public:
+
+ static QPID_BROKER_EXTERN void setDefaults(const std::string& groupId);
+ static boost::shared_ptr<MessageGroupManager> create( const std::string& qName,
+ Messages& messages,
+ const qpid::framing::FieldTable& settings );
+
+ MessageGroupManager(const std::string& header, const std::string& _qName,
+ Messages& container, unsigned int _timestamp=0 )
+ : StatefulQueueObserver(std::string("MessageGroupManager:") + header),
+ groupIdHeader( header ), timestamp(_timestamp), messages(container), qName(_qName) {}
+
+ // QueueObserver iface
+ void enqueued( const QueuedMessage& qm );
+ void acquired( const QueuedMessage& qm );
+ void requeued( const QueuedMessage& qm );
+ void dequeued( const QueuedMessage& qm );
+ void consumerAdded( const Consumer& ) {};
+ void consumerRemoved( const Consumer& ) {};
+ void getState(qpid::framing::FieldTable& state ) const;
+ void setState(const qpid::framing::FieldTable&);
+
+ // MessageDistributor iface
+ bool nextConsumableMessage(Consumer::shared_ptr& c, QueuedMessage& next);
+ bool allocate(const std::string& c, const QueuedMessage& qm);
+ bool nextBrowsableMessage(Consumer::shared_ptr& c, QueuedMessage& next);
+ void query(qpid::types::Variant::Map&) const;
+
+ bool match(const qpid::types::Variant::Map*, const QueuedMessage&) const;
+};
+
+}}
+
+#endif
diff --git a/cpp/src/qpid/broker/Messages.h b/cpp/src/qpid/broker/Messages.h
index 0d75417640..448f17432a 100644
--- a/cpp/src/qpid/broker/Messages.h
+++ b/cpp/src/qpid/broker/Messages.h
@@ -32,7 +32,8 @@ struct QueuedMessage;
/**
* This interface abstracts out the access to the messages held for
- * delivery by a Queue instance.
+ * delivery by a Queue instance. Note the the assumption at present is
+ * that all locking is done in the Queue itself.
*/
class Messages
{
@@ -75,7 +76,6 @@ class Messages
* @return true if there is another message, false otherwise.
*/
virtual bool next(const framing::SequenceNumber&, QueuedMessage&) = 0;
-
/**
* Note: Caller is responsible for ensuring that there is a front
* (e.g. empty() returns false)
diff --git a/cpp/src/qpid/broker/NullMessageStore.cpp b/cpp/src/qpid/broker/NullMessageStore.cpp
index dc8615d58b..43f600eaf1 100644
--- a/cpp/src/qpid/broker/NullMessageStore.cpp
+++ b/cpp/src/qpid/broker/NullMessageStore.cpp
@@ -126,21 +126,25 @@ std::auto_ptr<TPCTransactionContext> NullMessageStore::begin(const std::string&
void NullMessageStore::prepare(TPCTransactionContext& ctxt)
{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(lock);
prepared.insert(DummyCtxt::getXid(ctxt));
}
void NullMessageStore::commit(TransactionContext& ctxt)
{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(lock);
prepared.erase(DummyCtxt::getXid(ctxt));
}
void NullMessageStore::abort(TransactionContext& ctxt)
{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(lock);
prepared.erase(DummyCtxt::getXid(ctxt));
}
void NullMessageStore::collectPreparedXids(std::set<std::string>& out)
{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(lock);
out.insert(prepared.begin(), prepared.end());
}
diff --git a/cpp/src/qpid/broker/NullMessageStore.h b/cpp/src/qpid/broker/NullMessageStore.h
index e148ec4d51..c6f402662e 100644
--- a/cpp/src/qpid/broker/NullMessageStore.h
+++ b/cpp/src/qpid/broker/NullMessageStore.h
@@ -25,6 +25,7 @@
#include "qpid/broker/BrokerImportExport.h"
#include "qpid/broker/MessageStore.h"
#include "qpid/broker/Queue.h"
+#include "qpid/sys/Mutex.h"
#include <boost/intrusive_ptr.hpp>
@@ -34,10 +35,11 @@ namespace broker {
/**
* A null implementation of the MessageStore interface
*/
-class NullMessageStore : public MessageStore
+class QPID_BROKER_CLASS_EXTERN NullMessageStore : public MessageStore
{
std::set<std::string> prepared;
uint64_t nextPersistenceId;
+ qpid::sys::Mutex lock;
public:
QPID_BROKER_EXTERN NullMessageStore();
diff --git a/cpp/src/qpid/broker/PersistableMessage.cpp b/cpp/src/qpid/broker/PersistableMessage.cpp
index e5fbb71cbd..7ba28eb293 100644
--- a/cpp/src/qpid/broker/PersistableMessage.cpp
+++ b/cpp/src/qpid/broker/PersistableMessage.cpp
@@ -34,7 +34,6 @@ class MessageStore;
PersistableMessage::~PersistableMessage() {}
PersistableMessage::PersistableMessage() :
- asyncEnqueueCounter(0),
asyncDequeueCounter(0),
store(0)
{}
@@ -68,24 +67,6 @@ bool PersistableMessage::isContentReleased() const
return contentReleaseState.released;
}
-bool PersistableMessage::isEnqueueComplete() {
- sys::ScopedLock<sys::Mutex> l(asyncEnqueueLock);
- return asyncEnqueueCounter == 0;
-}
-
-void PersistableMessage::enqueueComplete() {
- bool notify = false;
- {
- sys::ScopedLock<sys::Mutex> l(asyncEnqueueLock);
- if (asyncEnqueueCounter > 0) {
- if (--asyncEnqueueCounter == 0) {
- notify = true;
- }
- }
- }
- if (notify)
- allEnqueuesComplete();
-}
bool PersistableMessage::isStoredOnQueue(PersistableQueue::shared_ptr queue){
if (store && (queue->getPersistenceId()!=0)) {
@@ -109,12 +90,7 @@ void PersistableMessage::addToSyncList(PersistableQueue::shared_ptr queue, Messa
void PersistableMessage::enqueueAsync(PersistableQueue::shared_ptr queue, MessageStore* _store) {
addToSyncList(queue, _store);
- enqueueAsync();
-}
-
-void PersistableMessage::enqueueAsync() {
- sys::ScopedLock<sys::Mutex> l(asyncEnqueueLock);
- asyncEnqueueCounter++;
+ enqueueStart();
}
bool PersistableMessage::isDequeueComplete() {
diff --git a/cpp/src/qpid/broker/PersistableMessage.h b/cpp/src/qpid/broker/PersistableMessage.h
index 96fb922c1a..d29c2c45b4 100644
--- a/cpp/src/qpid/broker/PersistableMessage.h
+++ b/cpp/src/qpid/broker/PersistableMessage.h
@@ -31,6 +31,7 @@
#include "qpid/framing/amqp_types.h"
#include "qpid/sys/Mutex.h"
#include "qpid/broker/PersistableQueue.h"
+#include "qpid/broker/AsyncCompletion.h"
namespace qpid {
namespace broker {
@@ -43,18 +44,19 @@ class MessageStore;
class PersistableMessage : public Persistable
{
typedef std::list< boost::weak_ptr<PersistableQueue> > syncList;
- sys::Mutex asyncEnqueueLock;
sys::Mutex asyncDequeueLock;
sys::Mutex storeLock;
-
+
/**
- * Tracks the number of outstanding asynchronous enqueue
- * operations. When the message is enqueued asynchronously the
- * count is incremented; when that enqueue completes it is
- * decremented. Thus when it is 0, there are no outstanding
- * enqueues.
+ * "Ingress" messages == messages sent _to_ the broker.
+ * Tracks the number of outstanding asynchronous operations that must
+ * complete before an inbound message can be considered fully received by the
+ * broker. E.g. all enqueues have completed, the message has been written
+ * to store, credit has been replenished, etc. Once all outstanding
+ * operations have completed, the transfer of this message from the client
+ * may be considered complete.
*/
- int asyncEnqueueCounter;
+ AsyncCompletion ingressCompletion;
/**
* Tracks the number of outstanding asynchronous dequeue
@@ -65,7 +67,6 @@ class PersistableMessage : public Persistable
*/
int asyncDequeueCounter;
- void enqueueAsync();
void dequeueAsync();
syncList synclist;
@@ -80,8 +81,6 @@ class PersistableMessage : public Persistable
ContentReleaseState contentReleaseState;
protected:
- /** Called when all enqueues are complete for this message. */
- virtual void allEnqueuesComplete() = 0;
/** Called when all dequeues are complete for this message. */
virtual void allDequeuesComplete() = 0;
@@ -115,9 +114,12 @@ class PersistableMessage : public Persistable
virtual QPID_BROKER_EXTERN bool isPersistent() const = 0;
- QPID_BROKER_EXTERN bool isEnqueueComplete();
+ /** track the progress of a message received by the broker - see ingressCompletion above */
+ QPID_BROKER_INLINE_EXTERN bool isIngressComplete() { return ingressCompletion.isDone(); }
+ QPID_BROKER_INLINE_EXTERN AsyncCompletion& getIngressCompletion() { return ingressCompletion; }
- QPID_BROKER_EXTERN void enqueueComplete();
+ QPID_BROKER_INLINE_EXTERN void enqueueStart() { ingressCompletion.startCompleter(); }
+ QPID_BROKER_INLINE_EXTERN void enqueueComplete() { ingressCompletion.finishCompleter(); }
QPID_BROKER_EXTERN void enqueueAsync(PersistableQueue::shared_ptr queue,
MessageStore* _store);
@@ -133,7 +135,6 @@ class PersistableMessage : public Persistable
bool isStoredOnQueue(PersistableQueue::shared_ptr queue);
void addToSyncList(PersistableQueue::shared_ptr queue, MessageStore* _store);
-
};
}}
diff --git a/cpp/src/qpid/broker/Queue.cpp b/cpp/src/qpid/broker/Queue.cpp
index 27c1cc4ad7..4627b1409a 100644
--- a/cpp/src/qpid/broker/Queue.cpp
+++ b/cpp/src/qpid/broker/Queue.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
@@ -31,7 +31,10 @@
#include "qpid/broker/MessageStore.h"
#include "qpid/broker/NullMessageStore.h"
#include "qpid/broker/QueueRegistry.h"
+#include "qpid/broker/QueueFlowLimit.h"
#include "qpid/broker/ThresholdAlerts.h"
+#include "qpid/broker/FifoDistributor.h"
+#include "qpid/broker/MessageGroupManager.h"
#include "qpid/StringUtils.h"
#include "qpid/log/Statement.h"
@@ -41,6 +44,7 @@
#include "qpid/sys/ClusterSafe.h"
#include "qpid/sys/Monitor.h"
#include "qpid/sys/Time.h"
+#include "qpid/types/Variant.h"
#include "qmf/org/apache/qpid/broker/ArgsQueuePurge.h"
#include "qmf/org/apache/qpid/broker/ArgsQueueReroute.h"
@@ -64,7 +68,7 @@ using std::mem_fun;
namespace _qmf = qmf::org::apache::qpid::broker;
-namespace
+namespace
{
const std::string qpidMaxSize("qpid.max_size");
const std::string qpidMaxCount("qpid.max_count");
@@ -86,16 +90,16 @@ const int ENQUEUE_ONLY=1;
const int ENQUEUE_AND_DEQUEUE=2;
}
-Queue::Queue(const string& _name, bool _autodelete,
+Queue::Queue(const string& _name, bool _autodelete,
MessageStore* const _store,
const OwnershipToken* const _owner,
Manageable* parent,
Broker* b) :
- name(_name),
+ name(_name),
autodelete(_autodelete),
store(_store),
- owner(_owner),
+ owner(_owner),
consumerCount(0),
exclusive(0),
noLocal(false),
@@ -110,7 +114,8 @@ Queue::Queue(const string& _name, bool _autodelete,
broker(b),
deleted(false),
barrier(*this),
- autoDeleteTimeout(0)
+ autoDeleteTimeout(0),
+ allocator(new FifoDistributor( *messages ))
{
if (parent != 0 && broker != 0) {
ManagementAgent* agent = broker->getManagementAgent();
@@ -163,13 +168,8 @@ void Queue::deliver(boost::intrusive_ptr<Message> msg){
//drop message
QPID_LOG(info, "Dropping excluded message from " << getName());
} else {
- // if no store then mark as enqueued
- if (!enqueue(0, msg)){
- push(msg);
- msg->enqueueComplete();
- }else {
- push(msg);
- }
+ enqueue(0, msg);
+ push(msg);
QPID_LOG(debug, "Message " << msg << " enqueued on " << name);
}
}
@@ -183,11 +183,10 @@ void Queue::recover(boost::intrusive_ptr<Message>& msg){
if (policy.get()) policy->recoverEnqueued(msg);
push(msg, true);
- if (store){
+ if (store){
// setup synclist for recovered messages, so they don't get re-stored on lastNodeFailure
- msg->addToSyncList(shared_from_this(), store);
+ msg->addToSyncList(shared_from_this(), store);
}
- msg->enqueueComplete(); // mark the message as enqueued
if (store && (!msg->isContentLoaded() || msg->checkContentReleasable())) {
//content has not been loaded, need to ensure that lazy loading mode is set:
@@ -211,14 +210,13 @@ void Queue::process(boost::intrusive_ptr<Message>& msg){
void Queue::requeue(const QueuedMessage& msg){
assertClusterSafe();
QueueListeners::NotificationSet copy;
- {
+ {
Mutex::ScopedLock locker(messageLock);
if (!isEnqueued(msg)) return;
- msg.payload->enqueueComplete(); // mark the message as enqueued
messages->reinsert(msg);
listeners.populate(copy);
- // for persistLastNode - don't force a message twice to disk, but force it if no force before
+ // for persistLastNode - don't force a message twice to disk, but force it if no force before
if(inLastNodeFailure && persistLastNode && !msg.payload->isStoredOnQueue(shared_from_this())) {
msg.payload->forcePersistent();
if (msg.payload->isForcedPersistent() ){
@@ -226,16 +224,17 @@ void Queue::requeue(const QueuedMessage& msg){
enqueue(0, payload);
}
}
+ observeRequeue(msg, locker);
}
copy.notify();
}
-bool Queue::acquireMessageAt(const SequenceNumber& position, QueuedMessage& message)
+bool Queue::acquireMessageAt(const SequenceNumber& position, QueuedMessage& message)
{
Mutex::ScopedLock locker(messageLock);
assertClusterSafe();
QPID_LOG(debug, "Attempting to acquire message at " << position);
- if (messages->remove(position, message)) {
+ if (acquire(position, message, locker)) {
QPID_LOG(debug, "Acquired message at " << position << " from " << name);
return true;
} else {
@@ -244,9 +243,24 @@ bool Queue::acquireMessageAt(const SequenceNumber& position, QueuedMessage& mess
}
}
-bool Queue::acquire(const QueuedMessage& msg) {
- QueuedMessage copy = msg;
- return acquireMessageAt(msg.position, copy);
+bool Queue::acquire(const QueuedMessage& msg, const std::string& consumer)
+{
+ Mutex::ScopedLock locker(messageLock);
+ assertClusterSafe();
+ QPID_LOG(debug, consumer << " attempting to acquire message at " << msg.position);
+
+ if (!allocator->allocate( consumer, msg )) {
+ QPID_LOG(debug, "Not permitted to acquire msg at " << msg.position << " from '" << name);
+ return false;
+ }
+
+ QueuedMessage copy(msg);
+ if (acquire( msg.position, copy, locker)) {
+ QPID_LOG(debug, "Acquired message at " << msg.position << " from " << name);
+ return true;
+ }
+ QPID_LOG(debug, "Could not acquire message at " << msg.position << " from " << name << "; no message at that position");
+ return false;
}
void Queue::notifyListener()
@@ -262,7 +276,7 @@ void Queue::notifyListener()
set.notify();
}
-bool Queue::getNextMessage(QueuedMessage& m, Consumer::shared_ptr c)
+bool Queue::getNextMessage(QueuedMessage& m, Consumer::shared_ptr& c)
{
checkNotDeleted();
if (c->preAcquires()) {
@@ -274,52 +288,71 @@ bool Queue::getNextMessage(QueuedMessage& m, Consumer::shared_ptr c)
case NO_MESSAGES:
default:
return false;
- }
+ }
} else {
return browseNextMessage(m, c);
}
}
-Queue::ConsumeCode Queue::consumeNextMessage(QueuedMessage& m, Consumer::shared_ptr c)
+Queue::ConsumeCode Queue::consumeNextMessage(QueuedMessage& m, Consumer::shared_ptr& c)
{
while (true) {
Mutex::ScopedLock locker(messageLock);
- if (messages->empty()) {
- QPID_LOG(debug, "No messages to dispatch on queue '" << name << "'");
+ QueuedMessage msg;
+
+ if (!allocator->nextConsumableMessage(c, msg)) { // no next available
+ QPID_LOG(debug, "No messages available to dispatch to consumer " <<
+ c->getName() << " on queue '" << name << "'");
listeners.addListener(c);
return NO_MESSAGES;
- } else {
- QueuedMessage msg = messages->front();
- if (msg.payload->hasExpired()) {
- QPID_LOG(debug, "Message expired from queue '" << name << "'");
- popAndDequeue();
- continue;
- }
+ }
- if (c->filter(msg.payload)) {
- if (c->accept(msg.payload)) {
- m = msg;
- pop();
- return CONSUMED;
- } else {
- //message(s) are available but consumer hasn't got enough credit
- QPID_LOG(debug, "Consumer can't currently accept message from '" << name << "'");
- return CANT_CONSUME;
- }
+ if (msg.payload->hasExpired()) {
+ QPID_LOG(debug, "Message expired from queue '" << name << "'");
+ c->position = msg.position;
+ acquire( msg.position, msg, locker);
+ dequeue( 0, msg );
+ continue;
+ }
+
+ // a message is available for this consumer - can the consumer use it?
+
+ if (c->filter(msg.payload)) {
+ if (c->accept(msg.payload)) {
+ bool ok = allocator->allocate( c->getName(), msg ); // inform allocator
+ (void) ok; assert(ok);
+ ok = acquire( msg.position, msg, locker);
+ (void) ok; assert(ok);
+ m = msg;
+ c->position = m.position;
+ return CONSUMED;
} else {
- //consumer will never want this message
- QPID_LOG(debug, "Consumer doesn't want message from '" << name << "'");
+ //message(s) are available but consumer hasn't got enough credit
+ QPID_LOG(debug, "Consumer can't currently accept message from '" << name << "'");
return CANT_CONSUME;
- }
+ }
+ } else {
+ //consumer will never want this message
+ QPID_LOG(debug, "Consumer doesn't want message from '" << name << "'");
+ c->position = msg.position;
+ return CANT_CONSUME;
}
}
}
-
-bool Queue::browseNextMessage(QueuedMessage& m, Consumer::shared_ptr c)
+bool Queue::browseNextMessage(QueuedMessage& m, Consumer::shared_ptr& c)
{
- QueuedMessage msg(this);
- while (seek(msg, c)) {
+ while (true) {
+ Mutex::ScopedLock locker(messageLock);
+ QueuedMessage msg;
+
+ if (!allocator->nextBrowsableMessage(c, msg)) { // no next available
+ QPID_LOG(debug, "No browsable messages available for consumer " <<
+ c->getName() << " on queue '" << name << "'");
+ listeners.addListener(c);
+ return false;
+ }
+
if (c->filter(msg.payload) && !msg.payload->hasExpired()) {
if (c->accept(msg.payload)) {
//consumer wants the message
@@ -333,8 +366,8 @@ bool Queue::browseNextMessage(QueuedMessage& m, Consumer::shared_ptr c)
}
} else {
//consumer will never want this message, continue seeking
- c->position = msg.position;
QPID_LOG(debug, "Browser skipping message from '" << name << "'");
+ c->position = msg.position;
}
}
return false;
@@ -364,61 +397,71 @@ bool Queue::dispatch(Consumer::shared_ptr c)
}
}
-// Find the next message
-bool Queue::seek(QueuedMessage& msg, Consumer::shared_ptr c) {
- Mutex::ScopedLock locker(messageLock);
- if (messages->next(c->position, msg)) {
- return true;
- } else {
- listeners.addListener(c);
- return false;
- }
-}
-
-QueuedMessage Queue::find(SequenceNumber pos) const {
+bool Queue::find(SequenceNumber pos, QueuedMessage& msg) const {
Mutex::ScopedLock locker(messageLock);
- QueuedMessage msg;
- messages->find(pos, msg);
- return msg;
+ if (messages->find(pos, msg))
+ return true;
+ return false;
}
void Queue::consume(Consumer::shared_ptr c, bool requestExclusive){
assertClusterSafe();
- Mutex::ScopedLock locker(consumerLock);
- if(exclusive) {
- throw ResourceLockedException(
- QPID_MSG("Queue " << getName() << " has an exclusive consumer. No more consumers allowed."));
- } else if(requestExclusive) {
- if(consumerCount) {
+ {
+ Mutex::ScopedLock locker(consumerLock);
+ if(exclusive) {
throw ResourceLockedException(
- QPID_MSG("Queue " << getName() << " already has consumers. Exclusive access denied."));
- } else {
- exclusive = c->getSession();
+ QPID_MSG("Queue " << getName() << " has an exclusive consumer. No more consumers allowed."));
+ } else if(requestExclusive) {
+ if(consumerCount) {
+ throw ResourceLockedException(
+ QPID_MSG("Queue " << getName() << " already has consumers. Exclusive access denied."));
+ } else {
+ exclusive = c->getSession();
+ }
+ }
+ consumerCount++;
+ if (mgmtObject != 0)
+ mgmtObject->inc_consumerCount ();
+ //reset auto deletion timer if necessary
+ if (autoDeleteTimeout && autoDeleteTask) {
+ autoDeleteTask->cancel();
}
}
- consumerCount++;
- if (mgmtObject != 0)
- mgmtObject->inc_consumerCount ();
- //reset auto deletion timer if necessary
- if (autoDeleteTimeout && autoDeleteTask) {
- autoDeleteTask->cancel();
+ Mutex::ScopedLock locker(messageLock);
+ for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) {
+ try{
+ (*i)->consumerAdded(*c);
+ } catch (const std::exception& e) {
+ QPID_LOG(warning, "Exception on notification of new consumer for queue " << getName() << ": " << e.what());
+ }
}
}
void Queue::cancel(Consumer::shared_ptr c){
removeListener(c);
- Mutex::ScopedLock locker(consumerLock);
- consumerCount--;
- if(exclusive) exclusive = 0;
- if (mgmtObject != 0)
- mgmtObject->dec_consumerCount ();
+ {
+ Mutex::ScopedLock locker(consumerLock);
+ consumerCount--;
+ if(exclusive) exclusive = 0;
+ if (mgmtObject != 0)
+ mgmtObject->dec_consumerCount ();
+ }
+ Mutex::ScopedLock locker(messageLock);
+ for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) {
+ try{
+ (*i)->consumerRemoved(*c);
+ } catch (const std::exception& e) {
+ QPID_LOG(warning, "Exception on notification of removed consumer for queue " << getName() << ": " << e.what());
+ }
+ }
}
QueuedMessage Queue::get(){
Mutex::ScopedLock locker(messageLock);
QueuedMessage msg(this);
- messages->pop(msg);
+ if (messages->pop(msg))
+ observeAcquire(msg, locker);
return msg;
}
@@ -432,22 +475,135 @@ bool collect_if_expired(std::deque<QueuedMessage>& expired, QueuedMessage& messa
}
}
-void Queue::purgeExpired()
+/**
+ *@param lapse: time since the last purgeExpired
+ */
+void Queue::purgeExpired(qpid::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.
-
- if (dequeueTracker.sampleRatePerSecond() < 1) {
+ //attempt is less than one per second.
+ int count = dequeueSincePurge.get();
+ 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, expired, _1));
+ messages->removeIf(boost::bind(&collect_if_expired, boost::ref(expired), _1));
+ }
+
+ for (std::deque<QueuedMessage>::const_iterator i = expired.begin();
+ i != expired.end(); ++i) {
+ {
+ Mutex::ScopedLock locker(messageLock);
+ observeAcquire(*i, locker);
+ }
+ dequeue( 0, *i );
}
- for_each(expired.begin(), expired.end(), bind(&Queue::dequeue, this, (TransactionContext*) 0, _1));
}
}
+
+namespace {
+ // for use with purge/move below - collect messages that match a given filter
+ //
+ class MessageFilter
+ {
+ public:
+ static const std::string typeKey;
+ static const std::string paramsKey;
+ static MessageFilter *create( const ::qpid::types::Variant::Map *filter );
+ virtual bool match( const QueuedMessage& ) const { return true; }
+ virtual ~MessageFilter() {}
+ protected:
+ MessageFilter() {};
+ };
+ const std::string MessageFilter::typeKey("filter_type");
+ const std::string MessageFilter::paramsKey("filter_params");
+
+ // filter by message header string value exact match
+ class HeaderMatchFilter : public MessageFilter
+ {
+ public:
+ /* Config:
+ { 'filter_type' : 'header_match_str',
+ 'filter_params' : { 'header_key' : "<header name>",
+ 'header_value' : "<value to match>"
+ }
+ }
+ */
+ static const std::string typeKey;
+ static const std::string headerKey;
+ static const std::string valueKey;
+ HeaderMatchFilter( const std::string& _header, const std::string& _value )
+ : MessageFilter (), header(_header), value(_value) {}
+ bool match( const QueuedMessage& msg ) const
+ {
+ const qpid::framing::FieldTable* headers = msg.payload->getApplicationHeaders();
+ if (!headers) return false;
+ FieldTable::ValuePtr h = headers->get(header);
+ if (!h || !h->convertsTo<std::string>()) return false;
+ return h->get<std::string>() == value;
+ }
+ private:
+ const std::string header;
+ const std::string value;
+ };
+ const std::string HeaderMatchFilter::typeKey("header_match_str");
+ const std::string HeaderMatchFilter::headerKey("header_key");
+ const std::string HeaderMatchFilter::valueKey("header_value");
+
+ // factory to create correct filter based on map
+ MessageFilter* MessageFilter::create( const ::qpid::types::Variant::Map *filter )
+ {
+ using namespace qpid::types;
+ if (filter && !filter->empty()) {
+ Variant::Map::const_iterator i = filter->find(MessageFilter::typeKey);
+ if (i != filter->end()) {
+
+ if (i->second.asString() == HeaderMatchFilter::typeKey) {
+ Variant::Map::const_iterator p = filter->find(MessageFilter::paramsKey);
+ if (p != filter->end() && p->second.getType() == VAR_MAP) {
+ Variant::Map::const_iterator k = p->second.asMap().find(HeaderMatchFilter::headerKey);
+ Variant::Map::const_iterator v = p->second.asMap().find(HeaderMatchFilter::valueKey);
+ if (k != p->second.asMap().end() && v != p->second.asMap().end()) {
+ std::string headerKey(k->second.asString());
+ std::string value(v->second.asString());
+ QPID_LOG(debug, "Message filtering by header value configured. key: " << headerKey << " value: " << value );
+ return new HeaderMatchFilter( headerKey, value );
+ }
+ }
+ }
+ }
+ QPID_LOG(error, "Ignoring unrecognized message filter: '" << *filter << "'");
+ }
+ return new MessageFilter();
+ }
+
+ // used by removeIf() to collect all messages matching a filter, maximum match count is
+ // optional.
+ struct Collector {
+ const uint32_t maxMatches;
+ MessageFilter& filter;
+ std::deque<QueuedMessage> matches;
+ Collector(MessageFilter& filter, uint32_t max)
+ : maxMatches(max), filter(filter) {}
+ bool operator() (QueuedMessage& qm)
+ {
+ if (maxMatches == 0 || matches.size() < maxMatches) {
+ if (filter.match( qm )) {
+ matches.push_back(qm);
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
+} // end namespace
+
+
/**
* purge - for purging all or some messages on a queue
* depending on the purge_request
@@ -459,63 +615,77 @@ void Queue::purgeExpired()
* The dest exchange may be supplied to re-route messages through the exchange.
* It is safe to re-route messages such that they arrive back on the same queue,
* even if the queue is ordered by priority.
+ *
+ * An optional filter can be supplied that will be applied against each message. The
+ * message is purged only if the filter matches. See MessageDistributor for more detail.
*/
-uint32_t Queue::purge(const uint32_t purge_request, boost::shared_ptr<Exchange> dest)
+uint32_t Queue::purge(const uint32_t purge_request, boost::shared_ptr<Exchange> dest,
+ const qpid::types::Variant::Map *filter)
{
- Mutex::ScopedLock locker(messageLock);
- uint32_t purge_count = purge_request; // only comes into play if >0
- std::deque<DeliverableMessage> rerouteQueue;
+ std::auto_ptr<MessageFilter> mf(MessageFilter::create(filter));
+ Collector c(*mf.get(), purge_request);
- uint32_t count = 0;
- // Either purge them all or just the some (purge_count) while the queue isn't empty.
- while((!purge_request || purge_count--) && !messages->empty()) {
+ Mutex::ScopedLock locker(messageLock);
+ messages->removeIf( boost::bind<bool>(boost::ref(c), _1) );
+ for (std::deque<QueuedMessage>::iterator qmsg = c.matches.begin();
+ qmsg != c.matches.end(); ++qmsg) {
+ // Update observers and message state:
+ observeAcquire(*qmsg, locker);
+ dequeue(0, *qmsg);
+ // now reroute if necessary
if (dest.get()) {
- //
- // If there is a destination exchange, stage the messages onto a reroute queue
- // so they don't wind up getting purged more than once.
- //
- DeliverableMessage msg(messages->front().payload);
- rerouteQueue.push_back(msg);
+ assert(qmsg->payload);
+ DeliverableMessage dmsg(qmsg->payload);
+ dest->routeWithAlternate(dmsg);
}
- popAndDequeue();
- count++;
}
-
- //
- // Re-route purged messages into the destination exchange. Note that there's no need
- // to test dest.get() here because if it is NULL, the rerouteQueue will be empty.
- //
- while (!rerouteQueue.empty()) {
- DeliverableMessage msg(rerouteQueue.front());
- rerouteQueue.pop_front();
- dest->route(msg, msg.getMessage().getRoutingKey(),
- msg.getMessage().getApplicationHeaders());
- }
-
- return count;
+ return c.matches.size();
}
-uint32_t Queue::move(const Queue::shared_ptr destq, uint32_t qty) {
- Mutex::ScopedLock locker(messageLock);
- uint32_t move_count = qty; // only comes into play if qty >0
- uint32_t count = 0; // count how many were moved for returning
+uint32_t Queue::move(const Queue::shared_ptr destq, uint32_t qty,
+ const qpid::types::Variant::Map *filter)
+{
+ std::auto_ptr<MessageFilter> mf(MessageFilter::create(filter));
+ Collector c(*mf.get(), qty);
- while((!qty || move_count--) && !messages->empty()) {
- QueuedMessage qmsg = messages->front();
- boost::intrusive_ptr<Message> msg = qmsg.payload;
- destq->deliver(msg); // deliver message to the destination queue
- pop();
- dequeue(0, qmsg);
- count++;
+ Mutex::ScopedLock locker(messageLock);
+ messages->removeIf( boost::bind<bool>(boost::ref(c), _1) );
+
+ for (std::deque<QueuedMessage>::iterator qmsg = c.matches.begin();
+ qmsg != c.matches.end(); ++qmsg) {
+ // Update observers and message state:
+ observeAcquire(*qmsg, locker);
+ dequeue(0, *qmsg);
+ // and move to destination Queue.
+ assert(qmsg->payload);
+ destq->deliver(qmsg->payload);
}
- return count;
+ return c.matches.size();
}
-void Queue::pop()
+/** Acquire the front (oldest) message from the in-memory queue.
+ * assumes messageLock held by caller
+ */
+void Queue::pop(const Mutex::ScopedLock& locker)
{
assertClusterSafe();
- messages->pop();
- ++dequeueTracker;
+ QueuedMessage msg;
+ if (messages->pop(msg)) {
+ observeAcquire(msg, locker);
+ ++dequeueSincePurge;
+ }
+}
+
+/** Acquire the message at the given position, return true and msg if acquire succeeds */
+bool Queue::acquire(const qpid::framing::SequenceNumber& position, QueuedMessage& msg,
+ const Mutex::ScopedLock& locker)
+{
+ if (messages->remove(position, msg)) {
+ observeAcquire(msg, locker);
+ ++dequeueSincePurge;
+ return true;
+ }
+ return false;
}
void Queue::push(boost::intrusive_ptr<Message>& msg, bool isRecovery){
@@ -524,13 +694,15 @@ void Queue::push(boost::intrusive_ptr<Message>& msg, bool isRecovery){
QueuedMessage removed;
bool dequeueRequired = false;
{
- Mutex::ScopedLock locker(messageLock);
+ Mutex::ScopedLock locker(messageLock);
QueuedMessage qm(this, msg, ++sequence);
- if (insertSeqNo) msg->getOrInsertHeaders().setInt64(seqNoKey, sequence);
-
+ if (insertSeqNo) msg->insertCustomProperty(seqNoKey, sequence);
+
dequeueRequired = messages->push(qm, removed);
+ if (dequeueRequired)
+ observeAcquire(removed, locker);
listeners.populate(copy);
- enqueued(qm);
+ observeEnqueue(qm, locker);
}
copy.notify();
if (dequeueRequired) {
@@ -546,7 +718,7 @@ void Queue::push(boost::intrusive_ptr<Message>& msg, bool isRecovery){
void isEnqueueComplete(uint32_t* result, const QueuedMessage& message)
{
- if (message.payload->isEnqueueComplete()) (*result)++;
+ if (message.payload->isIngressComplete()) (*result)++;
}
/** function only provided for unit tests, or code not in critical message path */
@@ -606,7 +778,7 @@ void Queue::setLastNodeFailure()
}
-// return true if store exists,
+// return true if store exists,
bool Queue::enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message>& msg, bool suppressPolicyCheck)
{
ScopedUse u(barrier);
@@ -620,24 +792,21 @@ bool Queue::enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message>& msg
policy->getPendingDequeues(dequeues);
}
//depending on policy, may have some dequeues that need to performed without holding the lock
- for_each(dequeues.begin(), dequeues.end(), boost::bind(&Queue::dequeue, this, (TransactionContext*) 0, _1));
+ for_each(dequeues.begin(), dequeues.end(), boost::bind(&Queue::dequeue, this, (TransactionContext*) 0, _1));
}
if (inLastNodeFailure && persistLastNode){
msg->forcePersistent();
}
-
+
if (traceId.size()) {
- //copy on write: take deep copy of message before modifying it
- //as the frames may already be available for delivery on other
- //threads
- boost::intrusive_ptr<Message> copy(new Message(*msg));
- msg = copy;
msg->addTraceId(traceId);
}
if ((msg->isPersistent() || msg->checkContentReleasable()) && store) {
- msg->enqueueAsync(shared_from_this(), store); //increment to async counter -- for message sent to more than one queue
+ // mark the message as being enqueued - the store MUST CALL msg->enqueueComplete()
+ // when it considers the message stored.
+ msg->enqueueAsync(shared_from_this(), store);
boost::intrusive_ptr<PersistableMessage> pmsg = boost::static_pointer_cast<PersistableMessage>(msg);
store->enqueue(ctxt, pmsg, *this);
return true;
@@ -654,10 +823,10 @@ bool Queue::enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message>& msg
void Queue::enqueueAborted(boost::intrusive_ptr<Message> msg)
{
Mutex::ScopedLock locker(messageLock);
- if (policy.get()) policy->enqueueAborted(msg);
+ if (policy.get()) policy->enqueueAborted(msg);
}
-// return true if store exists,
+// return true if store exists,
bool Queue::dequeue(TransactionContext* ctxt, const QueuedMessage& msg)
{
ScopedUse u(barrier);
@@ -666,8 +835,8 @@ bool Queue::dequeue(TransactionContext* ctxt, const QueuedMessage& msg)
{
Mutex::ScopedLock locker(messageLock);
if (!isEnqueued(msg)) return false;
- if (!ctxt) {
- dequeued(msg);
+ if (!ctxt) {
+ observeDequeue(msg, locker);
}
}
// This check prevents messages which have been forced persistent on one queue from dequeuing
@@ -687,7 +856,7 @@ bool Queue::dequeue(TransactionContext* ctxt, const QueuedMessage& msg)
void Queue::dequeueCommitted(const QueuedMessage& msg)
{
Mutex::ScopedLock locker(messageLock);
- dequeued(msg);
+ observeDequeue(msg, locker);
if (mgmtObject != 0) {
mgmtObject->inc_msgTxnDequeues();
mgmtObject->inc_byteTxnDequeues(msg.payload->contentSize());
@@ -695,21 +864,23 @@ void Queue::dequeueCommitted(const QueuedMessage& msg)
}
/**
- * Removes a message from the in-memory delivery queue as well
- * dequeing it from the logical (and persistent if applicable) queue
+ * Removes the first (oldest) message from the in-memory delivery queue as well dequeing
+ * it from the logical (and persistent if applicable) queue
*/
-void Queue::popAndDequeue()
+void Queue::popAndDequeue(const Mutex::ScopedLock& held)
{
- QueuedMessage msg = messages->front();
- pop();
- dequeue(0, msg);
+ if (!messages->empty()) {
+ QueuedMessage msg = messages->front();
+ pop(held);
+ dequeue(0, msg);
+ }
}
/**
* Updates policy and management when a message has been dequeued,
* expects messageLock to be held
*/
-void Queue::dequeued(const QueuedMessage& msg)
+void Queue::observeDequeue(const QueuedMessage& msg, const Mutex::ScopedLock&)
{
if (policy.get()) policy->dequeued(msg);
mgntDeqStats(msg.payload);
@@ -722,6 +893,33 @@ void Queue::dequeued(const QueuedMessage& msg)
}
}
+/** updates queue observers when a message has become unavailable for transfer,
+ * expects messageLock to be held
+ */
+void Queue::observeAcquire(const QueuedMessage& msg, const Mutex::ScopedLock&)
+{
+ for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) {
+ try{
+ (*i)->acquired(msg);
+ } catch (const std::exception& e) {
+ QPID_LOG(warning, "Exception on notification of message removal for queue " << getName() << ": " << e.what());
+ }
+ }
+}
+
+/** updates queue observers when a message has become re-available for transfer,
+ * expects messageLock to be held
+ */
+void Queue::observeRequeue(const QueuedMessage& msg, const Mutex::ScopedLock&)
+{
+ for (Observers::const_iterator i = observers.begin(); i != observers.end(); ++i) {
+ try{
+ (*i)->requeued(msg);
+ } catch (const std::exception& e) {
+ QPID_LOG(warning, "Exception on notification of message requeue for queue " << getName() << ": " << e.what());
+ }
+ }
+}
void Queue::create(const FieldTable& _settings)
{
@@ -729,7 +927,7 @@ void Queue::create(const FieldTable& _settings)
if (store) {
store->create(*this, _settings);
}
- configure(_settings);
+ configureImpl(_settings);
}
@@ -742,8 +940,8 @@ int getIntegerSetting(const qpid::framing::FieldTable& settings, const std::stri
return v->get<int>();
} else if (v->convertsTo<std::string>()){
std::string s = v->get<std::string>();
- try {
- return boost::lexical_cast<int>(s);
+ try {
+ return boost::lexical_cast<int>(s);
} catch(const boost::bad_lexical_cast&) {
QPID_LOG(warning, "Ignoring invalid integer value for " << key << ": " << s);
return 0;
@@ -754,15 +952,45 @@ int getIntegerSetting(const qpid::framing::FieldTable& settings, const std::stri
}
}
-void Queue::configure(const FieldTable& _settings, bool recovering)
+bool getBoolSetting(const qpid::framing::FieldTable& settings, const std::string& key)
{
+ qpid::framing::FieldTable::ValuePtr v = settings.get(key);
+ if (!v) {
+ return false;
+ } else if (v->convertsTo<int>()) {
+ return v->get<int>() != 0;
+ } else if (v->convertsTo<std::string>()){
+ std::string s = v->get<std::string>();
+ if (s == "True") return true;
+ if (s == "true") return true;
+ if (s == "False") return false;
+ if (s == "false") return false;
+ try {
+ return boost::lexical_cast<bool>(s);
+ } catch(const boost::bad_lexical_cast&) {
+ QPID_LOG(warning, "Ignoring invalid boolean value for " << key << ": " << s);
+ return false;
+ }
+ } else {
+ QPID_LOG(warning, "Ignoring invalid boolean value for " << key << ": " << *v);
+ return false;
+ }
+}
+
+void Queue::configure(const FieldTable& _settings)
+{
+ settings = _settings;
+ configureImpl(settings);
+}
+void Queue::configureImpl(const FieldTable& _settings)
+{
eventMode = _settings.getAsInt(qpidQueueEventGeneration);
if (eventMode && broker) {
broker->getQueueEvents().observe(*this, eventMode == ENQUEUE_ONLY);
}
- if (QueuePolicy::getType(_settings) == QueuePolicy::FLOW_TO_DISK &&
+ if (QueuePolicy::getType(_settings) == QueuePolicy::FLOW_TO_DISK &&
(!store || NullMessageStore::isNullStore(store) || (broker && !(broker->getQueueEvents().isSync())) )) {
if ( NullMessageStore::isNullStore(store)) {
QPID_LOG(warning, "Flow to disk not valid for non-persisted queue:" << getName());
@@ -776,32 +1004,43 @@ void Queue::configure(const FieldTable& _settings, bool recovering)
setPolicy(QueuePolicy::createQueuePolicy(getName(), _settings));
}
if (broker && broker->getManagementAgent()) {
- ThresholdAlerts::observe(*this, *(broker->getManagementAgent()), _settings);
+ ThresholdAlerts::observe(*this, *(broker->getManagementAgent()), _settings, broker->getOptions().queueThresholdEventRatio);
}
//set this regardless of owner to allow use of no-local with exclusive consumers also
- noLocal = _settings.get(qpidNoLocal);
+ noLocal = getBoolSetting(_settings, qpidNoLocal);
QPID_LOG(debug, "Configured queue " << getName() << " with no-local=" << noLocal);
std::string lvqKey = _settings.getAsString(qpidLastValueQueueKey);
if (lvqKey.size()) {
QPID_LOG(debug, "Configured queue " << getName() << " as Last Value Queue with key " << lvqKey);
messages = std::auto_ptr<Messages>(new MessageMap(lvqKey));
- } else if (_settings.get(qpidLastValueQueueNoBrowse)) {
+ allocator = boost::shared_ptr<MessageDistributor>(new FifoDistributor( *messages ));
+ } else if (getBoolSetting(_settings, qpidLastValueQueueNoBrowse)) {
QPID_LOG(debug, "Configured queue " << getName() << " as Legacy Last Value Queue with 'no-browse' on");
messages = LegacyLVQ::updateOrReplace(messages, qpidVQMatchProperty, true, broker);
- } else if (_settings.get(qpidLastValueQueue)) {
+ allocator = boost::shared_ptr<MessageDistributor>(new FifoDistributor( *messages ));
+ } else if (getBoolSetting(_settings, qpidLastValueQueue)) {
QPID_LOG(debug, "Configured queue " << getName() << " as Legacy Last Value Queue");
messages = LegacyLVQ::updateOrReplace(messages, qpidVQMatchProperty, false, broker);
+ allocator = boost::shared_ptr<MessageDistributor>(new FifoDistributor( *messages ));
} else {
std::auto_ptr<Messages> m = Fairshare::create(_settings);
if (m.get()) {
messages = m;
+ allocator = boost::shared_ptr<MessageDistributor>(new FifoDistributor( *messages ));
QPID_LOG(debug, "Configured queue " << getName() << " as priority queue.");
+ } else { // default (FIFO) queue type
+ // override default message allocator if message groups configured.
+ boost::shared_ptr<MessageGroupManager> mgm(MessageGroupManager::create( getName(), *messages, _settings));
+ if (mgm) {
+ allocator = mgm;
+ addObserver(mgm);
+ }
}
}
-
- persistLastNode= _settings.get(qpidPersistLastNode);
+
+ persistLastNode = getBoolSetting(_settings, qpidPersistLastNode);
if (persistLastNode) QPID_LOG(debug, "Configured queue to Persist data if cluster fails to one node for: " << getName());
traceId = _settings.getAsString(qpidTraceIdentity);
@@ -809,32 +1048,32 @@ void Queue::configure(const FieldTable& _settings, bool recovering)
if (excludeList.size()) {
split(traceExclude, excludeList, ", ");
}
- QPID_LOG(debug, "Configured queue " << getName() << " with qpid.trace.id='" << traceId
+ QPID_LOG(debug, "Configured queue " << getName() << " with qpid.trace.id='" << traceId
<< "' and qpid.trace.exclude='"<< excludeList << "' i.e. " << traceExclude.size() << " elements");
FieldTable::ValuePtr p =_settings.get(qpidInsertSequenceNumbers);
if (p && p->convertsTo<std::string>()) insertSequenceNumbers(p->get<std::string>());
autoDeleteTimeout = getIntegerSetting(_settings, qpidAutoDeleteTimeout);
- if (autoDeleteTimeout)
- QPID_LOG(debug, "Configured queue " << getName() << " with qpid.auto_delete_timeout=" << autoDeleteTimeout);
+ if (autoDeleteTimeout)
+ QPID_LOG(debug, "Configured queue " << getName() << " with qpid.auto_delete_timeout=" << autoDeleteTimeout);
- if (mgmtObject != 0)
+ if (mgmtObject != 0) {
mgmtObject->set_arguments(ManagementAgent::toMap(_settings));
+ }
- if ( isDurable() && ! getPersistenceId() && ! recovering )
- store->create(*this, _settings);
+ QueueFlowLimit::observe(*this, _settings);
}
-void Queue::destroy()
+void Queue::destroyed()
{
+ unbind(broker->getExchanges());
if (alternateExchange.get()) {
Mutex::ScopedLock locker(messageLock);
while(!messages->empty()){
DeliverableMessage msg(messages->front().payload);
- alternateExchange->route(msg, msg.getMessage().getRoutingKey(),
- msg.getMessage().getApplicationHeaders());
- popAndDequeue();
+ alternateExchange->routeWithAlternate(msg);
+ popAndDequeue(locker);
}
alternateExchange->decAlternateUsers();
}
@@ -846,6 +1085,7 @@ void Queue::destroy()
store = 0;//ensure we make no more calls to the store for this queue
}
if (autoDeleteTask) autoDeleteTask = boost::intrusive_ptr<TimerTask>();
+ notifyDeleted();
}
void Queue::notifyDeleted()
@@ -865,9 +1105,9 @@ void Queue::bound(const string& exchange, const string& key,
bindings.add(exchange, key, args);
}
-void Queue::unbind(ExchangeRegistry& exchanges, Queue::shared_ptr shared_ref)
+void Queue::unbind(ExchangeRegistry& exchanges)
{
- bindings.unbind(exchanges, shared_ref);
+ bindings.unbind(exchanges, shared_from_this());
}
void Queue::setPolicy(std::auto_ptr<QueuePolicy> _policy)
@@ -880,9 +1120,9 @@ const QueuePolicy* Queue::getPolicy()
return policy.get();
}
-uint64_t Queue::getPersistenceId() const
-{
- return persistenceId;
+uint64_t Queue::getPersistenceId() const
+{
+ return persistenceId;
}
void Queue::setPersistenceId(uint64_t _persistenceId) const
@@ -896,11 +1136,11 @@ void Queue::setPersistenceId(uint64_t _persistenceId) const
persistenceId = _persistenceId;
}
-void Queue::encode(Buffer& buffer) const
+void Queue::encode(Buffer& buffer) const
{
buffer.putShortString(name);
buffer.put(settings);
- if (policy.get()) {
+ if (policy.get()) {
buffer.put(*policy);
}
buffer.putShortString(alternateExchange.get() ? alternateExchange->getName() : std::string(""));
@@ -914,13 +1154,14 @@ uint32_t Queue::encodedSize() const
+ (policy.get() ? (*policy).encodedSize() : 0);
}
-Queue::shared_ptr Queue::decode ( QueueRegistry& queues, Buffer& buffer, bool recovering )
+Queue::shared_ptr Queue::restore( QueueRegistry& queues, Buffer& buffer )
{
string name;
buffer.getShortString(name);
- std::pair<Queue::shared_ptr, bool> result = queues.declare(name, true);
- buffer.get(result.first->settings);
- result.first->configure(result.first->settings, recovering );
+ FieldTable settings;
+ buffer.get(settings);
+ boost::shared_ptr<Exchange> alternate;
+ std::pair<Queue::shared_ptr, bool> result = queues.declare(name, true, false, 0, alternate, settings, true);
if (result.first->policy.get() && buffer.available() >= result.first->policy->encodedSize()) {
buffer.get ( *(result.first->policy) );
}
@@ -952,11 +1193,10 @@ boost::shared_ptr<Exchange> Queue::getAlternateExchange()
void tryAutoDeleteImpl(Broker& broker, Queue::shared_ptr queue)
{
- if (broker.getQueues().destroyIf(queue->getName(),
+ if (broker.getQueues().destroyIf(queue->getName(),
boost::bind(boost::mem_fn(&Queue::canAutoDelete), queue))) {
QPID_LOG(debug, "Auto-deleting " << queue->getName());
- queue->unbind(broker.getExchanges(), queue);
- queue->destroy();
+ queue->destroyed();
}
}
@@ -965,7 +1205,7 @@ struct AutoDeleteTask : qpid::sys::TimerTask
Broker& broker;
Queue::shared_ptr queue;
- AutoDeleteTask(Broker& b, Queue::shared_ptr q, AbsTime fireTime)
+ AutoDeleteTask(Broker& b, Queue::shared_ptr q, AbsTime fireTime)
: qpid::sys::TimerTask(fireTime, "DelayedAutoDeletion"), broker(b), queue(q) {}
void fire()
@@ -983,27 +1223,27 @@ void Queue::tryAutoDelete(Broker& broker, Queue::shared_ptr queue)
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));
- broker.getClusterTimer().add(queue->autoDeleteTask);
+ broker.getClusterTimer().add(queue->autoDeleteTask);
QPID_LOG(debug, "Timed auto-delete for " << queue->getName() << " initiated");
} else {
tryAutoDeleteImpl(broker, queue);
}
}
-bool Queue::isExclusiveOwner(const OwnershipToken* const o) const
-{
+bool Queue::isExclusiveOwner(const OwnershipToken* const o) const
+{
Mutex::ScopedLock locker(ownershipLock);
- return o == owner;
+ return o == owner;
}
-void Queue::releaseExclusiveOwnership()
-{
+void Queue::releaseExclusiveOwnership()
+{
Mutex::ScopedLock locker(ownershipLock);
- owner = 0;
+ owner = 0;
}
-bool Queue::setExclusiveOwner(const OwnershipToken* const o)
-{
+bool Queue::setExclusiveOwner(const OwnershipToken* const o)
+{
//reset auto deletion timer if necessary
if (autoDeleteTimeout && autoDeleteTask) {
autoDeleteTask->cancel();
@@ -1012,25 +1252,25 @@ bool Queue::setExclusiveOwner(const OwnershipToken* const o)
if (owner) {
return false;
} else {
- owner = o;
+ owner = o;
return true;
}
}
-bool Queue::hasExclusiveOwner() const
-{
+bool Queue::hasExclusiveOwner() const
+{
Mutex::ScopedLock locker(ownershipLock);
- return owner != 0;
+ return owner != 0;
}
-bool Queue::hasExclusiveConsumer() const
-{
- return exclusive;
+bool Queue::hasExclusiveConsumer() const
+{
+ return exclusive;
}
void Queue::setExternalQueueStore(ExternalQueueStore* inst) {
- if (externalQueueStore!=inst && externalQueueStore)
- delete externalQueueStore;
+ if (externalQueueStore!=inst && externalQueueStore)
+ delete externalQueueStore;
externalQueueStore = inst;
if (inst) {
@@ -1055,7 +1295,7 @@ Manageable::status_t Queue::ManagementMethod (uint32_t methodId, Args& args, str
case _qmf::Queue::METHOD_PURGE :
{
_qmf::ArgsQueuePurge& purgeArgs = (_qmf::ArgsQueuePurge&) args;
- purge(purgeArgs.i_request);
+ purge(purgeArgs.i_request, boost::shared_ptr<Exchange>(), &purgeArgs.i_filter);
status = Manageable::STATUS_OK;
}
break;
@@ -1076,7 +1316,7 @@ Manageable::status_t Queue::ManagementMethod (uint32_t methodId, Args& args, str
}
}
- purge(rerouteArgs.i_request, dest);
+ purge(rerouteArgs.i_request, dest, &rerouteArgs.i_filter);
status = Manageable::STATUS_OK;
}
break;
@@ -1085,6 +1325,14 @@ Manageable::status_t Queue::ManagementMethod (uint32_t methodId, Args& args, str
return status;
}
+
+void Queue::query(qpid::types::Variant::Map& results) const
+{
+ Mutex::ScopedLock locker(messageLock);
+ /** @todo add any interesting queue state into results */
+ if (allocator) allocator->query(results);
+}
+
void Queue::setPosition(SequenceNumber n) {
Mutex::ScopedLock locker(messageLock);
sequence = n;
@@ -1119,7 +1367,10 @@ void Queue::insertSequenceNumbers(const std::string& key)
QPID_LOG(debug, "Inserting sequence numbers as " << key);
}
-void Queue::enqueued(const QueuedMessage& m)
+/** updates queue observers and state when a message has become available for transfer,
+ * expects messageLock to be held
+ */
+void Queue::observeEnqueue(const QueuedMessage& m, const Mutex::ScopedLock&)
{
for (Observers::iterator i = observers.begin(); i != observers.end(); ++i) {
try {
@@ -1142,7 +1393,8 @@ void Queue::updateEnqueued(const QueuedMessage& m)
if (policy.get()) {
policy->recoverEnqueued(payload);
}
- enqueued(m);
+ Mutex::ScopedLock locker(messageLock);
+ observeEnqueue(m, locker);
} else {
QPID_LOG(warning, "Queue informed of enqueued message that has no payload");
}
@@ -1166,6 +1418,7 @@ void Queue::checkNotDeleted()
void Queue::addObserver(boost::shared_ptr<QueueObserver> observer)
{
+ Mutex::ScopedLock locker(messageLock);
observers.insert(observer);
}
@@ -1175,6 +1428,32 @@ void Queue::flush()
if (u.acquired && store) store->flush(*this);
}
+
+bool Queue::bind(boost::shared_ptr<Exchange> exchange, const std::string& key,
+ const qpid::framing::FieldTable& arguments)
+{
+ if (exchange->bind(shared_from_this(), key, &arguments)) {
+ bound(exchange->getName(), key, arguments);
+ if (exchange->isDurable() && isDurable()) {
+ store->bind(*exchange, *this, key, arguments);
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+const Broker* Queue::getBroker()
+{
+ return broker;
+}
+
+void Queue::setDequeueSincePurge(uint32_t value) {
+ dequeueSincePurge = value;
+}
+
+
Queue::UsageBarrier::UsageBarrier(Queue& q) : parent(q), count(0) {}
bool Queue::UsageBarrier::acquire()
diff --git a/cpp/src/qpid/broker/Queue.h b/cpp/src/qpid/broker/Queue.h
index 12a3d273be..59ae41e768 100644
--- a/cpp/src/qpid/broker/Queue.h
+++ b/cpp/src/qpid/broker/Queue.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -32,9 +32,9 @@
#include "qpid/broker/QueueBindings.h"
#include "qpid/broker/QueueListeners.h"
#include "qpid/broker/QueueObserver.h"
-#include "qpid/broker/RateTracker.h"
#include "qpid/framing/FieldTable.h"
+#include "qpid/sys/AtomicValue.h"
#include "qpid/sys/Monitor.h"
#include "qpid/sys/Timer.h"
#include "qpid/management/Manageable.h"
@@ -59,7 +59,7 @@ class MessageStore;
class QueueEvents;
class QueueRegistry;
class TransactionContext;
-class Exchange;
+class MessageDistributor;
/**
* The brokers representation of an amqp queue. Messages are
@@ -74,13 +74,13 @@ class Queue : public boost::enable_shared_from_this<Queue>,
{
Queue& parent;
uint count;
-
+
UsageBarrier(Queue&);
bool acquire();
void release();
void destroy();
};
-
+
struct ScopedUse
{
UsageBarrier& barrier;
@@ -88,7 +88,7 @@ class Queue : public boost::enable_shared_from_this<Queue>,
ScopedUse(UsageBarrier& b) : barrier(b), acquired(barrier.acquire()) {}
~ScopedUse() { if (acquired) barrier.release(); }
};
-
+
typedef std::set< boost::shared_ptr<QueueObserver> > Observers;
enum ConsumeCode {NO_MESSAGES=0, CANT_CONSUME=1, CONSUMED=2};
@@ -119,7 +119,7 @@ class Queue : public boost::enable_shared_from_this<Queue>,
boost::shared_ptr<Exchange> alternateExchange;
framing::SequenceNumber sequence;
qmf::org::apache::qpid::broker::Queue* mgmtObject;
- RateTracker dequeueTracker;
+ sys::AtomicValue<uint32_t> dequeueSincePurge; // Count dequeues since last purge.
int eventMode;
Observers observers;
bool insertSeqNo;
@@ -129,26 +129,36 @@ class Queue : public boost::enable_shared_from_this<Queue>,
UsageBarrier barrier;
int autoDeleteTimeout;
boost::intrusive_ptr<qpid::sys::TimerTask> autoDeleteTask;
+ boost::shared_ptr<MessageDistributor> allocator;
void push(boost::intrusive_ptr<Message>& msg, bool isRecovery=false);
void setPolicy(std::auto_ptr<QueuePolicy> policy);
- bool seek(QueuedMessage& msg, Consumer::shared_ptr position);
- bool getNextMessage(QueuedMessage& msg, Consumer::shared_ptr c);
- ConsumeCode consumeNextMessage(QueuedMessage& msg, Consumer::shared_ptr c);
- bool browseNextMessage(QueuedMessage& msg, Consumer::shared_ptr c);
+ bool getNextMessage(QueuedMessage& msg, Consumer::shared_ptr& c);
+ ConsumeCode consumeNextMessage(QueuedMessage& msg, Consumer::shared_ptr& c);
+ bool browseNextMessage(QueuedMessage& msg, Consumer::shared_ptr& c);
void notifyListener();
void removeListener(Consumer::shared_ptr);
bool isExcluded(boost::intrusive_ptr<Message>& msg);
- void enqueued(const QueuedMessage& msg);
- void dequeued(const QueuedMessage& msg);
- void pop();
- void popAndDequeue();
- QueuedMessage getFront();
+ /** update queue observers, stats, policy, etc when the messages' state changes. Lock
+ * must be held by caller */
+ void observeEnqueue(const QueuedMessage& msg, const sys::Mutex::ScopedLock& lock);
+ void observeAcquire(const QueuedMessage& msg, const sys::Mutex::ScopedLock& lock);
+ void observeRequeue(const QueuedMessage& msg, const sys::Mutex::ScopedLock& lock);
+ void observeDequeue(const QueuedMessage& msg, const sys::Mutex::ScopedLock& lock);
+
+ /** modify the Queue's message container - assumes messageLock held */
+ void pop(const sys::Mutex::ScopedLock& held); // acquire front msg
+ void popAndDequeue(const sys::Mutex::ScopedLock& held); // acquire and dequeue front msg
+ // acquire message @ position, return true and set msg if acquire succeeds
+ bool acquire(const qpid::framing::SequenceNumber& position, QueuedMessage& msg,
+ const sys::Mutex::ScopedLock& held);
+
void forcePersistent(QueuedMessage& msg);
int getEventMode();
+ void configureImpl(const qpid::framing::FieldTable& settings);
inline void mgntEnqStats(const boost::intrusive_ptr<Message>& msg)
{
@@ -172,8 +182,9 @@ class Queue : public boost::enable_shared_from_this<Queue>,
}
}
}
-
+
void checkNotDeleted();
+ void notifyDeleted();
public:
@@ -182,29 +193,50 @@ class Queue : public boost::enable_shared_from_this<Queue>,
typedef std::vector<shared_ptr> vector;
QPID_BROKER_EXTERN Queue(const std::string& name,
- bool autodelete = false,
- MessageStore* const store = 0,
+ bool autodelete = false,
+ MessageStore* const store = 0,
const OwnershipToken* const owner = 0,
management::Manageable* parent = 0,
Broker* broker = 0);
QPID_BROKER_EXTERN ~Queue();
+ /** allow the Consumer to consume or browse the next available message */
QPID_BROKER_EXTERN bool dispatch(Consumer::shared_ptr);
- void create(const qpid::framing::FieldTable& settings);
+ /** allow the Consumer to acquire a message that it has browsed.
+ * @param msg - message to be acquired.
+ * @return false if message is no longer available for acquire.
+ */
+ QPID_BROKER_EXTERN bool acquire(const QueuedMessage& msg, const std::string& consumer);
- // "recovering" means we are doing a MessageStore recovery.
- QPID_BROKER_EXTERN void configure(const qpid::framing::FieldTable& settings,
- bool recovering = false);
- void destroy();
- void notifyDeleted();
+ /**
+ * Used to configure a new queue and create a persistent record
+ * for it in store if required.
+ */
+ QPID_BROKER_EXTERN void create(const qpid::framing::FieldTable& settings);
+
+ /**
+ * Used to reconfigure a recovered queue (does not create
+ * persistent record in store).
+ */
+ QPID_BROKER_EXTERN void configure(const qpid::framing::FieldTable& settings);
+ void destroyed();
QPID_BROKER_EXTERN void bound(const std::string& exchange,
const std::string& key,
const qpid::framing::FieldTable& args);
- QPID_BROKER_EXTERN void unbind(ExchangeRegistry& exchanges,
- Queue::shared_ptr shared_ref);
+ //TODO: get unbind out of the public interface; only there for purposes of one unit test
+ QPID_BROKER_EXTERN void unbind(ExchangeRegistry& exchanges);
+ /**
+ * Bind self to specified exchange, and record that binding for unbinding on delete.
+ */
+ bool bind(boost::shared_ptr<Exchange> exchange, const std::string& key,
+ const qpid::framing::FieldTable& arguments=qpid::framing::FieldTable());
- QPID_BROKER_EXTERN bool acquire(const QueuedMessage& msg);
+ /** Acquire the message at the given position if it is available for acquire. Not to
+ * be used by clients, but used by the broker for queue management.
+ * @param message - set to the acquired message if true returned.
+ * @return true if the message has been acquired.
+ */
QPID_BROKER_EXTERN bool acquireMessageAt(const qpid::framing::SequenceNumber& position, QueuedMessage& message);
/**
@@ -233,11 +265,14 @@ class Queue : public boost::enable_shared_from_this<Queue>,
bool exclusive = false);
QPID_BROKER_EXTERN void cancel(Consumer::shared_ptr c);
- uint32_t purge(const uint32_t purge_request=0, boost::shared_ptr<Exchange> dest=boost::shared_ptr<Exchange>()); //defaults to all messages
- QPID_BROKER_EXTERN void purgeExpired();
+ uint32_t purge(const uint32_t purge_request=0, //defaults to all messages
+ boost::shared_ptr<Exchange> dest=boost::shared_ptr<Exchange>(),
+ const ::qpid::types::Variant::Map *filter=0);
+ QPID_BROKER_EXTERN void purgeExpired(sys::Duration);
//move qty # of messages to destination Queue destq
- uint32_t move(const Queue::shared_ptr destq, uint32_t qty);
+ uint32_t move(const Queue::shared_ptr destq, uint32_t qty,
+ const qpid::types::Variant::Map *filter=0);
QPID_BROKER_EXTERN uint32_t getMessageCount() const;
QPID_BROKER_EXTERN uint32_t getEnqueueCompleteMessageCount() const;
@@ -276,8 +311,8 @@ class Queue : public boost::enable_shared_from_this<Queue>,
* Inform queue of messages that were enqueued, have since
* been acquired but not yet accepted or released (and
* thus are still logically on the queue) - used in
- * clustered broker.
- */
+ * clustered broker.
+ */
void updateEnqueued(const QueuedMessage& msg);
/**
@@ -288,14 +323,14 @@ class Queue : public boost::enable_shared_from_this<Queue>,
* accepted it).
*/
bool isEnqueued(const QueuedMessage& msg);
-
+
/**
- * Gets the next available message
+ * Acquires the next available (oldest) message
*/
QPID_BROKER_EXTERN QueuedMessage get();
- /** Get the message at position pos */
- QPID_BROKER_EXTERN QueuedMessage find(framing::SequenceNumber pos) const;
+ /** Get the message at position pos, returns true if found and sets msg */
+ QPID_BROKER_EXTERN bool find(framing::SequenceNumber pos, QueuedMessage& msg ) const;
const QueuePolicy* getPolicy();
@@ -309,8 +344,13 @@ class Queue : public boost::enable_shared_from_this<Queue>,
void encode(framing::Buffer& buffer) const;
uint32_t encodedSize() const;
- // "recovering" means we are doing a MessageStore recovery.
- static Queue::shared_ptr decode(QueueRegistry& queues, framing::Buffer& buffer, bool recovering = false );
+ /**
+ * Restores a queue from encoded data (used in recovery)
+ *
+ * Note: restored queue will be neither auto-deleted or have an
+ * exclusive owner
+ */
+ static Queue::shared_ptr restore(QueueRegistry& queues, framing::Buffer& buffer);
static void tryAutoDelete(Broker& broker, Queue::shared_ptr);
virtual void setExternalQueueStore(ExternalQueueStore* inst);
@@ -319,6 +359,7 @@ class Queue : public boost::enable_shared_from_this<Queue>,
management::ManagementObject* GetManagementObject (void) const;
management::Manageable::status_t
ManagementMethod (uint32_t methodId, management::Args& args, std::string& text);
+ void query(::qpid::types::Variant::Map&) const;
/** Apply f to each Message on the queue. */
template <class F> void eachMessage(F f) {
@@ -331,6 +372,11 @@ class Queue : public boost::enable_shared_from_this<Queue>,
bindings.eachBinding(f);
}
+ /** Apply f to each Observer on the queue */
+ template <class F> void eachObserver(F f) {
+ 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.
@@ -358,6 +404,11 @@ class Queue : public boost::enable_shared_from_this<Queue>,
void recoverPrepared(boost::intrusive_ptr<Message>& msg);
void flush();
+
+ const Broker* getBroker();
+
+ uint32_t getDequeueSincePurge() { return dequeueSincePurge.get(); }
+ void setDequeueSincePurge(uint32_t value);
};
}
}
diff --git a/cpp/src/qpid/broker/QueueCleaner.cpp b/cpp/src/qpid/broker/QueueCleaner.cpp
index 3499ea8a4d..838bc28be8 100644
--- a/cpp/src/qpid/broker/QueueCleaner.cpp
+++ b/cpp/src/qpid/broker/QueueCleaner.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,7 +27,7 @@
namespace qpid {
namespace broker {
-QueueCleaner::QueueCleaner(QueueRegistry& q, sys::Timer& t) : queues(q), timer(t) {}
+QueueCleaner::QueueCleaner(QueueRegistry& q, sys::Timer* t) : queues(q), timer(t) {}
QueueCleaner::~QueueCleaner()
{
@@ -36,10 +36,16 @@ QueueCleaner::~QueueCleaner()
void QueueCleaner::start(qpid::sys::Duration p)
{
+ period = p;
task = new Task(*this, p);
- timer.add(task);
+ timer->add(task);
}
+void QueueCleaner::setTimer(qpid::sys::Timer* timer) {
+ this->timer = timer;
+}
+
+
QueueCleaner::Task::Task(QueueCleaner& p, qpid::sys::Duration d) : sys::TimerTask(d,"QueueCleaner"), parent(p) {}
void QueueCleaner::Task::fire()
@@ -65,9 +71,9 @@ void QueueCleaner::fired()
std::vector<Queue::shared_ptr> copy;
CollectQueues collect(&copy);
queues.eachQueue(collect);
- std::for_each(copy.begin(), copy.end(), boost::bind(&Queue::purgeExpired, _1));
+ std::for_each(copy.begin(), copy.end(), boost::bind(&Queue::purgeExpired, _1, period));
task->setupNextFire();
- timer.add(task);
+ timer->add(task);
}
diff --git a/cpp/src/qpid/broker/QueueCleaner.h b/cpp/src/qpid/broker/QueueCleaner.h
index 11c2d180ac..ffebfe3e1b 100644
--- a/cpp/src/qpid/broker/QueueCleaner.h
+++ b/cpp/src/qpid/broker/QueueCleaner.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -35,14 +35,15 @@ class QueueRegistry;
class QueueCleaner
{
public:
- QPID_BROKER_EXTERN QueueCleaner(QueueRegistry& queues, sys::Timer& timer);
+ QPID_BROKER_EXTERN QueueCleaner(QueueRegistry& queues, sys::Timer* timer);
QPID_BROKER_EXTERN ~QueueCleaner();
- QPID_BROKER_EXTERN void start(qpid::sys::Duration period);
+ QPID_BROKER_EXTERN void start(sys::Duration period);
+ QPID_BROKER_EXTERN void setTimer(sys::Timer* timer);
private:
class Task : public sys::TimerTask
{
public:
- Task(QueueCleaner& parent, qpid::sys::Duration duration);
+ Task(QueueCleaner& parent, sys::Duration duration);
void fire();
private:
QueueCleaner& parent;
@@ -50,7 +51,8 @@ class QueueCleaner
boost::intrusive_ptr<sys::TimerTask> task;
QueueRegistry& queues;
- sys::Timer& timer;
+ sys::Timer* timer;
+ sys::Duration period;
void fired();
};
diff --git a/cpp/src/qpid/broker/QueueEvents.cpp b/cpp/src/qpid/broker/QueueEvents.cpp
index 2c540ff1ad..c66bdabf0f 100644
--- a/cpp/src/qpid/broker/QueueEvents.cpp
+++ b/cpp/src/qpid/broker/QueueEvents.cpp
@@ -129,6 +129,10 @@ class EventGenerator : public QueueObserver
{
if (!enqueueOnly) manager.dequeued(m);
}
+
+ void acquired(const QueuedMessage&) {};
+ void requeued(const QueuedMessage&) {};
+
private:
QueueEvents& manager;
const bool enqueueOnly;
diff --git a/cpp/src/qpid/broker/QueueFlowLimit.cpp b/cpp/src/qpid/broker/QueueFlowLimit.cpp
new file mode 100644
index 0000000000..f15bb45c01
--- /dev/null
+++ b/cpp/src/qpid/broker/QueueFlowLimit.cpp
@@ -0,0 +1,410 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/QueueFlowLimit.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/Queue.h"
+#include "qpid/Exception.h"
+#include "qpid/framing/FieldValue.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/broker/SessionState.h"
+#include "qpid/sys/ClusterSafe.h"
+
+#include "qmf/org/apache/qpid/broker/Queue.h"
+
+#include <sstream>
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+
+namespace {
+ /** ensure that the configured flow control stop and resume values are
+ * valid with respect to the maximum queue capacity, and each other
+ */
+ template <typename T>
+ void validateFlowConfig(T max, T& stop, T& resume, const std::string& type, const std::string& queue)
+ {
+ if (resume > stop) {
+ throw InvalidArgumentException(QPID_MSG("Queue \"" << queue << "\": qpid.flow_resume_" << type
+ << "=" << resume
+ << " must be less than qpid.flow_stop_" << type
+ << "=" << stop));
+ }
+ if (resume == 0) resume = stop;
+ if (max != 0 && (max < stop)) {
+ throw InvalidArgumentException(QPID_MSG("Queue \"" << queue << "\": qpid.flow_stop_" << type
+ << "=" << stop
+ << " must be less than qpid.max_" << type
+ << "=" << max));
+ }
+ }
+
+ /** extract a capacity value as passed in an argument map
+ */
+ uint64_t getCapacity(const FieldTable& settings, const std::string& key, uint64_t defaultValue)
+ {
+ FieldTable::ValuePtr v = settings.get(key);
+
+ int64_t result = 0;
+
+ if (!v) return defaultValue;
+ if (v->getType() == 0x23) {
+ QPID_LOG(debug, "Value for " << key << " specified as float: " << v->get<float>());
+ } else if (v->getType() == 0x33) {
+ QPID_LOG(debug, "Value for " << key << " specified as double: " << v->get<double>());
+ } else if (v->convertsTo<int64_t>()) {
+ 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>());
+ QPID_LOG(debug, "Got string value for " << key << ": " << s);
+ std::istringstream convert(s);
+ if (convert >> result && result >= 0) return result;
+ }
+
+ QPID_LOG(warning, "Cannot convert " << key << " to unsigned integer, using default (" << defaultValue << ")");
+ return defaultValue;
+ }
+}
+
+
+
+QueueFlowLimit::QueueFlowLimit(Queue *_queue,
+ uint32_t _flowStopCount, uint32_t _flowResumeCount,
+ uint64_t _flowStopSize, uint64_t _flowResumeSize)
+ : StatefulQueueObserver(std::string("QueueFlowLimit")), queue(_queue), queueName("<unknown>"),
+ flowStopCount(_flowStopCount), flowResumeCount(_flowResumeCount),
+ flowStopSize(_flowStopSize), flowResumeSize(_flowResumeSize),
+ flowStopped(false), count(0), size(0), queueMgmtObj(0), broker(0)
+{
+ uint32_t maxCount(0);
+ uint64_t maxSize(0);
+
+ if (queue) {
+ queueName = _queue->getName();
+ if (queue->getPolicy()) {
+ maxSize = _queue->getPolicy()->getMaxSize();
+ maxCount = _queue->getPolicy()->getMaxCount();
+ }
+ broker = queue->getBroker();
+ queueMgmtObj = dynamic_cast<_qmfBroker::Queue*> (queue->GetManagementObject());
+ if (queueMgmtObj) {
+ queueMgmtObj->set_flowStopped(isFlowControlActive());
+ }
+ }
+ validateFlowConfig( maxCount, flowStopCount, flowResumeCount, "count", queueName );
+ validateFlowConfig( maxSize, flowStopSize, flowResumeSize, "size", queueName );
+ QPID_LOG(info, "Queue \"" << queueName << "\": Flow limit created: flowStopCount=" << flowStopCount
+ << ", flowResumeCount=" << flowResumeCount
+ << ", flowStopSize=" << flowStopSize << ", flowResumeSize=" << flowResumeSize );
+}
+
+
+QueueFlowLimit::~QueueFlowLimit()
+{
+ sys::Mutex::ScopedLock l(indexLock);
+ if (!index.empty()) {
+ // we're gone - release all pending msgs
+ for (std::map<framing::SequenceNumber, boost::intrusive_ptr<Message> >::iterator itr = index.begin();
+ itr != index.end(); ++itr)
+ if (itr->second)
+ try {
+ itr->second->getIngressCompletion().finishCompleter();
+ } catch (...) {} // ignore - not safe for a destructor to throw.
+ index.clear();
+ }
+}
+
+
+void QueueFlowLimit::enqueued(const QueuedMessage& msg)
+{
+ sys::Mutex::ScopedLock l(indexLock);
+
+ ++count;
+ size += msg.payload->contentSize();
+
+ if (!flowStopped) {
+ if (flowStopCount && count > flowStopCount) {
+ flowStopped = true;
+ QPID_LOG(info, "Queue \"" << queueName << "\": has reached " << flowStopCount << " enqueued messages. Producer flow control activated." );
+ } else if (flowStopSize && size > flowStopSize) {
+ flowStopped = true;
+ QPID_LOG(info, "Queue \"" << queueName << "\": has reached " << flowStopSize << " enqueued bytes. Producer flow control activated." );
+ }
+ if (flowStopped && queueMgmtObj) {
+ queueMgmtObj->set_flowStopped(true);
+ queueMgmtObj->inc_flowStoppedCount();
+ }
+ }
+
+ if (flowStopped || !index.empty()) {
+ // ignore flow control if we are populating the queue due to cluster replication:
+ if (broker && broker->isClusterUpdatee()) {
+ QPID_LOG(trace, "Queue \"" << queueName << "\": ignoring flow control for msg pos=" << msg.position);
+ return;
+ }
+ QPID_LOG(trace, "Queue \"" << queueName << "\": setting flow control for msg pos=" << msg.position);
+ msg.payload->getIngressCompletion().startCompleter(); // don't complete until flow resumes
+ bool unique;
+ unique = index.insert(std::pair<framing::SequenceNumber, boost::intrusive_ptr<Message> >(msg.position, msg.payload)).second;
+ // Like this to avoid tripping up unused variable warning when NDEBUG set
+ if (!unique) assert(unique);
+ }
+}
+
+
+
+void QueueFlowLimit::dequeued(const QueuedMessage& msg)
+{
+ sys::Mutex::ScopedLock l(indexLock);
+
+ if (count > 0) {
+ --count;
+ } else {
+ throw Exception(QPID_MSG("Flow limit count underflow on dequeue. Queue=" << queueName));
+ }
+
+ uint64_t _size = msg.payload->contentSize();
+ if (_size <= size) {
+ size -= _size;
+ } else {
+ throw Exception(QPID_MSG("Flow limit size underflow on dequeue. Queue=" << queueName));
+ }
+
+ if (flowStopped &&
+ (flowResumeSize == 0 || size < flowResumeSize) &&
+ (flowResumeCount == 0 || count < flowResumeCount)) {
+ flowStopped = false;
+ if (queueMgmtObj)
+ queueMgmtObj->set_flowStopped(false);
+ QPID_LOG(info, "Queue \"" << queueName << "\": has drained below the flow control resume level. Producer flow control deactivated." );
+ }
+
+ if (!index.empty()) {
+ if (!flowStopped) {
+ // flow enabled - release all pending msgs
+ for (std::map<framing::SequenceNumber, boost::intrusive_ptr<Message> >::iterator itr = index.begin();
+ itr != index.end(); ++itr)
+ if (itr->second)
+ itr->second->getIngressCompletion().finishCompleter();
+ index.clear();
+ } else {
+ // even if flow controlled, we must release this msg as it is being dequeued
+ std::map<framing::SequenceNumber, boost::intrusive_ptr<Message> >::iterator itr = index.find(msg.position);
+ if (itr != index.end()) { // this msg is flow controlled, release it:
+ msg.payload->getIngressCompletion().finishCompleter();
+ index.erase(itr);
+ }
+ }
+ }
+}
+
+
+void QueueFlowLimit::encode(Buffer& buffer) const
+{
+ buffer.putLong(flowStopCount);
+ buffer.putLong(flowResumeCount);
+ buffer.putLongLong(flowStopSize);
+ buffer.putLongLong(flowResumeSize);
+ buffer.putLong(count);
+ buffer.putLongLong(size);
+}
+
+
+void QueueFlowLimit::decode ( Buffer& buffer )
+{
+ flowStopCount = buffer.getLong();
+ flowResumeCount = buffer.getLong();
+ flowStopSize = buffer.getLongLong();
+ flowResumeSize = buffer.getLongLong();
+ count = buffer.getLong();
+ size = buffer.getLongLong();
+}
+
+
+uint32_t QueueFlowLimit::encodedSize() const {
+ return sizeof(uint32_t) + // flowStopCount
+ sizeof(uint32_t) + // flowResumecount
+ sizeof(uint64_t) + // flowStopSize
+ sizeof(uint64_t) + // flowResumeSize
+ sizeof(uint32_t) + // count
+ sizeof(uint64_t); // size
+}
+
+
+const std::string QueueFlowLimit::flowStopCountKey("qpid.flow_stop_count");
+const std::string QueueFlowLimit::flowResumeCountKey("qpid.flow_resume_count");
+const std::string QueueFlowLimit::flowStopSizeKey("qpid.flow_stop_size");
+const std::string QueueFlowLimit::flowResumeSizeKey("qpid.flow_resume_size");
+uint64_t QueueFlowLimit::defaultMaxSize;
+uint QueueFlowLimit::defaultFlowStopRatio;
+uint QueueFlowLimit::defaultFlowResumeRatio;
+
+
+void QueueFlowLimit::setDefaults(uint64_t maxQueueSize, uint flowStopRatio, uint flowResumeRatio)
+{
+ defaultMaxSize = maxQueueSize;
+ defaultFlowStopRatio = flowStopRatio;
+ defaultFlowResumeRatio = flowResumeRatio;
+
+ /** @todo KAG: Verify valid range on Broker::Options instead of here */
+ if (flowStopRatio > 100 || flowResumeRatio > 100)
+ throw InvalidArgumentException(QPID_MSG("Default queue flow ratios must be between 0 and 100, inclusive:"
+ << " flowStopRatio=" << flowStopRatio
+ << " flowResumeRatio=" << flowResumeRatio));
+ if (flowResumeRatio > flowStopRatio)
+ throw InvalidArgumentException(QPID_MSG("Default queue flow stop ratio must be >= flow resume ratio:"
+ << " flowStopRatio=" << flowStopRatio
+ << " flowResumeRatio=" << flowResumeRatio));
+}
+
+
+void QueueFlowLimit::observe(Queue& queue, const qpid::framing::FieldTable& settings)
+{
+ QueueFlowLimit *ptr = createLimit( &queue, settings );
+ if (ptr) {
+ boost::shared_ptr<QueueFlowLimit> observer(ptr);
+ queue.addObserver(observer);
+ }
+}
+
+/** returns ptr to a QueueFlowLimit, else 0 if no limit */
+QueueFlowLimit *QueueFlowLimit::createLimit(Queue *queue, const qpid::framing::FieldTable& settings)
+{
+ std::string type(QueuePolicy::getType(settings));
+
+ if (type == QueuePolicy::RING || type == QueuePolicy::RING_STRICT) {
+ // The size of a RING queue is limited by design - no need for flow control.
+ return 0;
+ }
+
+ if (settings.get(flowStopCountKey) || settings.get(flowStopSizeKey) ||
+ settings.get(flowResumeCountKey) || settings.get(flowResumeSizeKey)) {
+ // user provided (some) flow settings manually...
+ uint32_t flowStopCount = getCapacity(settings, flowStopCountKey, 0);
+ uint32_t flowResumeCount = getCapacity(settings, flowResumeCountKey, 0);
+ uint64_t flowStopSize = getCapacity(settings, flowStopSizeKey, 0);
+ uint64_t flowResumeSize = getCapacity(settings, flowResumeSizeKey, 0);
+ if (flowStopCount == 0 && flowStopSize == 0) { // disable flow control
+ return 0;
+ }
+ return new QueueFlowLimit(queue, flowStopCount, flowResumeCount, flowStopSize, flowResumeSize);
+ }
+
+ if (defaultFlowStopRatio) { // broker has a default ratio setup...
+ uint64_t maxByteCount = getCapacity(settings, QueuePolicy::maxSizeKey, defaultMaxSize);
+ uint64_t flowStopSize = (uint64_t)(maxByteCount * (defaultFlowStopRatio/100.0) + 0.5);
+ uint64_t flowResumeSize = (uint64_t)(maxByteCount * (defaultFlowResumeRatio/100.0));
+ uint32_t maxMsgCount = getCapacity(settings, QueuePolicy::maxCountKey, 0); // no size by default
+ uint32_t flowStopCount = (uint32_t)(maxMsgCount * (defaultFlowStopRatio/100.0) + 0.5);
+ uint32_t flowResumeCount = (uint32_t)(maxMsgCount * (defaultFlowResumeRatio/100.0));
+
+ return new QueueFlowLimit(queue, flowStopCount, flowResumeCount, flowStopSize, flowResumeSize);
+ }
+ return 0;
+}
+
+/* Cluster replication */
+
+namespace {
+ /** pack a set of sequence number ranges into a framing::Array */
+ void buildSeqRangeArray(qpid::framing::Array *seqs,
+ const qpid::framing::SequenceNumber& first,
+ const qpid::framing::SequenceNumber& last)
+ {
+ seqs->push_back(qpid::framing::Array::ValuePtr(new Unsigned32Value(first)));
+ seqs->push_back(qpid::framing::Array::ValuePtr(new Unsigned32Value(last)));
+ }
+}
+
+/** Runs on UPDATER to snapshot current state */
+void QueueFlowLimit::getState(qpid::framing::FieldTable& state ) const
+{
+ sys::Mutex::ScopedLock l(indexLock);
+ state.clear();
+
+ framing::SequenceSet ss;
+ if (!index.empty()) {
+ /* replicate the set of messages pending flow control */
+ for (std::map<framing::SequenceNumber, boost::intrusive_ptr<Message> >::const_iterator itr = index.begin();
+ itr != index.end(); ++itr) {
+ ss.add(itr->first);
+ }
+ framing::Array seqs(TYPE_CODE_UINT32);
+ typedef boost::function<void(framing::SequenceNumber, framing::SequenceNumber)> arrayBuilder;
+ ss.for_each((arrayBuilder)boost::bind(&buildSeqRangeArray, &seqs, _1, _2));
+ state.setArray("pendingMsgSeqs", seqs);
+ }
+ QPID_LOG(debug, "Queue \"" << queueName << "\": flow limit replicating pending msgs, range=" << ss);
+}
+
+
+/** called on UPDATEE to set state from snapshot */
+void QueueFlowLimit::setState(const qpid::framing::FieldTable& state)
+{
+ sys::Mutex::ScopedLock l(indexLock);
+ index.clear();
+
+ framing::SequenceSet fcmsg;
+ framing::Array seqArray(TYPE_CODE_UINT32);
+ if (state.getArray("pendingMsgSeqs", seqArray)) {
+ assert((seqArray.count() & 0x01) == 0); // must be even since they are sequence ranges
+ framing::Array::const_iterator i = seqArray.begin();
+ while (i != seqArray.end()) {
+ framing::SequenceNumber first((*i)->getIntegerValue<uint32_t, 4>());
+ ++i;
+ framing::SequenceNumber last((*i)->getIntegerValue<uint32_t, 4>());
+ ++i;
+ fcmsg.add(first, last);
+ for (SequenceNumber seq = first; seq <= last; ++seq) {
+ QueuedMessage msg;
+ queue->find(seq, msg); // fyi: may not be found if msg is acquired & unacked
+ bool unique;
+ unique = index.insert(std::pair<framing::SequenceNumber, boost::intrusive_ptr<Message> >(seq, msg.payload)).second;
+ // Like this to avoid tripping up unused variable warning when NDEBUG set
+ if (!unique) assert(unique);
+ }
+ }
+ }
+
+ flowStopped = index.size() != 0;
+ if (queueMgmtObj) {
+ queueMgmtObj->set_flowStopped(isFlowControlActive());
+ }
+ QPID_LOG(debug, "Queue \"" << queueName << "\": flow limit replicated the pending msgs, range=" << fcmsg)
+}
+
+
+namespace qpid {
+ namespace broker {
+
+std::ostream& operator<<(std::ostream& out, const QueueFlowLimit& f)
+{
+ out << "; flowStopCount=" << f.flowStopCount << ", flowResumeCount=" << f.flowResumeCount;
+ out << "; flowStopSize=" << f.flowStopSize << ", flowResumeSize=" << f.flowResumeSize;
+ return out;
+}
+
+ }
+}
+
diff --git a/cpp/src/qpid/broker/QueueFlowLimit.h b/cpp/src/qpid/broker/QueueFlowLimit.h
new file mode 100644
index 0000000000..ad8a2720ef
--- /dev/null
+++ b/cpp/src/qpid/broker/QueueFlowLimit.h
@@ -0,0 +1,132 @@
+/*
+ *
+ * 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 _QueueFlowLimit_
+#define _QueueFlowLimit_
+
+#include <list>
+#include <set>
+#include <iostream>
+#include <memory>
+#include "qpid/broker/BrokerImportExport.h"
+#include "qpid/broker/QueuedMessage.h"
+#include "qpid/broker/StatefulQueueObserver.h"
+#include "qpid/framing/FieldTable.h"
+#include "qpid/sys/AtomicValue.h"
+#include "qpid/sys/Mutex.h"
+
+namespace qmf {
+namespace org {
+namespace apache {
+namespace qpid {
+namespace broker {
+ class Queue;
+}}}}}
+namespace _qmfBroker = qmf::org::apache::qpid::broker;
+
+namespace qpid {
+namespace broker {
+
+class Broker;
+
+/**
+ * Producer flow control: when level is > flowStop*, flow control is ON.
+ * then level is < flowResume*, flow control is OFF. If == 0, flow control
+ * is not used. If both byte and msg count thresholds are set, then
+ * passing _either_ level may turn flow control ON, but _both_ must be
+ * below level before flow control will be turned OFF.
+ */
+ class QueueFlowLimit : public StatefulQueueObserver
+{
+ static uint64_t defaultMaxSize;
+ static uint defaultFlowStopRatio;
+ static uint defaultFlowResumeRatio;
+
+ Queue *queue;
+ std::string queueName;
+
+ uint32_t flowStopCount;
+ uint32_t flowResumeCount;
+ uint64_t flowStopSize;
+ uint64_t flowResumeSize;
+ bool flowStopped; // true = producers held in flow control
+
+ // current queue utilization
+ uint32_t count;
+ uint64_t size;
+
+ public:
+ static QPID_BROKER_EXTERN const std::string flowStopCountKey;
+ static QPID_BROKER_EXTERN const std::string flowResumeCountKey;
+ static QPID_BROKER_EXTERN const std::string flowStopSizeKey;
+ static QPID_BROKER_EXTERN const std::string flowResumeSizeKey;
+
+ QPID_BROKER_EXTERN virtual ~QueueFlowLimit();
+
+ /** the queue has added QueuedMessage. Returns true if flow state changes */
+ QPID_BROKER_EXTERN void enqueued(const QueuedMessage&);
+ /** the queue has removed QueuedMessage. Returns true if flow state changes */
+ QPID_BROKER_EXTERN void dequeued(const QueuedMessage&);
+ /** ignored */
+ QPID_BROKER_EXTERN void acquired(const QueuedMessage&) {};
+ QPID_BROKER_EXTERN void requeued(const QueuedMessage&) {};
+
+ /** for clustering: */
+ QPID_BROKER_EXTERN void getState(qpid::framing::FieldTable&) const;
+ QPID_BROKER_EXTERN void setState(const qpid::framing::FieldTable&);
+
+ uint32_t getFlowStopCount() const { return flowStopCount; }
+ uint32_t getFlowResumeCount() const { return flowResumeCount; }
+ uint64_t getFlowStopSize() const { return flowStopSize; }
+ uint64_t getFlowResumeSize() const { return flowResumeSize; }
+
+ uint32_t getFlowCount() const { return count; }
+ uint64_t getFlowSize() const { return size; }
+ bool isFlowControlActive() const { return flowStopped; }
+ bool monitorFlowControl() const { return flowStopCount || flowStopSize; }
+
+ void encode(framing::Buffer& buffer) const;
+ void decode(framing::Buffer& buffer);
+ uint32_t encodedSize() const;
+
+ static QPID_BROKER_EXTERN void observe(Queue& queue, const qpid::framing::FieldTable& settings);
+ static QPID_BROKER_EXTERN void setDefaults(uint64_t defaultMaxSize, uint defaultFlowStopRatio, uint defaultFlowResumeRatio);
+
+ friend QPID_BROKER_EXTERN std::ostream& operator<<(std::ostream&, const QueueFlowLimit&);
+
+ protected:
+ // msgs waiting for flow to become available.
+ std::map<framing::SequenceNumber, boost::intrusive_ptr<Message> > index;
+ mutable qpid::sys::Mutex indexLock;
+
+ _qmfBroker::Queue *queueMgmtObj;
+
+ const Broker *broker;
+
+ QPID_BROKER_EXTERN QueueFlowLimit(Queue *queue,
+ uint32_t flowStopCount, uint32_t flowResumeCount,
+ uint64_t flowStopSize, uint64_t flowResumeSize);
+ static QPID_BROKER_EXTERN QueueFlowLimit *createLimit(Queue *queue, const qpid::framing::FieldTable& settings);
+};
+
+}}
+
+
+#endif
diff --git a/cpp/src/qpid/broker/QueueListeners.cpp b/cpp/src/qpid/broker/QueueListeners.cpp
index 4d2c57d6b4..591f4443bb 100644
--- a/cpp/src/qpid/broker/QueueListeners.cpp
+++ b/cpp/src/qpid/broker/QueueListeners.cpp
@@ -26,19 +26,25 @@ namespace broker {
void QueueListeners::addListener(Consumer::shared_ptr c)
{
- if (c->preAcquires()) {
- add(consumers, c);
- } else {
- add(browsers, c);
+ if (!c->inListeners) {
+ if (c->acquires) {
+ add(consumers, c);
+ } else {
+ add(browsers, c);
+ }
+ c->inListeners = true;
}
}
void QueueListeners::removeListener(Consumer::shared_ptr c)
{
- if (c->preAcquires()) {
- remove(consumers, c);
- } else {
- remove(browsers, c);
+ if (c->inListeners) {
+ if (c->acquires) {
+ remove(consumers, c);
+ } else {
+ remove(browsers, c);
+ }
+ c->inListeners = false;
}
}
@@ -46,18 +52,20 @@ void QueueListeners::populate(NotificationSet& set)
{
if (consumers.size()) {
set.consumer = consumers.front();
- consumers.erase(consumers.begin());
+ consumers.pop_front();
+ set.consumer->inListeners = false;
} else {
- // Don't swap the vectors, hang on to the memory allocated.
+ // Don't swap the deques, hang on to the memory allocated.
set.browsers = browsers;
browsers.clear();
+ for (Listeners::iterator i = set.browsers.begin(); i != set.browsers.end(); i++)
+ (*i)->inListeners = false;
}
}
void QueueListeners::add(Listeners& listeners, Consumer::shared_ptr c)
{
- Listeners::iterator i = std::find(listeners.begin(), listeners.end(), c);
- if (i == listeners.end()) listeners.push_back(c);
+ listeners.push_back(c);
}
void QueueListeners::remove(Listeners& listeners, Consumer::shared_ptr c)
@@ -73,9 +81,7 @@ void QueueListeners::NotificationSet::notify()
}
bool QueueListeners::contains(Consumer::shared_ptr c) const {
- return
- std::find(browsers.begin(), browsers.end(), c) != browsers.end() ||
- std::find(consumers.begin(), consumers.end(), c) != consumers.end();
+ return c->inListeners;
}
void QueueListeners::ListenerSet::notifyAll()
diff --git a/cpp/src/qpid/broker/QueueListeners.h b/cpp/src/qpid/broker/QueueListeners.h
index 59d1c84ca4..0659499253 100644
--- a/cpp/src/qpid/broker/QueueListeners.h
+++ b/cpp/src/qpid/broker/QueueListeners.h
@@ -22,7 +22,7 @@
*
*/
#include "qpid/broker/Consumer.h"
-#include <vector>
+#include <deque>
namespace qpid {
namespace broker {
@@ -40,7 +40,7 @@ namespace broker {
class QueueListeners
{
public:
- typedef std::vector<Consumer::shared_ptr> Listeners;
+ typedef std::deque<Consumer::shared_ptr> Listeners;
class NotificationSet
{
diff --git a/cpp/src/qpid/broker/QueueObserver.h b/cpp/src/qpid/broker/QueueObserver.h
index a711213dee..b58becd2ae 100644
--- a/cpp/src/qpid/broker/QueueObserver.h
+++ b/cpp/src/qpid/broker/QueueObserver.h
@@ -24,18 +24,52 @@
namespace qpid {
namespace broker {
-class QueuedMessage;
+struct QueuedMessage;
+class Consumer;
+
/**
- * Interface for notifying classes who want to act as 'observers' of a
- * queue of particular events.
+ * Interface for notifying classes who want to act as 'observers' of a queue of particular
+ * events.
+ *
+ * The events that are monitored reflect the relationship between a particular message and
+ * the queue it has been delivered to. A message can be considered in one of three states
+ * with respect to the queue:
+ *
+ * 1) "Available" - available for transfer to consumers (i.e. for browse or acquire),
+ *
+ * 2) "Acquired" - owned by a particular consumer, no longer available to other consumers
+ * (by either browse or acquire), but still considered on the queue.
+ *
+ * 3) "Dequeued" - removed from the queue and no longer available to any consumer.
+ *
+ * The queue events that are observable are:
+ *
+ * "Enqueued" - the message is "Available" - on the queue for transfer to any consumer
+ * (e.g. browse or acquire)
+ *
+ * "Acquired" - - a consumer has claimed exclusive access to it. It is no longer available
+ * for other consumers to browse or acquire, but it is not yet considered dequeued as it
+ * may be requeued by the consumer.
+ *
+ * "Requeued" - a previously-acquired message is released by its owner: it is put back on
+ * the queue at its original position and returns to the "Available" state.
+ *
+ * "Dequeued" - a message is no longer queued. At this point, the queue no longer tracks
+ * the message, and the broker considers the consumer's transaction complete.
*/
class QueueObserver
{
public:
virtual ~QueueObserver() {}
+
+ // note: the Queue will hold the messageLock while calling these methods!
virtual void enqueued(const QueuedMessage&) = 0;
virtual void dequeued(const QueuedMessage&) = 0;
- private:
+ virtual void acquired(const QueuedMessage&) = 0;
+ virtual void requeued(const QueuedMessage&) = 0;
+ virtual void consumerAdded( const Consumer& ) {};
+ virtual void consumerRemoved( const Consumer& ) {};
+ private:
};
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/QueuePolicy.cpp b/cpp/src/qpid/broker/QueuePolicy.cpp
index 4168221ad0..0c245700af 100644
--- a/cpp/src/qpid/broker/QueuePolicy.cpp
+++ b/cpp/src/qpid/broker/QueuePolicy.cpp
@@ -117,30 +117,30 @@ void QueuePolicy::update(FieldTable& settings)
settings.setString(typeKey, type);
}
-uint32_t QueuePolicy::getCapacity(const FieldTable& settings, const std::string& key, uint32_t defaultValue)
+template <typename T>
+T getCapacity(const FieldTable& settings, const std::string& key, T defaultValue)
{
FieldTable::ValuePtr v = settings.get(key);
- int32_t result = 0;
+ T result = 0;
if (!v) return defaultValue;
if (v->getType() == 0x23) {
QPID_LOG(debug, "Value for " << key << " specified as float: " << v->get<float>());
} else if (v->getType() == 0x33) {
QPID_LOG(debug, "Value for " << key << " specified as double: " << v->get<double>());
- } else if (v->convertsTo<int>()) {
- result = v->get<int>();
+ } else if (v->convertsTo<T>()) {
+ 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>());
QPID_LOG(debug, "Got string value for " << key << ": " << s);
std::istringstream convert(s);
- if (convert >> result && result >= 0) return result;
+ if (convert >> result && result >= 0 && convert.eof()) return result;
}
- QPID_LOG(warning, "Cannot convert " << key << " to unsigned integer, using default (" << defaultValue << ")");
- return defaultValue;
+ throw IllegalArgumentException(QPID_MSG("Cannot convert " << key << " to unsigned integer: " << *v));
}
std::string QueuePolicy::getType(const FieldTable& settings)
@@ -247,7 +247,7 @@ bool RingQueuePolicy::checkLimit(boost::intrusive_ptr<Message> m)
{
// If the message is bigger than the queue size, give up
- if (m->contentSize() > getMaxSize()) {
+ if (getMaxSize() && m->contentSize() > getMaxSize()) {
QPID_LOG(debug, "Message too large for ring queue " << name
<< " [" << *this << "] "
<< ": message size = " << m->contentSize() << " bytes"
@@ -269,8 +269,7 @@ bool RingQueuePolicy::checkLimit(boost::intrusive_ptr<Message> m)
do {
QueuedMessage oldest = queue.front();
-
- if (oldest.queue->acquire(oldest) || !strict) {
+ if (oldest.queue->acquireMessageAt(oldest.position, oldest) || !strict) {
queue.pop_front();
pendingDequeues.push_back(oldest);
QPID_LOG(debug, "Ring policy triggered in " << name
@@ -320,8 +319,8 @@ std::auto_ptr<QueuePolicy> QueuePolicy::createQueuePolicy(const qpid::framing::F
std::auto_ptr<QueuePolicy> QueuePolicy::createQueuePolicy(const std::string& name, const qpid::framing::FieldTable& settings)
{
- uint32_t maxCount = getCapacity(settings, maxCountKey, 0);
- uint32_t maxSize = getCapacity(settings, maxSizeKey, defaultMaxSize);
+ uint32_t maxCount = getCapacity<int32_t>(settings, maxCountKey, 0);
+ uint64_t maxSize = getCapacity<int64_t>(settings, maxSizeKey, defaultMaxSize);
if (maxCount || maxSize) {
return createQueuePolicy(name, maxCount, maxSize, getType(settings));
} else {
diff --git a/cpp/src/qpid/broker/QueuePolicy.h b/cpp/src/qpid/broker/QueuePolicy.h
index 3cdd63784d..ec7f846704 100644
--- a/cpp/src/qpid/broker/QueuePolicy.h
+++ b/cpp/src/qpid/broker/QueuePolicy.h
@@ -43,8 +43,7 @@ class QueuePolicy
uint32_t count;
uint64_t size;
bool policyExceeded;
-
- static uint32_t getCapacity(const qpid::framing::FieldTable& settings, const std::string& key, uint32_t defaultValue);
+
protected:
uint64_t getCurrentQueueSize() const { return size; }
diff --git a/cpp/src/qpid/broker/QueueRegistry.cpp b/cpp/src/qpid/broker/QueueRegistry.cpp
index ea2531dae7..135a3543d9 100644
--- a/cpp/src/qpid/broker/QueueRegistry.cpp
+++ b/cpp/src/qpid/broker/QueueRegistry.cpp
@@ -21,6 +21,7 @@
#include "qpid/broker/Queue.h"
#include "qpid/broker/QueueRegistry.h"
#include "qpid/broker/QueueEvents.h"
+#include "qpid/broker/Exchange.h"
#include "qpid/log/Statement.h"
#include <sstream>
#include <assert.h>
@@ -36,7 +37,13 @@ QueueRegistry::~QueueRegistry(){}
std::pair<Queue::shared_ptr, bool>
QueueRegistry::declare(const string& declareName, bool durable,
- bool autoDelete, const OwnershipToken* owner)
+ bool autoDelete, const OwnershipToken* owner,
+ boost::shared_ptr<Exchange> alternate,
+ const qpid::framing::FieldTable& arguments,
+ bool recovering/*true if this declare is a
+ result of recovering queue
+ definition from persistente
+ record*/)
{
RWlock::ScopedWlock locker(lock);
string name = declareName.empty() ? generateName() : declareName;
@@ -45,6 +52,17 @@ QueueRegistry::declare(const string& declareName, bool durable,
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);
+ } else {
+ //i.e. recovering a queue for which we already have a persistent record
+ queue->configure(arguments);
+ }
queues[name] = queue;
if (lastNode) queue->setLastNodeFailure();
diff --git a/cpp/src/qpid/broker/QueueRegistry.h b/cpp/src/qpid/broker/QueueRegistry.h
index 57859fe639..8a32a64f05 100644
--- a/cpp/src/qpid/broker/QueueRegistry.h
+++ b/cpp/src/qpid/broker/QueueRegistry.h
@@ -24,6 +24,7 @@
#include "qpid/broker/BrokerImportExport.h"
#include "qpid/sys/Mutex.h"
#include "qpid/management/Manageable.h"
+#include "qpid/framing/FieldTable.h"
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <algorithm>
@@ -34,6 +35,7 @@ namespace broker {
class Queue;
class QueueEvents;
+class Exchange;
class OwnershipToken;
class Broker;
class MessageStore;
@@ -60,7 +62,10 @@ class QueueRegistry {
const std::string& name,
bool durable = false,
bool autodelete = false,
- const OwnershipToken* owner = 0);
+ const OwnershipToken* owner = 0,
+ boost::shared_ptr<Exchange> alternateExchange = boost::shared_ptr<Exchange>(),
+ const qpid::framing::FieldTable& args = framing::FieldTable(),
+ bool recovering = false);
/**
* Destroy the named queue.
diff --git a/cpp/src/qpid/broker/RateTracker.cpp b/cpp/src/qpid/broker/RateTracker.cpp
deleted file mode 100644
index 048349b658..0000000000
--- a/cpp/src/qpid/broker/RateTracker.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-#include "qpid/broker/RateTracker.h"
-
-using qpid::sys::AbsTime;
-using qpid::sys::Duration;
-using qpid::sys::TIME_SEC;
-
-namespace qpid {
-namespace broker {
-
-RateTracker::RateTracker() : currentCount(0), lastCount(0), lastTime(AbsTime::now()) {}
-
-RateTracker& RateTracker::operator++()
-{
- ++currentCount;
- return *this;
-}
-
-double RateTracker::sampleRatePerSecond()
-{
- int32_t increment = currentCount - lastCount;
- AbsTime now = AbsTime::now();
- Duration interval(lastTime, now);
- lastCount = currentCount;
- lastTime = now;
- //if sampling at higher frequency than supported, will just return the number of increments
- if (interval < TIME_SEC) return increment;
- else if (increment == 0) return 0;
- else return increment / (interval / TIME_SEC);
-}
-
-}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/RateTracker.h b/cpp/src/qpid/broker/RateTracker.h
deleted file mode 100644
index 0c20b37312..0000000000
--- a/cpp/src/qpid/broker/RateTracker.h
+++ /dev/null
@@ -1,57 +0,0 @@
-#ifndef QPID_BROKER_RATETRACKER_H
-#define QPID_BROKER_RATETRACKER_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/Time.h"
-
-namespace qpid {
-namespace broker {
-
-/**
- * Simple rate tracker: represents some value that can be incremented,
- * then can periodcially sample the rate of increments.
- */
-class RateTracker
-{
- public:
- RateTracker();
- /**
- * Increments the count being tracked. Can be called concurrently
- * with other calls to this operator as well as with calls to
- * sampleRatePerSecond().
- */
- RateTracker& operator++();
- /**
- * Returns the rate of increments per second since last
- * called. Calls to this method should be serialised, but can be
- * called concurrently with the increment operator
- */
- double sampleRatePerSecond();
- private:
- volatile int32_t currentCount;
- int32_t lastCount;
- qpid::sys::AbsTime lastTime;
-};
-}} // namespace qpid::broker
-
-#endif /*!QPID_BROKER_RATETRACKER_H*/
diff --git a/cpp/src/qpid/broker/RecoveredDequeue.cpp b/cpp/src/qpid/broker/RecoveredDequeue.cpp
index 38cb8043c9..cd6735328f 100644
--- a/cpp/src/qpid/broker/RecoveredDequeue.cpp
+++ b/cpp/src/qpid/broker/RecoveredDequeue.cpp
@@ -43,7 +43,6 @@ void RecoveredDequeue::commit() throw()
void RecoveredDequeue::rollback() throw()
{
- msg->enqueueComplete();
queue->process(msg);
}
diff --git a/cpp/src/qpid/broker/RecoveredEnqueue.cpp b/cpp/src/qpid/broker/RecoveredEnqueue.cpp
index 6263c63e3d..6d2eaee6c4 100644
--- a/cpp/src/qpid/broker/RecoveredEnqueue.cpp
+++ b/cpp/src/qpid/broker/RecoveredEnqueue.cpp
@@ -36,7 +36,6 @@ bool RecoveredEnqueue::prepare(TransactionContext*) throw(){
}
void RecoveredEnqueue::commit() throw(){
- msg->enqueueComplete();
queue->process(msg);
}
diff --git a/cpp/src/qpid/broker/RecoveryManagerImpl.cpp b/cpp/src/qpid/broker/RecoveryManagerImpl.cpp
index 2f04943581..d08409695e 100644
--- a/cpp/src/qpid/broker/RecoveryManagerImpl.cpp
+++ b/cpp/src/qpid/broker/RecoveryManagerImpl.cpp
@@ -113,7 +113,7 @@ RecoverableExchange::shared_ptr RecoveryManagerImpl::recoverExchange(framing::Bu
RecoverableQueue::shared_ptr RecoveryManagerImpl::recoverQueue(framing::Buffer& buffer)
{
- Queue::shared_ptr queue = Queue::decode(queues, buffer, true);
+ Queue::shared_ptr queue = Queue::restore(queues, buffer);
try {
Exchange::shared_ptr exchange = exchanges.getDefault();
if (exchange) {
@@ -252,7 +252,6 @@ void RecoverableMessageImpl::dequeue(DtxBuffer::shared_ptr buffer, Queue::shared
void RecoverableMessageImpl::enqueue(DtxBuffer::shared_ptr buffer, Queue::shared_ptr queue)
{
- msg->enqueueComplete(); // recoved nmessage to enqueued in store already
buffer->enlist(TxOp::shared_ptr(new RecoveredEnqueue(queue, msg)));
}
diff --git a/cpp/src/qpid/broker/SaslAuthenticator.cpp b/cpp/src/qpid/broker/SaslAuthenticator.cpp
index acdb4934d4..d7adbd68ab 100644
--- a/cpp/src/qpid/broker/SaslAuthenticator.cpp
+++ b/cpp/src/qpid/broker/SaslAuthenticator.cpp
@@ -30,6 +30,7 @@
#include <boost/format.hpp>
#if HAVE_SASL
+#include <sys/stat.h>
#include <sasl/sasl.h>
#include "qpid/sys/cyrus/CyrusSecurityLayer.h"
using qpid::sys::cyrus::CyrusSecurityLayer;
@@ -57,7 +58,7 @@ public:
NullAuthenticator(Connection& connection, bool encrypt);
~NullAuthenticator();
void getMechanisms(framing::Array& mechanisms);
- void start(const std::string& mechanism, const std::string& response);
+ void start(const std::string& mechanism, const std::string* response);
void step(const std::string&) {}
std::auto_ptr<SecurityLayer> getSecurityLayer(uint16_t maxFrameSize);
};
@@ -81,7 +82,7 @@ public:
~CyrusAuthenticator();
void init();
void getMechanisms(framing::Array& mechanisms);
- void start(const std::string& mechanism, const std::string& response);
+ void start(const std::string& mechanism, const std::string* response);
void step(const std::string& response);
void getError(std::string& error);
void getUid(std::string& uid) { getUsername(uid); }
@@ -98,11 +99,33 @@ void SaslAuthenticator::init(const std::string& saslName, std::string const & sa
// Check if we have a version of SASL that supports sasl_set_path()
#if (SASL_VERSION_FULL >= ((2<<16)|(1<<8)|22))
// If we are not given a sasl path, do nothing and allow the default to be used.
- if ( ! saslConfigPath.empty() ) {
- int code = sasl_set_path(SASL_PATH_TYPE_CONFIG,
- const_cast<char *>(saslConfigPath.c_str()));
+ if ( saslConfigPath.empty() ) {
+ QPID_LOG ( info, "SASL: no config path set - using default." );
+ }
+ else {
+ struct stat st;
+
+ // Make sure the directory exists and we can read up to it.
+ if ( ::stat ( saslConfigPath.c_str(), & st) ) {
+ // Note: not using strerror() here because I think its messages are a little too hazy.
+ if ( errno == ENOENT )
+ throw Exception ( QPID_MSG ( "SASL: sasl_set_path failed: no such directory: " << saslConfigPath ) );
+ if ( errno == EACCES )
+ throw Exception ( QPID_MSG ( "SASL: sasl_set_path failed: cannot read parent of: " << saslConfigPath ) );
+ // catch-all stat failure
+ throw Exception ( QPID_MSG ( "SASL: sasl_set_path failed: cannot stat: " << saslConfigPath ) );
+ }
+
+ // Make sure the directory is readable.
+ if ( ::access ( saslConfigPath.c_str(), R_OK ) ) {
+ throw Exception ( QPID_MSG ( "SASL: sasl_set_path failed: directory not readable:" << saslConfigPath ) );
+ }
+
+ // This shouldn't fail now, but check anyway.
+ int code = sasl_set_path(SASL_PATH_TYPE_CONFIG, const_cast<char *>(saslConfigPath.c_str()));
if(SASL_OK != code)
throw Exception(QPID_MSG("SASL: sasl_set_path failed [" << code << "] " ));
+
QPID_LOG(info, "SASL: config path set to " << saslConfigPath );
}
#endif
@@ -164,7 +187,7 @@ void NullAuthenticator::getMechanisms(Array& mechanisms)
mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value("PLAIN")));//useful for testing
}
-void NullAuthenticator::start(const string& mechanism, const string& response)
+void NullAuthenticator::start(const string& mechanism, const string* response)
{
if (encrypt) {
#if HAVE_SASL
@@ -180,16 +203,16 @@ void NullAuthenticator::start(const string& mechanism, const string& response)
}
}
if (mechanism == "PLAIN") { // Old behavior
- if (response.size() > 0) {
+ if (response && response->size() > 0) {
string uid;
- string::size_type i = response.find((char)0);
- if (i == 0 && response.size() > 1) {
+ string::size_type i = response->find((char)0);
+ if (i == 0 && response->size() > 1) {
//no authorization id; use authentication id
- i = response.find((char)0, 1);
- if (i != string::npos) uid = response.substr(1, i-1);
+ i = response->find((char)0, 1);
+ if (i != string::npos) uid = response->substr(1, i-1);
} else if (i != string::npos) {
//authorization id is first null delimited field
- uid = response.substr(0, i);
+ uid = response->substr(0, i);
}//else not a valid SASL PLAIN response, throw error?
if (!uid.empty()) {
//append realm if it has not already been added
@@ -376,18 +399,22 @@ void CyrusAuthenticator::getMechanisms(Array& mechanisms)
}
}
-void CyrusAuthenticator::start(const string& mechanism, const string& response)
+void CyrusAuthenticator::start(const string& mechanism, const string* response)
{
const char *challenge;
unsigned int challenge_len;
- QPID_LOG(debug, "SASL: Starting authentication with mechanism: " << mechanism);
+ // 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.c_str(), response.length(),
+ (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 )
+ cnxMgmt->set_saslMechanism(mechanism);
}
void CyrusAuthenticator::step(const string& response)
@@ -424,10 +451,12 @@ void CyrusAuthenticator::processAuthenticationStep(int code, const char *challen
client.secure(challenge_str);
} else {
std::string uid;
+ //save error detail before trying to retrieve username as error in doing so will overwrite it
+ std::string errordetail = sasl_errdetail(sasl_conn);
if (!getUsername(uid)) {
- QPID_LOG(info, "SASL: Authentication failed (no username available):" << sasl_errdetail(sasl_conn));
+ QPID_LOG(info, "SASL: Authentication failed (no username available yet):" << errordetail);
} else {
- QPID_LOG(info, "SASL: Authentication failed for " << uid << ":" << sasl_errdetail(sasl_conn));
+ QPID_LOG(info, "SASL: Authentication failed for " << uid << ":" << errordetail);
}
// TODO: Change to more specific exceptions, when they are
@@ -459,6 +488,9 @@ std::auto_ptr<SecurityLayer> CyrusAuthenticator::getSecurityLayer(uint16_t maxFr
if (ssf) {
securityLayer = std::auto_ptr<SecurityLayer>(new CyrusSecurityLayer(sasl_conn, maxFrameSize));
}
+ qmf::org::apache::qpid::broker::Connection* cnxMgmt = connection.getMgmtObject();
+ 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 cfbe1a0cd1..4e5d43214c 100644
--- a/cpp/src/qpid/broker/SaslAuthenticator.h
+++ b/cpp/src/qpid/broker/SaslAuthenticator.h
@@ -41,7 +41,7 @@ class SaslAuthenticator
public:
virtual ~SaslAuthenticator() {}
virtual void getMechanisms(framing::Array& mechanisms) = 0;
- virtual void start(const std::string& mechanism, const std::string& response) = 0;
+ virtual void start(const std::string& mechanism, const std::string* response) = 0;
virtual void step(const std::string& response) = 0;
virtual void getUid(std::string&) {}
virtual bool getUsername(std::string&) { return false; };
diff --git a/cpp/src/qpid/broker/SemanticState.cpp b/cpp/src/qpid/broker/SemanticState.cpp
index c91cfba2f8..fbcb21eab9 100644
--- a/cpp/src/qpid/broker/SemanticState.cpp
+++ b/cpp/src/qpid/broker/SemanticState.cpp
@@ -70,14 +70,12 @@ SemanticState::SemanticState(DeliveryAdapter& da, SessionContext& ss)
deliveryAdapter(da),
tagGenerator("sgen"),
dtxSelected(false),
- authMsg(getSession().getBroker().getOptions().auth && !getSession().getConnection().isFederationLink()),
+ authMsg(getSession().getBroker().getOptions().auth && !getSession().getConnection().isUserProxyAuth()),
userID(getSession().getConnection().getUserId()),
userName(getSession().getConnection().getUserId().substr(0,getSession().getConnection().getUserId().find('@'))),
isDefaultRealm(userID.find('@') != std::string::npos && getSession().getBroker().getOptions().realm == userID.substr(userID.find('@')+1,userID.size())),
closeComplete(false)
-{
- acl = getSession().getBroker().getAcl();
-}
+{}
SemanticState::~SemanticState() {
closed();
@@ -88,7 +86,7 @@ void SemanticState::closed() {
//prevent requeued messages being redelivered to consumers
for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) {
disable(i->second);
- }
+ }
if (dtxBuffer.get()) {
dtxBuffer->fail();
}
@@ -107,16 +105,24 @@ bool SemanticState::exists(const string& consumerTag){
return consumers.find(consumerTag) != consumers.end();
}
-void SemanticState::consume(const string& tag,
+namespace {
+ const std::string SEPARATOR("::");
+}
+
+void SemanticState::consume(const string& tag,
Queue::shared_ptr queue, bool ackRequired, bool acquire,
bool exclusive, const string& resumeId, uint64_t resumeTtl, const FieldTable& arguments)
{
- ConsumerImpl::shared_ptr c(new ConsumerImpl(this, tag, queue, ackRequired, acquire, exclusive, resumeId, resumeTtl, arguments));
+ // "tag" is only guaranteed to be unique to this session (see AMQP 0-10 Message.subscribe, destination).
+ // Create a globally unique name so the broker can identify individual consumers
+ std::string name = session.getSessionId().str() + SEPARATOR + tag;
+ ConsumerImpl::shared_ptr c(new ConsumerImpl(this, name, queue, ackRequired, acquire, exclusive, tag, resumeId, resumeTtl, arguments));
queue->consume(c, exclusive);//may throw exception
consumers[tag] = c;
}
-void SemanticState::cancel(const string& tag){
+bool SemanticState::cancel(const string& tag)
+{
ConsumerImplMap::iterator i = consumers.find(tag);
if (i != consumers.end()) {
cancel(i->second);
@@ -124,7 +130,13 @@ void SemanticState::cancel(const string& tag){
//should cancel all unacked messages for this consumer so that
//they are not redelivered on recovery
for_each(unacked.begin(), unacked.end(), boost::bind(&DeliveryRecord::cancel, _1, tag));
-
+ //can also remove any records that are now redundant
+ DeliveryRecords::iterator removed =
+ remove_if(unacked.begin(), unacked.end(), bind(&DeliveryRecord::isRedundant, _1));
+ unacked.erase(removed, unacked.end());
+ return true;
+ } else {
+ return false;
}
}
@@ -167,8 +179,8 @@ void SemanticState::startDtx(const std::string& xid, DtxManager& mgr, bool join)
if (!dtxSelected) {
throw CommandInvalidException(QPID_MSG("Session has not been selected for use with dtx"));
}
- dtxBuffer = DtxBuffer::shared_ptr(new DtxBuffer(xid));
- txBuffer = boost::static_pointer_cast<TxBuffer>(dtxBuffer);
+ dtxBuffer.reset(new DtxBuffer(xid));
+ txBuffer = dtxBuffer;
if (join) {
mgr.join(xid, dtxBuffer);
} else {
@@ -194,7 +206,7 @@ void SemanticState::endDtx(const std::string& xid, bool fail)
dtxBuffer->fail();
} else {
dtxBuffer->markEnded();
- }
+ }
dtxBuffer.reset();
}
@@ -236,7 +248,7 @@ void SemanticState::resumeDtx(const std::string& xid)
checkDtxTimeout();
dtxBuffer->setSuspended(false);
- txBuffer = boost::static_pointer_cast<TxBuffer>(dtxBuffer);
+ txBuffer = dtxBuffer;
}
void SemanticState::checkDtxTimeout()
@@ -254,31 +266,33 @@ void SemanticState::record(const DeliveryRecord& delivery)
const std::string QPID_SYNC_FREQUENCY("qpid.sync_frequency");
-SemanticState::ConsumerImpl::ConsumerImpl(SemanticState* _parent,
- const string& _name,
- Queue::shared_ptr _queue,
+SemanticState::ConsumerImpl::ConsumerImpl(SemanticState* _parent,
+ const string& _name,
+ Queue::shared_ptr _queue,
bool ack,
bool _acquire,
bool _exclusive,
+ const string& _tag,
const string& _resumeId,
uint64_t _resumeTtl,
const framing::FieldTable& _arguments
-) :
- Consumer(_acquire),
- parent(_parent),
- name(_name),
- queue(_queue),
- ackExpected(ack),
+) :
+ Consumer(_name, _acquire),
+ parent(_parent),
+ queue(_queue),
+ ackExpected(ack),
acquire(_acquire),
- blocked(true),
+ blocked(true),
windowing(true),
+ windowActive(false),
exclusive(_exclusive),
resumeId(_resumeId),
+ tag(_tag),
resumeTtl(_resumeTtl),
arguments(_arguments),
- msgCredit(0),
+ msgCredit(0),
byteCredit(0),
notifyEnabled(true),
syncFrequency(_arguments.getAsInt(QPID_SYNC_FREQUENCY)),
@@ -289,10 +303,10 @@ SemanticState::ConsumerImpl::ConsumerImpl(SemanticState* _parent,
{
ManagementAgent* agent = parent->session.getBroker().getManagementAgent();
qpid::management::Manageable* ms = dynamic_cast<qpid::management::Manageable*> (&(parent->session));
-
+
if (agent != 0)
{
- mgmtObject = new _qmf::Subscription(agent, this, ms , queue->GetManagementObject()->getObjectId() ,name,
+ mgmtObject = new _qmf::Subscription(agent, this, ms , queue->GetManagementObject()->getObjectId(), getTag(),
!acquire, ackExpected, exclusive, ManagementAgent::toMap(arguments));
agent->addObject (mgmtObject);
mgmtObject->set_creditMode("WINDOW");
@@ -324,16 +338,16 @@ bool SemanticState::ConsumerImpl::deliver(QueuedMessage& msg)
{
assertClusterSafe();
allocateCredit(msg.payload);
- DeliveryRecord record(msg, queue, name, acquire, !ackExpected, windowing);
+ DeliveryRecord record(msg, queue, getTag(), acquire, !ackExpected, windowing);
bool sync = syncFrequency && ++deliveryCount >= syncFrequency;
if (sync) deliveryCount = 0;//reset
parent->deliver(record, sync);
- if (!ackExpected && acquire) record.setEnded();//allows message to be released now its been delivered
if (windowing || ackExpected || !acquire) {
parent->record(record);
- }
- if (acquire && !ackExpected) {
- queue->dequeue(0, msg);
+ }
+ if (acquire && !ackExpected) { // auto acquire && auto accept
+ queue->dequeue(0 /*ctxt*/, msg);
+ record.setEnded();
}
if (mgmtObject) { mgmtObject->inc_delivered(); }
return true;
@@ -351,7 +365,7 @@ bool SemanticState::ConsumerImpl::accept(intrusive_ptr<Message> msg)
// checkCredit fails because the message is to big, we should
// remain on queue's listener list for possible smaller messages
// in future.
- //
+ //
blocked = !(filter(msg) && checkCredit(msg));
return !blocked;
}
@@ -363,7 +377,7 @@ struct ConsumerName {
};
ostream& operator<<(ostream& o, const ConsumerName& pc) {
- return o << pc.consumer.getName() << " on "
+ return o << pc.consumer.getTag() << " on "
<< pc.consumer.getParent().getSession().getSessionId();
}
}
@@ -372,7 +386,7 @@ void SemanticState::ConsumerImpl::allocateCredit(intrusive_ptr<Message>& msg)
{
assertClusterSafe();
uint32_t originalMsgCredit = msgCredit;
- uint32_t originalByteCredit = byteCredit;
+ uint32_t originalByteCredit = byteCredit;
if (msgCredit != 0xFFFFFFFF) {
msgCredit--;
}
@@ -382,7 +396,7 @@ void SemanticState::ConsumerImpl::allocateCredit(intrusive_ptr<Message>& msg)
QPID_LOG(debug, "Credit allocated for " << ConsumerName(*this)
<< ", was " << " bytes: " << originalByteCredit << " msgs: " << originalMsgCredit
<< " now bytes: " << byteCredit << " msgs: " << msgCredit);
-
+
}
bool SemanticState::ConsumerImpl::checkCredit(intrusive_ptr<Message>& msg)
@@ -396,7 +410,7 @@ bool SemanticState::ConsumerImpl::checkCredit(intrusive_ptr<Message>& msg)
return enoughCredit;
}
-SemanticState::ConsumerImpl::~ConsumerImpl()
+SemanticState::ConsumerImpl::~ConsumerImpl()
{
if (mgmtObject != 0)
mgmtObject->resourceDestroy ();
@@ -414,7 +428,7 @@ void SemanticState::unsubscribe(ConsumerImpl::shared_ptr c)
Queue::shared_ptr queue = c->getQueue();
if(queue) {
queue->cancel(c);
- if (queue->canAutoDelete() && !queue->hasExclusiveOwner()) {
+ if (queue->canAutoDelete() && !queue->hasExclusiveOwner()) {
Queue::tryAutoDelete(session.getBroker(), queue);
}
}
@@ -456,23 +470,23 @@ const std::string nullstring;
}
void SemanticState::route(intrusive_ptr<Message> msg, Deliverable& strategy) {
- msg->setTimestamp(getSession().getBroker().getExpiryPolicy());
-
+ msg->computeExpiration(getSession().getBroker().getExpiryPolicy());
+
std::string exchangeName = msg->getExchangeName();
- if (!cacheExchange || cacheExchange->getName() != exchangeName)
+ if (!cacheExchange || cacheExchange->getName() != exchangeName || cacheExchange->isDestroyed())
cacheExchange = session.getBroker().getExchanges().get(exchangeName);
cacheExchange->setProperties(msg);
/* verify the userid if specified: */
std::string id =
msg->hasProperties<MessageProperties>() ? msg->getProperties<MessageProperties>()->getUserId() : nullstring;
-
if (authMsg && !id.empty() && !(id == userID || (isDefaultRealm && id == userName)))
{
QPID_LOG(debug, "authorised user id : " << userID << " but user id in message declared as " << id);
throw UnauthorizedAccessException(QPID_MSG("authorised user id : " << userID << " but user id in message declared as " << id));
}
+ AclModule* acl = getSession().getBroker().getAcl();
if (acl && acl->doTransferAcl())
{
if (!acl->authorise(getSession().getConnection().getUserId(),acl::ACT_PUBLISH,acl::OBJ_EXCHANGE,exchangeName, msg->getRoutingKey() ))
@@ -484,7 +498,7 @@ void SemanticState::route(intrusive_ptr<Message> msg, Deliverable& strategy) {
if (!strategy.delivered) {
//TODO:if discard-unroutable, just drop it
- //TODO:else if accept-mode is explicit, reject it
+ //TODO:else if accept-mode is explicit, reject it
//else route it to alternate exchange
if (cacheExchange->getAlternate()) {
cacheExchange->getAlternate()->route(strategy, msg->getRoutingKey(), msg->getApplicationHeaders());
@@ -513,7 +527,7 @@ void SemanticState::ConsumerImpl::requestDispatch()
}
bool SemanticState::complete(DeliveryRecord& delivery)
-{
+{
ConsumerImplMap::iterator i = consumers.find(delivery.getTag());
if (i != consumers.end()) {
i->second->complete(delivery);
@@ -525,7 +539,7 @@ void SemanticState::ConsumerImpl::complete(DeliveryRecord& delivery)
{
if (!delivery.isComplete()) {
delivery.complete();
- if (windowing) {
+ if (windowing && windowActive) {
if (msgCredit != 0xFFFFFFFF) msgCredit++;
if (byteCredit != 0xFFFFFFFF) byteCredit += delivery.getCredit();
}
@@ -541,7 +555,7 @@ void SemanticState::recover(bool requeue)
unacked.clear();
for_each(copy.rbegin(), copy.rend(), mem_fun_ref(&DeliveryRecord::requeue));
}else{
- for_each(unacked.begin(), unacked.end(), boost::bind(&DeliveryRecord::redeliver, _1, this));
+ for_each(unacked.begin(), unacked.end(), boost::bind(&DeliveryRecord::redeliver, _1, this));
//unconfirmed messages re redelivered and therefore have their
//id adjusted, confirmed messages are not and so the ordering
//w.r.t id is lost
@@ -554,50 +568,61 @@ void SemanticState::deliver(DeliveryRecord& msg, bool sync)
return deliveryAdapter.deliver(msg, sync);
}
-SemanticState::ConsumerImpl& SemanticState::find(const std::string& destination)
+const SemanticState::ConsumerImpl::shared_ptr SemanticState::find(const std::string& destination) const
{
- ConsumerImplMap::iterator i = consumers.find(destination);
- if (i == consumers.end()) {
- throw NotFoundException(QPID_MSG("Unknown destination " << destination));
+ ConsumerImpl::shared_ptr consumer;
+ if (!find(destination, consumer)) {
+ throw NotFoundException(QPID_MSG("Unknown destination " << destination << " session=" << session.getSessionId()));
} else {
- return *(i->second);
+ return consumer;
+ }
+}
+
+bool SemanticState::find(const std::string& destination, ConsumerImpl::shared_ptr& consumer) const
+{
+ // @todo KAG gsim: shouldn't the consumers map be locked????
+ ConsumerImplMap::const_iterator i = consumers.find(destination);
+ if (i == consumers.end()) {
+ return false;
}
+ consumer = i->second;
+ return true;
}
void SemanticState::setWindowMode(const std::string& destination)
{
- find(destination).setWindowMode();
+ find(destination)->setWindowMode();
}
void SemanticState::setCreditMode(const std::string& destination)
{
- find(destination).setCreditMode();
+ find(destination)->setCreditMode();
}
void SemanticState::addByteCredit(const std::string& destination, uint32_t value)
{
- ConsumerImpl& c = find(destination);
- c.addByteCredit(value);
- c.requestDispatch();
+ ConsumerImpl::shared_ptr c = find(destination);
+ c->addByteCredit(value);
+ c->requestDispatch();
}
void SemanticState::addMessageCredit(const std::string& destination, uint32_t value)
{
- ConsumerImpl& c = find(destination);
- c.addMessageCredit(value);
- c.requestDispatch();
+ ConsumerImpl::shared_ptr c = find(destination);
+ c->addMessageCredit(value);
+ c->requestDispatch();
}
void SemanticState::flush(const std::string& destination)
{
- find(destination).flush();
+ find(destination)->flush();
}
void SemanticState::stop(const std::string& destination)
{
- find(destination).stop();
+ find(destination)->stop();
}
void SemanticState::ConsumerImpl::setWindowMode()
@@ -621,6 +646,7 @@ void SemanticState::ConsumerImpl::setCreditMode()
void SemanticState::ConsumerImpl::addByteCredit(uint32_t value)
{
assertClusterSafe();
+ if (windowing) windowActive = true;
if (byteCredit != 0xFFFFFFFF) {
if (value == 0xFFFFFFFF) byteCredit = value;
else byteCredit += value;
@@ -630,6 +656,7 @@ void SemanticState::ConsumerImpl::addByteCredit(uint32_t value)
void SemanticState::ConsumerImpl::addMessageCredit(uint32_t value)
{
assertClusterSafe();
+ if (windowing) windowActive = true;
if (msgCredit != 0xFFFFFFFF) {
if (value == 0xFFFFFFFF) msgCredit = value;
else msgCredit += value;
@@ -650,7 +677,8 @@ void SemanticState::ConsumerImpl::flush()
{
while(haveCredit() && queue->dispatch(shared_from_this()))
;
- stop();
+ msgCredit = 0;
+ byteCredit = 0;
}
void SemanticState::ConsumerImpl::stop()
@@ -658,6 +686,7 @@ void SemanticState::ConsumerImpl::stop()
assertClusterSafe();
msgCredit = 0;
byteCredit = 0;
+ windowActive = false;
}
Queue::shared_ptr SemanticState::getQueue(const string& name) const {
@@ -673,7 +702,7 @@ Queue::shared_ptr SemanticState::getQueue(const string& name) const {
}
AckRange SemanticState::findRange(DeliveryId first, DeliveryId last)
-{
+{
return DeliveryRecord::findRange(unacked, first, last);
}
@@ -691,14 +720,21 @@ void SemanticState::release(DeliveryId first, DeliveryId last, bool setRedeliver
DeliveryRecords::reverse_iterator start(range.end);
DeliveryRecords::reverse_iterator end(range.start);
for_each(start, end, boost::bind(&DeliveryRecord::release, _1, setRedelivered));
+
+ DeliveryRecords::iterator removed =
+ remove_if(range.start, range.end, bind(&DeliveryRecord::isRedundant, _1));
+ unacked.erase(removed, range.end);
}
void SemanticState::reject(DeliveryId first, DeliveryId last)
{
AckRange range = findRange(first, last);
for_each(range.start, range.end, mem_fun_ref(&DeliveryRecord::reject));
- //need to remove the delivery records as well
- unacked.erase(range.start, range.end);
+ //may need to remove the delivery records as well
+ for (DeliveryRecords::iterator i = range.start; i != unacked.end() && i->getId() <= last; ) {
+ if (i->isRedundant()) i = unacked.erase(i);
+ else i++;
+ }
}
bool SemanticState::ConsumerImpl::doOutput()
@@ -761,13 +797,13 @@ void SemanticState::accepted(const SequenceSet& commands) {
//in transactional mode, don't dequeue or remove, just
//maintain set of acknowledged messages:
accumulatedAck.add(commands);
-
+
if (dtxBuffer.get()) {
//if enlisted in a dtx, copy the relevant slice from
//unacked and record it against that transaction
TxOp::shared_ptr txAck(new DtxAck(accumulatedAck, unacked));
accumulatedAck.clear();
- dtxBuffer->enlist(txAck);
+ dtxBuffer->enlist(txAck);
//mark the relevant messages as 'ended' in unacked
//if the messages are already completed, they can be
@@ -789,7 +825,6 @@ void SemanticState::accepted(const SequenceSet& commands) {
}
void SemanticState::completed(const SequenceSet& commands) {
- assertClusterSafe();
DeliveryRecords::iterator removed =
remove_if(unacked.begin(), unacked.end(),
isInSequenceSetAnd(commands,
@@ -800,7 +835,6 @@ void SemanticState::completed(const SequenceSet& commands) {
void SemanticState::attached()
{
- assertClusterSafe();
for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) {
i->second->enableNotify();
session.getConnection().outputTasks.addOutputTask(i->second.get());
@@ -810,7 +844,6 @@ void SemanticState::attached()
void SemanticState::detached()
{
- assertClusterSafe();
for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) {
i->second->disableNotify();
session.getConnection().outputTasks.removeOutputTask(i->second.get());
diff --git a/cpp/src/qpid/broker/SemanticState.h b/cpp/src/qpid/broker/SemanticState.h
index b2e648410a..6d88dd56d9 100644
--- a/cpp/src/qpid/broker/SemanticState.h
+++ b/cpp/src/qpid/broker/SemanticState.h
@@ -65,7 +65,7 @@ class SessionContext;
*
* Message delivery is driven by ConsumerImpl::doOutput(), which is
* called when a client's socket is ready to write data.
- *
+ *
*/
class SemanticState : private boost::noncopyable {
public:
@@ -75,14 +75,15 @@ class SemanticState : private boost::noncopyable {
{
mutable qpid::sys::Mutex lock;
SemanticState* const parent;
- const std::string name;
const boost::shared_ptr<Queue> queue;
const bool ackExpected;
const bool acquire;
bool blocked;
bool windowing;
+ bool windowActive;
bool exclusive;
std::string resumeId;
+ const std::string tag; // <destination> from AMQP 0-10 Message.subscribe command
uint64_t resumeTtl;
framing::FieldTable arguments;
uint32_t msgCredit;
@@ -99,15 +100,16 @@ class SemanticState : private boost::noncopyable {
public:
typedef boost::shared_ptr<ConsumerImpl> shared_ptr;
- ConsumerImpl(SemanticState* parent,
+ ConsumerImpl(SemanticState* parent,
const std::string& name, boost::shared_ptr<Queue> queue,
bool ack, bool acquire, bool exclusive,
- const std::string& resumeId, uint64_t resumeTtl, const framing::FieldTable& arguments);
+ const std::string& tag, const std::string& resumeId,
+ uint64_t resumeTtl, const framing::FieldTable& arguments);
~ConsumerImpl();
OwnershipToken* getSession();
- bool deliver(QueuedMessage& msg);
- bool filter(boost::intrusive_ptr<Message> msg);
- bool accept(boost::intrusive_ptr<Message> msg);
+ bool deliver(QueuedMessage& msg);
+ bool filter(boost::intrusive_ptr<Message> msg);
+ bool accept(boost::intrusive_ptr<Message> msg);
void disableNotify();
void enableNotify();
@@ -122,15 +124,13 @@ class SemanticState : private boost::noncopyable {
void addMessageCredit(uint32_t value);
void flush();
void stop();
- void complete(DeliveryRecord&);
+ void complete(DeliveryRecord&);
boost::shared_ptr<Queue> getQueue() const { return queue; }
bool isBlocked() const { return blocked; }
bool setBlocked(bool set) { std::swap(set, blocked); return set; }
bool doOutput();
- std::string getName() const { return name; }
-
bool isAckExpected() const { return ackExpected; }
bool isAcquire() const { return acquire; }
bool isWindowing() const { return windowing; }
@@ -138,6 +138,7 @@ class SemanticState : private boost::noncopyable {
uint32_t getMsgCredit() const { return msgCredit; }
uint32_t getByteCredit() const { return byteCredit; }
std::string getResumeId() const { return resumeId; };
+ const std::string& getTag() const { return tag; }
uint64_t getResumeTtl() const { return resumeTtl; }
const framing::FieldTable& getArguments() const { return arguments; }
@@ -148,9 +149,10 @@ class SemanticState : private boost::noncopyable {
management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args, std::string& text);
};
+ typedef std::map<std::string, DtxBuffer::shared_ptr> DtxBufferMap;
+
private:
typedef std::map<std::string, ConsumerImpl::shared_ptr> ConsumerImplMap;
- typedef std::map<std::string, DtxBuffer::shared_ptr> DtxBufferMap;
SessionContext& session;
DeliveryAdapter& deliveryAdapter;
@@ -163,7 +165,6 @@ class SemanticState : private boost::noncopyable {
DtxBufferMap suspendedXids;
framing::SequenceSet accumulatedAck;
boost::shared_ptr<Exchange> cacheExchange;
- AclModule* acl;
const bool authMsg;
const std::string userID;
const std::string userName;
@@ -181,14 +182,16 @@ class SemanticState : private boost::noncopyable {
void disable(ConsumerImpl::shared_ptr);
public:
+
SemanticState(DeliveryAdapter&, SessionContext&);
~SemanticState();
SessionContext& getSession() { return session; }
const SessionContext& getSession() const { return session; }
- ConsumerImpl& find(const std::string& destination);
-
+ const ConsumerImpl::shared_ptr find(const std::string& destination) const;
+ bool find(const std::string& destination, ConsumerImpl::shared_ptr&) const;
+
/**
* Get named queue, never returns 0.
* @return: named queue
@@ -196,16 +199,16 @@ class SemanticState : private boost::noncopyable {
* @exception: ConnectionException if name="" and session has no default.
*/
boost::shared_ptr<Queue> getQueue(const std::string& name) const;
-
+
bool exists(const std::string& consumerTag);
- void consume(const std::string& destination,
- boost::shared_ptr<Queue> queue,
+ void consume(const std::string& destination,
+ boost::shared_ptr<Queue> queue,
bool ackRequired, bool acquire, bool exclusive,
const std::string& resumeId=std::string(), uint64_t resumeTtl=0,
const framing::FieldTable& = framing::FieldTable());
- void cancel(const std::string& tag);
+ bool cancel(const std::string& tag);
void setWindowMode(const std::string& destination);
void setCreditMode(const std::string& destination);
@@ -218,12 +221,13 @@ class SemanticState : private boost::noncopyable {
void commit(MessageStore* const store);
void rollback();
void selectDtx();
+ bool getDtxSelected() const { return dtxSelected; }
void startDtx(const std::string& xid, DtxManager& mgr, bool join);
void endDtx(const std::string& xid, bool fail);
void suspendDtx(const std::string& xid);
void resumeDtx(const std::string& xid);
void recover(bool requeue);
- void deliver(DeliveryRecord& message, bool sync);
+ void deliver(DeliveryRecord& message, bool sync);
void acquire(DeliveryId first, DeliveryId last, DeliveryIds& acquired);
void release(DeliveryId first, DeliveryId last, bool setRedelivered);
void reject(DeliveryId first, DeliveryId last);
@@ -244,9 +248,12 @@ class SemanticState : private boost::noncopyable {
DeliveryRecords& getUnacked() { return unacked; }
framing::SequenceSet getAccumulatedAck() const { return accumulatedAck; }
TxBuffer::shared_ptr getTxBuffer() const { return txBuffer; }
+ DtxBuffer::shared_ptr getDtxBuffer() const { return dtxBuffer; }
void setTxBuffer(const TxBuffer::shared_ptr& txb) { txBuffer = txb; }
+ void setDtxBuffer(const DtxBuffer::shared_ptr& dtxb) { dtxBuffer = dtxb; txBuffer = dtxb; }
void setAccumulatedAck(const framing::SequenceSet& s) { accumulatedAck = s; }
void record(const DeliveryRecord& delivery);
+ DtxBufferMap& getSuspendedXids() { return suspendedXids; }
};
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/SessionAdapter.cpp b/cpp/src/qpid/broker/SessionAdapter.cpp
index 3d62e73185..63c4b660b2 100644
--- a/cpp/src/qpid/broker/SessionAdapter.cpp
+++ b/cpp/src/qpid/broker/SessionAdapter.cpp
@@ -24,6 +24,7 @@
#include "qpid/log/Statement.h"
#include "qpid/framing/SequenceSet.h"
#include "qpid/management/ManagementAgent.h"
+#include "qpid/broker/SessionState.h"
#include "qmf/org/apache/qpid/broker/EventExchangeDeclare.h"
#include "qmf/org/apache/qpid/broker/EventExchangeDelete.h"
#include "qmf/org/apache/qpid/broker/EventQueueDeclare.h"
@@ -64,53 +65,56 @@ void SessionAdapter::ExchangeHandlerImpl::declare(const string& exchange, const
const string& alternateExchange,
bool passive, bool durable, bool /*autoDelete*/, const FieldTable& args){
- AclModule* acl = getBroker().getAcl();
- if (acl) {
- std::map<acl::Property, std::string> params;
- params.insert(make_pair(acl::PROP_TYPE, type));
- params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange));
- params.insert(make_pair(acl::PROP_PASSIVE, std::string(passive ? _TRUE : _FALSE) ));
- params.insert(make_pair(acl::PROP_DURABLE, std::string(durable ? _TRUE : _FALSE)));
- if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_EXCHANGE,exchange,&params) )
- throw UnauthorizedAccessException(QPID_MSG("ACL denied exchange declare request from " << getConnection().getUserId()));
- }
-
//TODO: implement autoDelete
Exchange::shared_ptr alternate;
if (!alternateExchange.empty()) {
alternate = getBroker().getExchanges().get(alternateExchange);
}
if(passive){
+ AclModule* acl = getBroker().getAcl();
+ if (acl) {
+ //TODO: why does a passive declare require create
+ //permission? The purpose of the passive flag is to state
+ //that the exchange should *not* created. For
+ //authorisation a passive declare is similar to
+ //exchange-query.
+ std::map<acl::Property, std::string> params;
+ params.insert(make_pair(acl::PROP_TYPE, type));
+ params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange));
+ params.insert(make_pair(acl::PROP_PASSIVE, _TRUE));
+ params.insert(make_pair(acl::PROP_DURABLE, durable ? _TRUE : _FALSE));
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_EXCHANGE,exchange,&params) )
+ throw framing::UnauthorizedAccessException(QPID_MSG("ACL denied exchange create request from " << getConnection().getUserId()));
+ }
Exchange::shared_ptr actual(getBroker().getExchanges().get(exchange));
checkType(actual, type);
checkAlternate(actual, alternate);
- }else{
+ }else{
if(exchange.find("amq.") == 0 || exchange.find("qpid.") == 0) {
throw framing::NotAllowedException(QPID_MSG("Exchange names beginning with \"amq.\" or \"qpid.\" are reserved. (exchange=\"" << exchange << "\")"));
}
try{
- std::pair<Exchange::shared_ptr, bool> response = getBroker().getExchanges().declare(exchange, type, durable, args);
- if (response.second) {
- if (alternate) {
- response.first->setAlternate(alternate);
- alternate->incAlternateUsers();
- }
- if (durable) {
- getBroker().getStore().create(*response.first, args);
- }
- } else {
+ std::pair<Exchange::shared_ptr, bool> response =
+ getBroker().createExchange(exchange, type, durable, alternateExchange, args,
+ getConnection().getUserId(), getConnection().getUrl());
+ if (!response.second) {
+ //exchange already there, not created
checkType(response.first, type);
checkAlternate(response.first, alternate);
+ ManagementAgent* agent = getBroker().getManagementAgent();
+ if (agent)
+ agent->raiseEvent(_qmf::EventExchangeDeclare(getConnection().getUrl(),
+ getConnection().getUserId(),
+ exchange,
+ type,
+ alternateExchange,
+ durable,
+ false,
+ ManagementAgent::toMap(args),
+ "existing"));
}
-
- ManagementAgent* agent = getBroker().getManagementAgent();
- if (agent)
- agent->raiseEvent(_qmf::EventExchangeDeclare(getConnection().getUrl(), getConnection().getUserId(), exchange, type,
- alternateExchange, durable, false, ManagementAgent::toMap(args),
- response.second ? "created" : "existing"));
-
}catch(UnknownExchangeTypeException& /*e*/){
- throw CommandInvalidException(QPID_MSG("Exchange type not implemented: " << type));
+ throw NotFoundException(QPID_MSG("Exchange type not implemented: " << type));
}
}
}
@@ -134,22 +138,8 @@ void SessionAdapter::ExchangeHandlerImpl::checkAlternate(Exchange::shared_ptr ex
void SessionAdapter::ExchangeHandlerImpl::delete_(const string& name, bool /*ifUnused*/)
{
- AclModule* acl = getBroker().getAcl();
- if (acl) {
- if (!acl->authorise(getConnection().getUserId(),acl::ACT_DELETE,acl::OBJ_EXCHANGE,name,NULL) )
- throw UnauthorizedAccessException(QPID_MSG("ACL denied exchange delete request from " << getConnection().getUserId()));
- }
-
- //TODO: implement unused
- Exchange::shared_ptr exchange(getBroker().getExchanges().get(name));
- if (exchange->inUseAsAlternate()) throw NotAllowedException(QPID_MSG("Exchange in use as alternate-exchange."));
- if (exchange->isDurable()) getBroker().getStore().destroy(*exchange);
- if (exchange->getAlternate()) exchange->getAlternate()->decAlternateUsers();
- getBroker().getExchanges().destroy(name);
-
- ManagementAgent* agent = getBroker().getManagementAgent();
- if (agent)
- agent->raiseEvent(_qmf::EventExchangeDelete(getConnection().getUrl(), getConnection().getUserId(), name));
+ //TODO: implement if-unused
+ getBroker().deleteExchange(name, getConnection().getUserId(), getConnection().getUrl());
}
ExchangeQueryResult SessionAdapter::ExchangeHandlerImpl::query(const string& name)
@@ -169,67 +159,19 @@ ExchangeQueryResult SessionAdapter::ExchangeHandlerImpl::query(const string& nam
}
void SessionAdapter::ExchangeHandlerImpl::bind(const string& queueName,
- const string& exchangeName, const string& routingKey,
- const FieldTable& arguments)
+ const string& exchangeName, const string& routingKey,
+ const FieldTable& arguments)
{
- AclModule* acl = getBroker().getAcl();
- if (acl) {
- std::map<acl::Property, std::string> params;
- params.insert(make_pair(acl::PROP_QUEUENAME, queueName));
- params.insert(make_pair(acl::PROP_ROUTINGKEY, routingKey));
-
- if (!acl->authorise(getConnection().getUserId(),acl::ACT_BIND,acl::OBJ_EXCHANGE,exchangeName,&params))
- throw UnauthorizedAccessException(QPID_MSG("ACL denied exchange bind request from " << getConnection().getUserId()));
- }
-
- Queue::shared_ptr queue = getQueue(queueName);
- Exchange::shared_ptr exchange = getBroker().getExchanges().get(exchangeName);
- if(exchange){
- string exchangeRoutingKey = routingKey.empty() && queueName.empty() ? queue->getName() : routingKey;
- if (exchange->bind(queue, exchangeRoutingKey, &arguments)) {
- queue->bound(exchangeName, routingKey, arguments);
- if (exchange->isDurable() && queue->isDurable()) {
- getBroker().getStore().bind(*exchange, *queue, routingKey, arguments);
- }
-
- ManagementAgent* agent = getBroker().getManagementAgent();
- if (agent)
- agent->raiseEvent(_qmf::EventBind(getConnection().getUrl(), getConnection().getUserId(), exchangeName,
- queueName, exchangeRoutingKey, ManagementAgent::toMap(arguments)));
- }
- }else{
- throw NotFoundException("Bind failed. No such exchange: " + exchangeName);
- }
+ getBroker().bind(queueName, exchangeName, routingKey, arguments,
+ getConnection().getUserId(), getConnection().getUrl());
}
void SessionAdapter::ExchangeHandlerImpl::unbind(const string& queueName,
const string& exchangeName,
const string& routingKey)
{
- AclModule* acl = getBroker().getAcl();
- if (acl) {
- std::map<acl::Property, std::string> params;
- params.insert(make_pair(acl::PROP_QUEUENAME, queueName));
- params.insert(make_pair(acl::PROP_ROUTINGKEY, routingKey));
- if (!acl->authorise(getConnection().getUserId(),acl::ACT_UNBIND,acl::OBJ_EXCHANGE,exchangeName,&params) )
- throw UnauthorizedAccessException(QPID_MSG("ACL denied exchange unbind request from " << getConnection().getUserId()));
- }
-
- Queue::shared_ptr queue = getQueue(queueName);
- if (!queue.get()) throw NotFoundException("Unbind failed. No such exchange: " + exchangeName);
-
- Exchange::shared_ptr exchange = getBroker().getExchanges().get(exchangeName);
- if (!exchange.get()) throw NotFoundException("Unbind failed. No such exchange: " + exchangeName);
-
- //TODO: revise unbind to rely solely on binding key (not args)
- if (exchange->unbind(queue, routingKey, 0)) {
- if (exchange->isDurable() && queue->isDurable())
- getBroker().getStore().unbind(*exchange, *queue, routingKey, FieldTable());
-
- ManagementAgent* agent = getBroker().getManagementAgent();
- if (agent)
- agent->raiseEvent(_qmf::EventUnbind(getConnection().getUrl(), getConnection().getUserId(), exchangeName, queueName, routingKey));
- }
+ getBroker().unbind(queueName, exchangeName, routingKey,
+ getConnection().getUserId(), getConnection().getUrl());
}
ExchangeBoundResult SessionAdapter::ExchangeHandlerImpl::bound(const std::string& exchangeName,
@@ -332,52 +274,42 @@ QueueQueryResult SessionAdapter::QueueHandlerImpl::query(const string& name)
void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string& alternateExchange,
bool passive, bool durable, bool exclusive,
bool autoDelete, const qpid::framing::FieldTable& arguments)
-{
- AclModule* acl = getBroker().getAcl();
- if (acl) {
- std::map<acl::Property, std::string> params;
- params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange));
- params.insert(make_pair(acl::PROP_PASSIVE, std::string(passive ? _TRUE : _FALSE) ));
- params.insert(make_pair(acl::PROP_DURABLE, std::string(durable ? _TRUE : _FALSE)));
- params.insert(make_pair(acl::PROP_EXCLUSIVE, std::string(exclusive ? _TRUE : _FALSE)));
- params.insert(make_pair(acl::PROP_AUTODELETE, std::string(autoDelete ? _TRUE : _FALSE)));
- params.insert(make_pair(acl::PROP_POLICYTYPE, arguments.getAsString("qpid.policy_type")));
- params.insert(make_pair(acl::PROP_MAXQUEUECOUNT, boost::lexical_cast<string>(arguments.getAsInt("qpid.max_count"))));
- params.insert(make_pair(acl::PROP_MAXQUEUESIZE, boost::lexical_cast<string>(arguments.getAsInt64("qpid.max_size"))));
-
- if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_QUEUE,name,&params) )
- throw UnauthorizedAccessException(QPID_MSG("ACL denied queue create request from " << getConnection().getUserId()));
- }
-
- Exchange::shared_ptr alternate;
- if (!alternateExchange.empty()) {
- alternate = getBroker().getExchanges().get(alternateExchange);
- }
+{
Queue::shared_ptr queue;
if (passive && !name.empty()) {
- queue = getQueue(name);
+ AclModule* acl = getBroker().getAcl();
+ if (acl) {
+ //TODO: why does a passive declare require create
+ //permission? The purpose of the passive flag is to state
+ //that the queue should *not* created. For
+ //authorisation a passive declare is similar to
+ //queue-query (or indeed a qmf query).
+ std::map<acl::Property, std::string> params;
+ params.insert(make_pair(acl::PROP_ALTERNATE, alternateExchange));
+ params.insert(make_pair(acl::PROP_PASSIVE, _TRUE));
+ params.insert(make_pair(acl::PROP_DURABLE, std::string(durable ? _TRUE : _FALSE)));
+ params.insert(make_pair(acl::PROP_EXCLUSIVE, std::string(exclusive ? _TRUE : _FALSE)));
+ params.insert(make_pair(acl::PROP_AUTODELETE, std::string(autoDelete ? _TRUE : _FALSE)));
+ params.insert(make_pair(acl::PROP_POLICYTYPE, arguments.getAsString("qpid.policy_type")));
+ params.insert(make_pair(acl::PROP_MAXQUEUECOUNT, boost::lexical_cast<string>(arguments.getAsInt("qpid.max_count"))));
+ params.insert(make_pair(acl::PROP_MAXQUEUESIZE, boost::lexical_cast<string>(arguments.getAsInt64("qpid.max_size"))));
+ if (!acl->authorise(getConnection().getUserId(),acl::ACT_CREATE,acl::OBJ_QUEUE,name,&params) )
+ throw UnauthorizedAccessException(QPID_MSG("ACL denied queue create request from " << getConnection().getUserId()));
+ }
+ queue = getQueue(name);
//TODO: check alternate-exchange is as expected
} else {
- std::pair<Queue::shared_ptr, bool> queue_created =
- getBroker().getQueues().declare(name, durable,
- autoDelete,
- exclusive ? &session : 0);
+ std::pair<Queue::shared_ptr, bool> queue_created =
+ getBroker().createQueue(name, durable,
+ autoDelete,
+ exclusive ? &session : 0,
+ alternateExchange,
+ arguments,
+ getConnection().getUserId(),
+ getConnection().getUrl());
queue = queue_created.first;
assert(queue);
if (queue_created.second) { // This is a new queue
- if (alternate) {
- queue->setAlternateExchange(alternate);
- alternate->incAlternateUsers();
- }
-
- //apply settings & create persistent record if required
- try { queue_created.first->create(arguments); }
- catch (...) { getBroker().getQueues().destroy(name); throw; }
-
- //add default binding:
- getBroker().getExchanges().getDefault()->bind(queue, name, 0);
- queue->bound(getBroker().getExchanges().getDefault()->getName(), name, arguments);
-
//handle automatic cleanup:
if (exclusive) {
exclusiveQueues.push_back(queue);
@@ -386,21 +318,20 @@ void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string&
if (exclusive && queue->setExclusiveOwner(&session)) {
exclusiveQueues.push_back(queue);
}
+ ManagementAgent* agent = getBroker().getManagementAgent();
+ if (agent)
+ agent->raiseEvent(_qmf::EventQueueDeclare(getConnection().getUrl(), getConnection().getUserId(),
+ name, durable, exclusive, autoDelete, ManagementAgent::toMap(arguments),
+ "existing"));
}
- ManagementAgent* agent = getBroker().getManagementAgent();
- if (agent)
- agent->raiseEvent(_qmf::EventQueueDeclare(getConnection().getUrl(), getConnection().getUserId(),
- name, durable, exclusive, autoDelete, ManagementAgent::toMap(arguments),
- queue_created.second ? "created" : "existing"));
}
- if (exclusive && !queue->isExclusiveOwner(&session))
+ if (exclusive && !queue->isExclusiveOwner(&session))
throw ResourceLockedException(QPID_MSG("Cannot grant exclusive access to queue "
<< queue->getName()));
-}
-
-
+}
+
void SessionAdapter::QueueHandlerImpl::purge(const string& queue){
AclModule* acl = getBroker().getAcl();
if (acl)
@@ -409,40 +340,32 @@ void SessionAdapter::QueueHandlerImpl::purge(const string& queue){
throw UnauthorizedAccessException(QPID_MSG("ACL denied queue purge request from " << getConnection().getUserId()));
}
getQueue(queue)->purge();
-}
-
-void SessionAdapter::QueueHandlerImpl::delete_(const string& queue, bool ifUnused, bool ifEmpty){
-
- AclModule* acl = getBroker().getAcl();
- if (acl)
- {
- if (!acl->authorise(getConnection().getUserId(),acl::ACT_DELETE,acl::OBJ_QUEUE,queue,NULL) )
- throw UnauthorizedAccessException(QPID_MSG("ACL denied queue delete request from " << getConnection().getUserId()));
- }
+}
- Queue::shared_ptr q = getQueue(queue);
- if (q->hasExclusiveOwner() && !q->isExclusiveOwner(&session))
+void SessionAdapter::QueueHandlerImpl::checkDelete(Queue::shared_ptr queue, bool ifUnused, bool ifEmpty)
+{
+ if (queue->hasExclusiveOwner() && !queue->isExclusiveOwner(&session)) {
throw ResourceLockedException(QPID_MSG("Cannot delete queue "
- << queue << "; it is exclusive to another session"));
- if(ifEmpty && q->getMessageCount() > 0){
- throw PreconditionFailedException("Queue not empty.");
- }else if(ifUnused && q->getConsumerCount() > 0){
- throw PreconditionFailedException("Queue in use.");
- }else{
+ << queue->getName() << "; it is exclusive to another session"));
+ } else if(ifEmpty && queue->getMessageCount() > 0) {
+ throw PreconditionFailedException(QPID_MSG("Cannot delete queue "
+ << queue->getName() << "; queue not empty"));
+ } else if(ifUnused && queue->getConsumerCount() > 0) {
+ throw PreconditionFailedException(QPID_MSG("Cannot delete queue "
+ << queue->getName() << "; queue in use"));
+ } else if (queue->isExclusiveOwner(&session)) {
//remove the queue from the list of exclusive queues if necessary
- if(q->isExclusiveOwner(&getConnection())){
- QueueVector::iterator i = std::find(getConnection().exclusiveQueues.begin(), getConnection().exclusiveQueues.end(), q);
- if(i < getConnection().exclusiveQueues.end()) getConnection().exclusiveQueues.erase(i);
- }
- q->destroy();
- getBroker().getQueues().destroy(queue);
- q->unbind(getBroker().getExchanges(), q);
-
- ManagementAgent* agent = getBroker().getManagementAgent();
- if (agent)
- agent->raiseEvent(_qmf::EventQueueDelete(getConnection().getUrl(), getConnection().getUserId(), queue));
- q->notifyDeleted();
- }
+ QueueVector::iterator i = std::find(exclusiveQueues.begin(),
+ exclusiveQueues.end(),
+ queue);
+ if (i < exclusiveQueues.end()) exclusiveQueues.erase(i);
+ }
+}
+
+void SessionAdapter::QueueHandlerImpl::delete_(const string& queue, bool ifUnused, bool ifEmpty)
+{
+ getBroker().deleteQueue(queue, getConnection().getUserId(), getConnection().getUrl(),
+ boost::bind(&SessionAdapter::QueueHandlerImpl::checkDelete, this, _1, ifUnused, ifEmpty));
}
SessionAdapter::MessageHandlerImpl::MessageHandlerImpl(SemanticState& s) :
@@ -508,7 +431,9 @@ SessionAdapter::MessageHandlerImpl::subscribe(const string& queueName,
void
SessionAdapter::MessageHandlerImpl::cancel(const string& destination )
{
- state.cancel(destination);
+ if (!state.cancel(destination)) {
+ throw NotFoundException(QPID_MSG("No such subscription: " << destination));
+ }
ManagementAgent* agent = getBroker().getManagementAgent();
if (agent)
@@ -587,7 +512,12 @@ framing::MessageResumeResult SessionAdapter::MessageHandlerImpl::resume(const st
-void SessionAdapter::ExecutionHandlerImpl::sync() {} //essentially a no-op
+void SessionAdapter::ExecutionHandlerImpl::sync()
+{
+ session.addPendingExecutionSync();
+ /** @todo KAG - need a generic mechanism to allow a command to returning "not completed" status back to SessionState */
+
+}
void SessionAdapter::ExecutionHandlerImpl::result(const SequenceNumber& /*commandId*/, const string& /*value*/)
{
diff --git a/cpp/src/qpid/broker/SessionAdapter.h b/cpp/src/qpid/broker/SessionAdapter.h
index ca27fb6e1d..8987c4812f 100644
--- a/cpp/src/qpid/broker/SessionAdapter.h
+++ b/cpp/src/qpid/broker/SessionAdapter.h
@@ -138,6 +138,7 @@ class Queue;
bool isLocal(const ConnectionToken* t) const;
void destroyExclusiveQueues();
+ void checkDelete(boost::shared_ptr<Queue> queue, bool ifUnused, bool ifEmpty);
template <class F> void eachExclusiveQueue(F f)
{
std::for_each(exclusiveQueues.begin(), exclusiveQueues.end(), f);
diff --git a/cpp/src/qpid/broker/SessionContext.h b/cpp/src/qpid/broker/SessionContext.h
index afbbb2cc22..253ce8dcf2 100644
--- a/cpp/src/qpid/broker/SessionContext.h
+++ b/cpp/src/qpid/broker/SessionContext.h
@@ -46,6 +46,7 @@ class SessionContext : public OwnershipToken, public sys::OutputControl
virtual Broker& getBroker() = 0;
virtual uint16_t getChannel() const = 0;
virtual const SessionId& getSessionId() const = 0;
+ virtual void addPendingExecutionSync() = 0;
};
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/SessionHandler.cpp b/cpp/src/qpid/broker/SessionHandler.cpp
index 69b364ad7b..752fa55535 100644
--- a/cpp/src/qpid/broker/SessionHandler.cpp
+++ b/cpp/src/qpid/broker/SessionHandler.cpp
@@ -40,11 +40,6 @@ SessionHandler::SessionHandler(Connection& c, ChannelId ch)
SessionHandler::~SessionHandler() {}
-namespace {
-ClassId classId(AMQMethodBody* m) { return m ? m->amqpMethodId() : 0; }
-MethodId methodId(AMQMethodBody* m) { return m ? m->amqpClassId() : 0; }
-} // namespace
-
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);
diff --git a/cpp/src/qpid/broker/SessionState.cpp b/cpp/src/qpid/broker/SessionState.cpp
index 1ca7b6dfc1..1ab17e9893 100644
--- a/cpp/src/qpid/broker/SessionState.cpp
+++ b/cpp/src/qpid/broker/SessionState.cpp
@@ -25,6 +25,7 @@
#include "qpid/broker/SessionManager.h"
#include "qpid/broker/SessionHandler.h"
#include "qpid/broker/RateFlowcontrol.h"
+#include "qpid/sys/ClusterSafe.h"
#include "qpid/sys/Timer.h"
#include "qpid/framing/AMQContentBody.h"
#include "qpid/framing/AMQHeaderBody.h"
@@ -60,9 +61,9 @@ SessionState::SessionState(
semanticState(*this, *this),
adapter(semanticState),
msgBuilder(&broker.getStore()),
- enqueuedOp(boost::bind(&SessionState::enqueued, this, _1)),
mgmtObject(0),
- rateFlowcontrol(0)
+ rateFlowcontrol(0),
+ asyncCommandCompleter(new AsyncCommandCompleter(this))
{
uint32_t maxRate = broker.getOptions().maxSessionRate;
if (maxRate) {
@@ -95,6 +96,7 @@ void SessionState::addManagementObject() {
}
SessionState::~SessionState() {
+ asyncCommandCompleter->cancel();
semanticState.closed();
if (mgmtObject != 0)
mgmtObject->resourceDestroy ();
@@ -125,6 +127,7 @@ bool SessionState::isLocal(const ConnectionToken* t) const
void SessionState::detach() {
QPID_LOG(debug, getId() << ": detached on broker.");
+ asyncCommandCompleter->detached();
disableOutput();
handler = 0;
if (mgmtObject != 0)
@@ -145,6 +148,7 @@ void SessionState::attach(SessionHandler& h) {
mgmtObject->set_connectionRef (h.getConnection().GetManagementObject()->getObjectId());
mgmtObject->set_channelId (h.getChannel());
}
+ asyncCommandCompleter->attached();
}
void SessionState::abort() {
@@ -202,15 +206,17 @@ Manageable::status_t SessionState::ManagementMethod (uint32_t methodId,
}
void SessionState::handleCommand(framing::AMQMethodBody* method, const SequenceNumber& id) {
+ currentCommandComplete = true; // assumed, can be overridden by invoker method (this sucks).
Invoker::Result invocation = invoke(adapter, *method);
- receiverCompleted(id);
+ if (currentCommandComplete) receiverCompleted(id);
+
if (!invocation.wasHandled()) {
throw NotImplementedException(QPID_MSG("Not implemented: " << *method));
} else if (invocation.hasResult()) {
getProxy().getExecution().result(id, invocation.getResult());
}
- if (method->isSync()) {
- incomplete.process(enqueuedOp, true);
+
+ if (method->isSync() && currentCommandComplete) {
sendAcceptAndCompletion();
}
}
@@ -253,23 +259,14 @@ void SessionState::handleContent(AMQFrame& frame, const SequenceNumber& id)
header.setEof(false);
msg->getFrames().append(header);
}
+ if (broker.isTimestamping())
+ msg->setTimestamp();
msg->setPublisher(&getConnection());
+ msg->getIngressCompletion().begin();
semanticState.handle(msg);
msgBuilder.end();
-
- if (msg->isEnqueueComplete()) {
- enqueued(msg);
- } else {
- incomplete.add(msg);
- }
-
- //hold up execution until async enqueue is complete
- if (msg->getFrames().getMethod()->isSync()) {
- incomplete.process(enqueuedOp, true);
- sendAcceptAndCompletion();
- } else {
- incomplete.process(enqueuedOp, false);
- }
+ IncompleteIngressMsgXfer xfer(this, msg);
+ msg->getIngressCompletion().end(xfer); // allows msg to complete xfer
}
// Handle producer session flow control
@@ -319,11 +316,41 @@ void SessionState::sendAcceptAndCompletion()
sendCompletion();
}
-void SessionState::enqueued(boost::intrusive_ptr<Message> msg)
+/** Invoked when the given inbound message is finished being processed
+ * by all interested parties (eg. it is done being enqueued to all queues,
+ * its credit has been accounted for, etc). At this point, msg is considered
+ * by this receiver as 'completed' (as defined by AMQP 0_10)
+ */
+void SessionState::completeRcvMsg(SequenceNumber id,
+ bool requiresAccept,
+ bool requiresSync)
{
- receiverCompleted(msg->getCommandId());
- if (msg->requiresAccept())
- accepted.add(msg->getCommandId());
+ // Mark this as a cluster-unsafe scope since it can be called in
+ // journal threads or connection threads as part of asynchronous
+ // command completion.
+ sys::ClusterUnsafeScope cus;
+
+ bool callSendCompletion = false;
+ receiverCompleted(id);
+ if (requiresAccept)
+ // will cause msg's seq to appear in the next message.accept we send.
+ accepted.add(id);
+
+ // Are there any outstanding Execution.Sync commands pending the
+ // completion of this msg? If so, complete them.
+ while (!pendingExecutionSyncs.empty() &&
+ receiverGetIncomplete().front() >= pendingExecutionSyncs.front()) {
+ const SequenceNumber id = pendingExecutionSyncs.front();
+ pendingExecutionSyncs.pop();
+ QPID_LOG(debug, getId() << ": delayed execution.sync " << id << " is completed.");
+ receiverCompleted(id);
+ callSendCompletion = true; // likely peer is pending for this completion.
+ }
+
+ // if the sender has requested immediate notification of the completion...
+ if (requiresSync || callSendCompletion) {
+ sendAcceptAndCompletion();
+ }
}
void SessionState::handleIn(AMQFrame& frame) {
@@ -396,4 +423,176 @@ framing::AMQP_ClientProxy& SessionState::getClusterOrderProxy() {
return handler->getClusterOrderProxy();
}
+
+// Current received command is an execution.sync command.
+// Complete this command only when all preceding commands have completed.
+// (called via the invoker() in handleCommand() above)
+void SessionState::addPendingExecutionSync()
+{
+ SequenceNumber syncCommandId = receiverGetCurrent();
+ if (receiverGetIncomplete().front() < syncCommandId) {
+ currentCommandComplete = false;
+ pendingExecutionSyncs.push(syncCommandId);
+ asyncCommandCompleter->flushPendingMessages();
+ QPID_LOG(debug, getId() << ": delaying completion of execution.sync " << syncCommandId);
+ }
+}
+
+
+/** factory for creating a reference-counted IncompleteIngressMsgXfer object
+ * which will be attached to a message that will be completed asynchronously.
+ */
+boost::intrusive_ptr<AsyncCompletion::Callback>
+SessionState::IncompleteIngressMsgXfer::clone()
+{
+ // Optimization: this routine is *only* invoked when the message needs to be asynchronously completed.
+ // If the client is pending the message.transfer completion, flush now to force immediate write to journal.
+ if (requiresSync)
+ msg->flush();
+ else {
+ // otherwise, we need to track this message in order to flush it if an execution.sync arrives
+ // before it has been completed (see flushPendingMessages())
+ pending = true;
+ completerContext->addPendingMessage(msg);
+ }
+
+ return boost::intrusive_ptr<SessionState::IncompleteIngressMsgXfer>(new SessionState::IncompleteIngressMsgXfer(*this));
+}
+
+
+/** Invoked by the asynchronous completer associated with a received
+ * msg that is pending Completion. May be invoked by the IO thread
+ * (sync == true), or some external thread (!sync).
+ */
+void SessionState::IncompleteIngressMsgXfer::completed(bool sync)
+{
+ if (pending) completerContext->deletePendingMessage(id);
+ if (!sync) {
+ /** note well: this path may execute in any thread. It is safe to access
+ * the scheduledCompleterContext, since *this has a shared pointer to it.
+ * but not session!
+ */
+ session = 0;
+ QPID_LOG(debug, ": async completion callback scheduled for msg seq=" << id);
+ completerContext->scheduleMsgCompletion(id, requiresAccept, requiresSync);
+ } else {
+ // this path runs directly from the ac->end() call in handleContent() above,
+ // so *session is definately valid.
+ if (session->isAttached()) {
+ QPID_LOG(debug, ": receive completed for msg seq=" << id);
+ session->completeRcvMsg(id, requiresAccept, requiresSync);
+ }
+ }
+ completerContext = boost::intrusive_ptr<AsyncCommandCompleter>();
+}
+
+
+/** Scheduled from an asynchronous command's completed callback to run on
+ * the IO thread.
+ */
+void SessionState::AsyncCommandCompleter::schedule(boost::intrusive_ptr<AsyncCommandCompleter> ctxt)
+{
+ ctxt->completeCommands();
+}
+
+
+/** Track an ingress message that is pending completion */
+void SessionState::AsyncCommandCompleter::addPendingMessage(boost::intrusive_ptr<Message> msg)
+{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock);
+ std::pair<SequenceNumber, boost::intrusive_ptr<Message> > item(msg->getCommandId(), msg);
+ bool unique = pendingMsgs.insert(item).second;
+ if (!unique) {
+ assert(false);
+ }
+}
+
+
+/** pending message has completed */
+void SessionState::AsyncCommandCompleter::deletePendingMessage(SequenceNumber id)
+{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock);
+ pendingMsgs.erase(id);
+}
+
+
+/** done when an execution.sync arrives */
+void SessionState::AsyncCommandCompleter::flushPendingMessages()
+{
+ std::map<SequenceNumber, boost::intrusive_ptr<Message> > copy;
+ {
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock);
+ pendingMsgs.swap(copy); // we've only tracked these in case a flush is needed, so nuke 'em now.
+ }
+ // drop lock, so it is safe to call "flush()"
+ for (std::map<SequenceNumber, boost::intrusive_ptr<Message> >::iterator i = copy.begin();
+ i != copy.end(); ++i) {
+ i->second->flush();
+ }
+}
+
+
+/** mark an ingress Message.Transfer command as completed.
+ * This method must be thread safe - it may run on any thread.
+ */
+void SessionState::AsyncCommandCompleter::scheduleMsgCompletion(SequenceNumber cmd,
+ bool requiresAccept,
+ bool requiresSync)
+{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock);
+
+ if (session && isAttached) {
+ MessageInfo msg(cmd, requiresAccept, requiresSync);
+ completedMsgs.push_back(msg);
+ if (completedMsgs.size() == 1) {
+ session->getConnection().requestIOProcessing(boost::bind(&schedule,
+ session->asyncCommandCompleter));
+ }
+ }
+}
+
+
+/** Cause the session to complete all completed commands.
+ * Executes on the IO thread.
+ */
+void SessionState::AsyncCommandCompleter::completeCommands()
+{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock);
+
+ // when session is destroyed, it clears the session pointer via cancel().
+ if (session && session->isAttached()) {
+ for (std::vector<MessageInfo>::iterator msg = completedMsgs.begin();
+ msg != completedMsgs.end(); ++msg) {
+ session->completeRcvMsg(msg->cmd, msg->requiresAccept, msg->requiresSync);
+ }
+ }
+ completedMsgs.clear();
+}
+
+
+/** cancel any pending calls to scheduleComplete */
+void SessionState::AsyncCommandCompleter::cancel()
+{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock);
+ session = 0;
+}
+
+
+/** inform the completer that the session has attached,
+ * allows command completion scheduling from any thread */
+void SessionState::AsyncCommandCompleter::attached()
+{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock);
+ isAttached = true;
+}
+
+
+/** inform the completer that the session has detached,
+ * disables command completion scheduling from any thread */
+void SessionState::AsyncCommandCompleter::detached()
+{
+ qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock);
+ isAttached = false;
+}
+
}} // namespace qpid::broker
diff --git a/cpp/src/qpid/broker/SessionState.h b/cpp/src/qpid/broker/SessionState.h
index be79eb0eab..506af85c47 100644
--- a/cpp/src/qpid/broker/SessionState.h
+++ b/cpp/src/qpid/broker/SessionState.h
@@ -30,13 +30,15 @@
#include "qmf/org/apache/qpid/broker/Session.h"
#include "qpid/broker/SessionAdapter.h"
#include "qpid/broker/DeliveryAdapter.h"
-#include "qpid/broker/IncompleteMessageList.h"
+#include "qpid/broker/AsyncCompletion.h"
#include "qpid/broker/MessageBuilder.h"
#include "qpid/broker/SessionContext.h"
#include "qpid/broker/SemanticState.h"
+#include "qpid/sys/Monitor.h"
#include <boost/noncopyable.hpp>
#include <boost/scoped_ptr.hpp>
+#include <boost/intrusive_ptr.hpp>
#include <set>
#include <vector>
@@ -123,6 +125,10 @@ class SessionState : public qpid::SessionState,
const SessionId& getSessionId() const { return getId(); }
+ // Used by ExecutionHandler sync command processing. Notifies
+ // the SessionState of a received Execution.Sync command.
+ void addPendingExecutionSync();
+
// Used to delay creation of management object for sessions
// belonging to inter-broker bridges
void addManagementObject();
@@ -130,7 +136,10 @@ class SessionState : public qpid::SessionState,
private:
void handleCommand(framing::AMQMethodBody* method, const framing::SequenceNumber& id);
void handleContent(framing::AMQFrame& frame, const framing::SequenceNumber& id);
- void enqueued(boost::intrusive_ptr<Message> msg);
+
+ // indicate that the given ingress msg has been completely received by the
+ // broker, and the msg's message.transfer command can be considered completed.
+ void completeRcvMsg(SequenceNumber id, bool requiresAccept, bool requiresSync);
void handleIn(framing::AMQFrame& frame);
void handleOut(framing::AMQFrame& frame);
@@ -156,8 +165,6 @@ class SessionState : public qpid::SessionState,
SemanticState semanticState;
SessionAdapter adapter;
MessageBuilder msgBuilder;
- IncompleteMessageList incomplete;
- IncompleteMessageList::CompletionListener enqueuedOp;
qmf::org::apache::qpid::broker::Session* mgmtObject;
qpid::framing::SequenceSet accepted;
@@ -166,6 +173,110 @@ class SessionState : public qpid::SessionState,
boost::scoped_ptr<RateFlowcontrol> rateFlowcontrol;
boost::intrusive_ptr<sys::TimerTask> flowControlTimer;
+ // sequence numbers for pending received Execution.Sync commands
+ std::queue<SequenceNumber> pendingExecutionSyncs;
+ bool currentCommandComplete;
+
+ /** This class provides a context for completing asynchronous commands in a thread
+ * safe manner. Asynchronous commands save their completion state in this class.
+ * This class then schedules the completeCommands() method in the IO thread.
+ * While running in the IO thread, completeCommands() may safely complete all
+ * saved commands without the risk of colliding with other operations on this
+ * SessionState.
+ */
+ class AsyncCommandCompleter : public RefCounted {
+ private:
+ SessionState *session;
+ bool isAttached;
+ qpid::sys::Mutex completerLock;
+
+ // special-case message.transfer commands for optimization
+ struct MessageInfo {
+ SequenceNumber cmd; // message.transfer command id
+ bool requiresAccept;
+ bool requiresSync;
+ MessageInfo(SequenceNumber c, bool a, bool s)
+ : cmd(c), requiresAccept(a), requiresSync(s) {}
+ };
+ std::vector<MessageInfo> completedMsgs;
+ // If an ingress message does not require a Sync, we need to
+ // hold a reference to it in case an Execution.Sync command is received and we
+ // have to manually flush the message.
+ std::map<SequenceNumber, boost::intrusive_ptr<Message> > pendingMsgs;
+
+ /** complete all pending commands, runs in IO thread */
+ void completeCommands();
+
+ /** for scheduling a run of "completeCommands()" on the IO thread */
+ static void schedule(boost::intrusive_ptr<AsyncCommandCompleter>);
+
+ public:
+ AsyncCommandCompleter(SessionState *s) : session(s), isAttached(s->isAttached()) {};
+ ~AsyncCommandCompleter() {};
+
+ /** track a message pending ingress completion */
+ void addPendingMessage(boost::intrusive_ptr<Message> m);
+ void deletePendingMessage(SequenceNumber id);
+ void flushPendingMessages();
+ /** schedule the processing of a completed ingress message.transfer command */
+ void scheduleMsgCompletion(SequenceNumber cmd,
+ bool requiresAccept,
+ bool requiresSync);
+ void cancel(); // called by SessionState destructor.
+ void attached(); // called by SessionState on attach()
+ void detached(); // called by SessionState on detach()
+ };
+ boost::intrusive_ptr<AsyncCommandCompleter> asyncCommandCompleter;
+
+ /** Abstract class that represents a single asynchronous command that is
+ * pending completion.
+ */
+ class AsyncCommandContext : public AsyncCompletion::Callback
+ {
+ public:
+ AsyncCommandContext( SessionState *ss, SequenceNumber _id )
+ : id(_id), completerContext(ss->asyncCommandCompleter) {}
+ virtual ~AsyncCommandContext() {}
+
+ protected:
+ SequenceNumber id;
+ boost::intrusive_ptr<AsyncCommandCompleter> completerContext;
+ };
+
+ /** incomplete Message.transfer commands - inbound to broker from client
+ */
+ class IncompleteIngressMsgXfer : public SessionState::AsyncCommandContext
+ {
+ public:
+ IncompleteIngressMsgXfer( SessionState *ss,
+ boost::intrusive_ptr<Message> m )
+ : AsyncCommandContext(ss, m->getCommandId()),
+ session(ss),
+ msg(m),
+ requiresAccept(m->requiresAccept()),
+ requiresSync(m->getFrames().getMethod()->isSync()),
+ pending(false) {}
+ IncompleteIngressMsgXfer( const IncompleteIngressMsgXfer& x )
+ : AsyncCommandContext(x.session, x.msg->getCommandId()),
+ session(x.session),
+ msg(x.msg),
+ requiresAccept(x.requiresAccept),
+ requiresSync(x.requiresSync),
+ pending(x.pending) {}
+
+ virtual ~IncompleteIngressMsgXfer() {};
+
+ virtual void completed(bool);
+ virtual boost::intrusive_ptr<AsyncCompletion::Callback> clone();
+
+ private:
+ SessionState *session; // only valid if sync flag in callback is true
+ boost::intrusive_ptr<Message> msg;
+ bool requiresAccept;
+ bool requiresSync;
+ bool pending; // true if msg saved on pending list...
+ };
+
friend class SessionManager;
};
diff --git a/cpp/src/qpid/broker/StatefulQueueObserver.h b/cpp/src/qpid/broker/StatefulQueueObserver.h
new file mode 100644
index 0000000000..c682d460b7
--- /dev/null
+++ b/cpp/src/qpid/broker/StatefulQueueObserver.h
@@ -0,0 +1,63 @@
+#ifndef QPID_BROKER_STATEFULQUEUEOBSERVER_H
+#define QPID_BROKER_STATEFULQUEUEOBSERVER_H
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "qpid/broker/QueueObserver.h"
+#include "qpid/framing/FieldTable.h"
+
+namespace qpid {
+namespace broker {
+
+/**
+ * Specialized type of QueueObserver that maintains internal state that has to
+ * be replicated across clustered brokers.
+ */
+class StatefulQueueObserver : public QueueObserver
+{
+ public:
+ StatefulQueueObserver(std::string _id) : id(_id) {}
+ virtual ~StatefulQueueObserver() {}
+
+ /** This identifier must uniquely identify this particular observer amoung
+ * all observers on a queue. For cluster replication, this id will be used
+ * to identify the peer queue observer for synchronization across
+ * brokers.
+ */
+ const std::string& getId() const { return id; }
+
+ /** This method should return the observer's internal state as an opaque
+ * map.
+ */
+ virtual void getState(qpid::framing::FieldTable& state ) const = 0;
+
+ /** The input map represents the internal state of the peer observer that
+ * this observer should synchonize to.
+ */
+ virtual void setState(const qpid::framing::FieldTable&) = 0;
+
+
+ private:
+ std::string id;
+};
+}} // namespace qpid::broker
+
+#endif /*!QPID_BROKER_STATEFULQUEUEOBSERVER_H*/
diff --git a/cpp/src/qpid/broker/ThresholdAlerts.cpp b/cpp/src/qpid/broker/ThresholdAlerts.cpp
index 4f35884af8..3c9e210d4d 100644
--- a/cpp/src/qpid/broker/ThresholdAlerts.cpp
+++ b/cpp/src/qpid/broker/ThresholdAlerts.cpp
@@ -28,6 +28,52 @@
namespace qpid {
namespace broker {
+namespace {
+const qmf::org::apache::qpid::broker::EventQueueThresholdExceeded EVENT("dummy", 0, 0);
+bool isQMFv2(const boost::intrusive_ptr<Message> message)
+{
+ const qpid::framing::MessageProperties* props = message->getProperties<qpid::framing::MessageProperties>();
+ return props && props->getAppId() == "qmf2";
+}
+
+bool isThresholdEvent(const boost::intrusive_ptr<Message> message)
+{
+ if (message->getIsManagementMessage()) {
+ //is this a qmf event? if so is it a threshold event?
+ if (isQMFv2(message)) {
+ const qpid::framing::FieldTable* headers = message->getApplicationHeaders();
+ if (headers && headers->getAsString("qmf.content") == "_event") {
+ //decode as list
+ std::string content = message->getFrames().getContent();
+ qpid::types::Variant::List list;
+ qpid::amqp_0_10::ListCodec::decode(content, list);
+ if (list.empty() || list.front().getType() != qpid::types::VAR_MAP) return false;
+ qpid::types::Variant::Map map = list.front().asMap();
+ try {
+ std::string eventName = map["_schema_id"].asMap()["_class_name"].asString();
+ return eventName == EVENT.getEventName();
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Error checking for recursive threshold alert: " << e.what());
+ }
+ }
+ } else {
+ std::string content = message->getFrames().getContent();
+ qpid::framing::Buffer buffer(const_cast<char*>(content.data()), content.size());
+ if (buffer.getOctet() == 'A' && buffer.getOctet() == 'M' && buffer.getOctet() == '2' && buffer.getOctet() == 'e') {
+ buffer.getLong();//sequence
+ std::string packageName;
+ buffer.getShortString(packageName);
+ if (packageName != EVENT.getPackageName()) return false;
+ std::string eventName;
+ buffer.getShortString(eventName);
+ return eventName == EVENT.getEventName();
+ }
+ }
+ }
+ return false;
+}
+}
+
ThresholdAlerts::ThresholdAlerts(const std::string& n,
qpid::management::ManagementAgent& a,
const uint32_t ct,
@@ -44,8 +90,14 @@ void ThresholdAlerts::enqueued(const QueuedMessage& m)
if ((countThreshold && count >= countThreshold) || (sizeThreshold && size >= sizeThreshold)) {
if ((repeatInterval == 0 && lastAlert == qpid::sys::EPOCH)
|| qpid::sys::Duration(lastAlert, qpid::sys::now()) > repeatInterval) {
- agent.raiseEvent(qmf::org::apache::qpid::broker::EventQueueThresholdExceeded(name, count, size));
+ //Note: Raising an event may result in messages being
+ //enqueued on queues; it may even be that this event
+ //causes a message to be enqueued on the queue we are
+ //tracking, and so we need to avoid recursing
+ if (isThresholdEvent(m.payload)) return;
lastAlert = qpid::sys::now();
+ agent.raiseEvent(qmf::org::apache::qpid::broker::EventQueueThresholdExceeded(name, count, size));
+ QPID_LOG(info, "Threshold event triggered for " << name << ", count=" << count << ", size=" << size);
}
}
}
@@ -75,12 +127,12 @@ void ThresholdAlerts::observe(Queue& queue, qpid::management::ManagementAgent& a
}
void ThresholdAlerts::observe(Queue& queue, qpid::management::ManagementAgent& agent,
- const qpid::framing::FieldTable& settings)
+ const qpid::framing::FieldTable& settings, uint16_t limitRatio)
{
qpid::types::Variant::Map map;
qpid::amqp_0_10::translate(settings, map);
- observe(queue, agent, map);
+ observe(queue, agent, map, limitRatio);
}
template <class T>
@@ -118,19 +170,19 @@ class Option
};
void ThresholdAlerts::observe(Queue& queue, qpid::management::ManagementAgent& agent,
- const qpid::types::Variant::Map& settings)
+ const qpid::types::Variant::Map& settings, uint16_t limitRatio)
{
//Note: aliases are keys defined by java broker
Option<int64_t> repeatInterval("qpid.alert_repeat_gap", 60);
repeatInterval.addAlias("x-qpid-minimum-alert-repeat-gap");
- //If no explicit threshold settings were given use 80% of any
- //limit from the policy.
+ //If no explicit threshold settings were given use specified
+ //percentage of any limit from the policy.
const QueuePolicy* policy = queue.getPolicy();
- Option<uint32_t> countThreshold("qpid.alert_count", (uint32_t) (policy ? policy->getMaxCount()*0.8 : 0));
+ Option<uint32_t> countThreshold("qpid.alert_count", (uint32_t) (policy && limitRatio ? (policy->getMaxCount()*limitRatio/100) : 0));
countThreshold.addAlias("x-qpid-maximum-message-count");
- Option<uint64_t> sizeThreshold("qpid.alert_size", (uint64_t) (policy ? policy->getMaxSize()*0.8 : 0));
+ Option<uint64_t> sizeThreshold("qpid.alert_size", (uint64_t) (policy && limitRatio ? (policy->getMaxSize()*limitRatio/100) : 0));
sizeThreshold.addAlias("x-qpid-maximum-message-size");
observe(queue, agent, countThreshold.get(settings), sizeThreshold.get(settings), repeatInterval.get(settings));
diff --git a/cpp/src/qpid/broker/ThresholdAlerts.h b/cpp/src/qpid/broker/ThresholdAlerts.h
index e1f59252c4..2b4a46b736 100644
--- a/cpp/src/qpid/broker/ThresholdAlerts.h
+++ b/cpp/src/qpid/broker/ThresholdAlerts.h
@@ -50,14 +50,17 @@ class ThresholdAlerts : public QueueObserver
const long repeatInterval);
void enqueued(const QueuedMessage&);
void dequeued(const QueuedMessage&);
+ void acquired(const QueuedMessage&) {};
+ void requeued(const QueuedMessage&) {};
+
static void observe(Queue& queue, qpid::management::ManagementAgent& agent,
const uint64_t countThreshold,
const uint64_t sizeThreshold,
const long repeatInterval);
static void observe(Queue& queue, qpid::management::ManagementAgent& agent,
- const qpid::framing::FieldTable& settings);
+ const qpid::framing::FieldTable& settings, uint16_t limitRatio);
static void observe(Queue& queue, qpid::management::ManagementAgent& agent,
- const qpid::types::Variant::Map& settings);
+ const qpid::types::Variant::Map& settings, uint16_t limitRatio);
private:
const std::string name;
qpid::management::ManagementAgent& agent;
diff --git a/cpp/src/qpid/broker/TopicExchange.cpp b/cpp/src/qpid/broker/TopicExchange.cpp
index 1b0fe71bcf..644a3d628e 100644
--- a/cpp/src/qpid/broker/TopicExchange.cpp
+++ b/cpp/src/qpid/broker/TopicExchange.cpp
@@ -221,6 +221,7 @@ TopicExchange::TopicExchange(const std::string& _name, bool _durable,
bool TopicExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* args)
{
+ ClearCache cc(&cacheLock,&bindingCache); // clear the cache on function exit.
string fedOp(args ? args->getAsString(qpidFedOp) : fedOpBind);
string fedTags(args ? args->getAsString(qpidFedTags) : "");
string fedOrigin(args ? args->getAsString(qpidFedOrigin) : "");
@@ -249,21 +250,21 @@ bool TopicExchange::bind(Queue::shared_ptr queue, const string& routingKey, cons
if (mgmtExchange != 0) {
mgmtExchange->inc_bindingCount();
}
- QPID_LOG(debug, "Bound key [" << routingPattern << "] to queue " << queue->getName()
- << " (origin=" << fedOrigin << ")");
+ QPID_LOG(debug, "Binding key [" << routingPattern << "] to queue " << queue->getName()
+ << " on exchange " << getName() << " (origin=" << fedOrigin << ")");
}
} else if (fedOp == fedOpUnbind) {
- bool reallyUnbind = false;
- {
- RWlock::ScopedWlock l(lock);
- BindingKey* bk = bindingTree.getBindingKey(routingPattern);
- if (bk) {
- propagate = bk->fedBinding.delOrigin(queue->getName(), fedOrigin);
- reallyUnbind = bk->fedBinding.countFedBindings(queue->getName()) == 0;
+ RWlock::ScopedWlock l(lock);
+ BindingKey* bk = getQueueBinding(queue, routingPattern);
+ if (bk) {
+ QPID_LOG(debug, "FedOpUnbind [" << routingPattern << "] from exchange " << getName()
+ << " on queue=" << queue->getName() << " origin=" << fedOrigin);
+ propagate = bk->fedBinding.delOrigin(queue->getName(), fedOrigin);
+ // if this was the last binding for the queue, delete the binding
+ if (bk->fedBinding.countFedBindings(queue->getName()) == 0) {
+ deleteBinding(queue, routingPattern, bk);
}
}
- if (reallyUnbind)
- unbind(queue, routingPattern, 0);
} else if (fedOp == fedOpReorigin) {
/** gather up all the keys that need rebinding in a local vector
* while holding the lock. Then propagate once the lock is
@@ -281,20 +282,38 @@ bool TopicExchange::bind(Queue::shared_ptr queue, const string& routingKey, cons
}
}
+ cc.clearCache(); // clear the cache before we IVE route.
routeIVE();
if (propagate)
propagateFedOp(routingKey, fedTags, fedOp, fedOrigin);
return true;
}
-bool TopicExchange::unbind(Queue::shared_ptr queue, const string& constRoutingKey, const FieldTable* /*args*/){
+bool TopicExchange::unbind(Queue::shared_ptr queue, const string& constRoutingKey, const FieldTable* args)
+{
+ string fedOrigin(args ? args->getAsString(qpidFedOrigin) : "");
+ QPID_LOG(debug, "Unbinding key [" << constRoutingKey << "] from queue " << queue->getName()
+ << " on exchange " << getName() << " origin=" << fedOrigin << ")" );
+
+ ClearCache cc(&cacheLock,&bindingCache); // clear the cache on function exit.
RWlock::ScopedWlock l(lock);
string routingKey = normalize(constRoutingKey);
- BindingKey* bk = bindingTree.getBindingKey(routingKey);
+ BindingKey* bk = getQueueBinding(queue, routingKey);
if (!bk) return false;
- Binding::vector& qv(bk->bindingVector);
- bool propagate = false;
+ bool propagate = bk->fedBinding.delOrigin(queue->getName(), fedOrigin);
+ deleteBinding(queue, routingKey, bk);
+ if (propagate)
+ propagateFedOp(routingKey, string(), fedOpUnbind, string());
+ return true;
+}
+
+bool TopicExchange::deleteBinding(Queue::shared_ptr queue,
+ const std::string& routingKey,
+ BindingKey *bk)
+{
+ // Note well: write lock held by caller
+ Binding::vector& qv(bk->bindingVector);
Binding::vector::iterator q;
for (q = qv.begin(); q != qv.end(); q++)
if ((*q)->queue == queue)
@@ -303,42 +322,55 @@ bool TopicExchange::unbind(Queue::shared_ptr queue, const string& constRoutingKe
qv.erase(q);
assert(nBindings > 0);
nBindings--;
- propagate = bk->fedBinding.delOrigin();
+
if(qv.empty()) {
bindingTree.removeBindingKey(routingKey);
}
if (mgmtExchange != 0) {
mgmtExchange->dec_bindingCount();
}
- QPID_LOG(debug, "Unbound [" << routingKey << "] from queue " << queue->getName());
-
- if (propagate)
- propagateFedOp(routingKey, string(), fedOpUnbind, string());
+ QPID_LOG(debug, "Unbound key [" << routingKey << "] from queue " << queue->getName()
+ << " on exchange " << getName());
return true;
}
-bool TopicExchange::isBound(Queue::shared_ptr queue, const string& pattern)
+/** returns a pointer to the BindingKey if the given queue is bound to this
+ * exchange using the routing pattern. 0 if queue binding does not exist.
+ */
+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
- if (!bk) return false;
+ if (!bk) return 0;
Binding::vector& qv(bk->bindingVector);
Binding::vector::iterator q;
for (q = qv.begin(); q != qv.end(); q++)
if ((*q)->queue == queue)
break;
- return q != qv.end();
+ return (q != qv.end()) ? bk : 0;
}
void TopicExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/)
{
// Note: PERFORMANCE CRITICAL!!!
- BindingList b(new std::vector<boost::shared_ptr<qpid::broker::Exchange::Binding> >);
+ BindingList b;
+ std::map<std::string, BindingList>::iterator it;
+ { // only lock the cache for read
+ RWlock::ScopedRlock cl(cacheLock);
+ it = bindingCache.find(routingKey);
+ if (it != bindingCache.end()) {
+ b = it->second;
+ }
+ }
PreRoute pr(msg, this);
- BindingsFinderIter bindingsFinder(b);
+ if (!b.get()) // no cache hit
{
RWlock::ScopedRlock l(lock);
+ b = BindingList(new std::vector<boost::shared_ptr<qpid::broker::Exchange::Binding> >);
+ BindingsFinderIter bindingsFinder(b);
bindingTree.iterateMatch(routingKey, bindingsFinder);
+ RWlock::ScopedWlock cwl(cacheLock);
+ bindingCache[routingKey] = b; // update cache
}
doRoute(msg, b);
}
@@ -348,7 +380,7 @@ bool TopicExchange::isBound(Queue::shared_ptr queue, const string* const routing
RWlock::ScopedRlock l(lock);
if (routingKey && queue) {
string key(normalize(*routingKey));
- return isBound(queue, key);
+ return getQueueBinding(queue, key) != 0;
} else if (!routingKey && !queue) {
return nBindings > 0;
} else if (routingKey) {
diff --git a/cpp/src/qpid/broker/TopicExchange.h b/cpp/src/qpid/broker/TopicExchange.h
index a6c457dcb3..636918f8a1 100644
--- a/cpp/src/qpid/broker/TopicExchange.h
+++ b/cpp/src/qpid/broker/TopicExchange.h
@@ -56,7 +56,7 @@ class TopicExchange : public virtual Exchange {
// | +-->d-->...
// +-->x-->y-->...
//
- class BindingNode {
+ class QPID_BROKER_CLASS_EXTERN BindingNode {
public:
typedef boost::shared_ptr<BindingNode> shared_ptr;
@@ -135,8 +135,31 @@ class TopicExchange : public virtual Exchange {
BindingNode bindingTree;
unsigned long nBindings;
qpid::sys::RWlock lock; // protects bindingTree and nBindings
-
- bool isBound(Queue::shared_ptr queue, const std::string& pattern);
+ 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;
+ public:
+ 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;
+ }
+ };
+ ~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;
diff --git a/cpp/src/qpid/broker/TxBuffer.cpp b/cpp/src/qpid/broker/TxBuffer.cpp
index b509778e89..d92e6ace48 100644
--- a/cpp/src/qpid/broker/TxBuffer.cpp
+++ b/cpp/src/qpid/broker/TxBuffer.cpp
@@ -76,5 +76,5 @@ bool TxBuffer::commitLocal(TransactionalStore* const store)
}
void TxBuffer::accept(TxOpConstVisitor& v) const {
- std::for_each(ops.begin(), ops.end(), boost::bind(&TxOp::accept, _1, boost::ref(v)));
+ std::for_each(ops.begin(), ops.end(), boost::bind(&TxOp::accept, _1, boost::ref(v)));
}
diff --git a/cpp/src/qpid/broker/TxPublish.cpp b/cpp/src/qpid/broker/TxPublish.cpp
index 36a451e62c..9c2cf4a467 100644
--- a/cpp/src/qpid/broker/TxPublish.cpp
+++ b/cpp/src/qpid/broker/TxPublish.cpp
@@ -90,14 +90,7 @@ void TxPublish::deliverTo(const boost::shared_ptr<Queue>& queue){
void TxPublish::prepare(TransactionContext* ctxt, const boost::shared_ptr<Queue> queue)
{
- if (!queue->enqueue(ctxt, msg)){
- /**
- * if not store then mark message for ack and deleivery once
- * commit happens, as async IO will never set it when no store
- * exists
- */
- msg->enqueueComplete();
- }
+ queue->enqueue(ctxt, msg);
}
TxPublish::Commit::Commit(intrusive_ptr<Message>& _msg) : msg(_msg){}
diff --git a/cpp/src/qpid/broker/TxPublish.h b/cpp/src/qpid/broker/TxPublish.h
index effa585676..dba7878af2 100644
--- a/cpp/src/qpid/broker/TxPublish.h
+++ b/cpp/src/qpid/broker/TxPublish.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
@@ -34,57 +34,58 @@
#include <boost/intrusive_ptr.hpp>
namespace qpid {
- namespace broker {
- /**
- * Defines the behaviour for publish operations on a
- * transactional channel. Messages are routed through
- * exchanges when received but are not at that stage delivered
- * to the matching queues, rather the queues are held in an
- * instance of this class. On prepare() the message is marked
- * enqueued to the relevant queues in the MessagesStore. On
- * commit() the messages will be passed to the queue for
- * dispatch or to be added to the in-memory queue.
- */
- class TxPublish : public TxOp, public Deliverable{
+namespace broker {
+/**
+ * Defines the behaviour for publish operations on a
+ * transactional channel. Messages are routed through
+ * exchanges when received but are not at that stage delivered
+ * to the matching queues, rather the queues are held in an
+ * instance of this class. On prepare() the message is marked
+ * enqueued to the relevant queues in the MessagesStore. On
+ * commit() the messages will be passed to the queue for
+ * dispatch or to be added to the in-memory queue.
+ */
+class QPID_BROKER_CLASS_EXTERN TxPublish : public TxOp, public Deliverable{
- class Commit{
- boost::intrusive_ptr<Message>& msg;
- public:
- Commit(boost::intrusive_ptr<Message>& msg);
- void operator()(const boost::shared_ptr<Queue>& queue);
- };
- class Rollback{
- boost::intrusive_ptr<Message>& msg;
- public:
- Rollback(boost::intrusive_ptr<Message>& msg);
- void operator()(const boost::shared_ptr<Queue>& queue);
- };
+ class Commit{
+ boost::intrusive_ptr<Message>& msg;
+ public:
+ Commit(boost::intrusive_ptr<Message>& msg);
+ void operator()(const boost::shared_ptr<Queue>& queue);
+ };
+ class Rollback{
+ boost::intrusive_ptr<Message>& msg;
+ public:
+ Rollback(boost::intrusive_ptr<Message>& msg);
+ void operator()(const boost::shared_ptr<Queue>& queue);
+ };
- boost::intrusive_ptr<Message> msg;
- std::list<boost::shared_ptr<Queue> > queues;
- std::list<boost::shared_ptr<Queue> > prepared;
+ boost::intrusive_ptr<Message> msg;
+ std::list<boost::shared_ptr<Queue> > queues;
+ std::list<boost::shared_ptr<Queue> > prepared;
- void prepare(TransactionContext* ctxt, boost::shared_ptr<Queue>);
+ void prepare(TransactionContext* ctxt, boost::shared_ptr<Queue>);
- public:
- QPID_BROKER_EXTERN TxPublish(boost::intrusive_ptr<Message> msg);
- QPID_BROKER_EXTERN virtual bool prepare(TransactionContext* ctxt) throw();
- QPID_BROKER_EXTERN virtual void commit() throw();
- QPID_BROKER_EXTERN virtual void rollback() throw();
+ public:
+ QPID_BROKER_EXTERN TxPublish(boost::intrusive_ptr<Message> msg);
+ QPID_BROKER_EXTERN virtual bool prepare(TransactionContext* ctxt) throw();
+ QPID_BROKER_EXTERN virtual void commit() throw();
+ QPID_BROKER_EXTERN virtual void rollback() throw();
- virtual Message& getMessage() { return *msg; };
-
- QPID_BROKER_EXTERN virtual void deliverTo(const boost::shared_ptr<Queue>& queue);
+ virtual Message& getMessage() { return *msg; };
- virtual ~TxPublish(){}
- virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); }
+ QPID_BROKER_EXTERN virtual void deliverTo(const boost::shared_ptr<Queue>& queue);
- QPID_BROKER_EXTERN uint64_t contentSize();
+ virtual ~TxPublish(){}
+ virtual void accept(TxOpConstVisitor& visitor) const { visitor(*this); }
- boost::intrusive_ptr<Message> getMessage() const { return msg; }
- const std::list<boost::shared_ptr<Queue> > getQueues() const { return queues; }
- };
- }
+ QPID_BROKER_EXTERN uint64_t contentSize();
+
+ boost::intrusive_ptr<Message> getMessage() const { return msg; }
+ const std::list<boost::shared_ptr<Queue> >& getQueues() const { return queues; }
+ const std::list<boost::shared_ptr<Queue> >& getPrepared() const { return prepared; }
+};
+}
}
diff --git a/cpp/src/qpid/broker/windows/BrokerDefaults.cpp b/cpp/src/qpid/broker/windows/BrokerDefaults.cpp
index b6862f0418..b65440b5ad 100644
--- a/cpp/src/qpid/broker/windows/BrokerDefaults.cpp
+++ b/cpp/src/qpid/broker/windows/BrokerDefaults.cpp
@@ -31,10 +31,16 @@ const std::string Broker::Options::DEFAULT_DATA_DIR_NAME("\\QPIDD.DATA");
std::string
Broker::Options::getHome() {
std::string home;
+#ifdef _MSC_VER
char home_c[MAX_PATH+1];
size_t unused;
if (0 == getenv_s (&unused, home_c, sizeof(home_c), "HOME"))
home += home_c;
+#else
+ char *home_c = getenv("HOME");
+ if (home_c)
+ home += home_c;
+#endif
return home;
}
diff --git a/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp b/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp
index 608a8f7dae..2acc09cded 100644
--- a/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp
+++ b/cpp/src/qpid/broker/windows/SaslAuthenticator.cpp
@@ -42,7 +42,7 @@ public:
NullAuthenticator(Connection& connection);
~NullAuthenticator();
void getMechanisms(framing::Array& mechanisms);
- void start(const std::string& mechanism, const std::string& response);
+ void start(const std::string& mechanism, const std::string* response);
void step(const std::string&) {}
std::auto_ptr<SecurityLayer> getSecurityLayer(uint16_t maxFrameSize);
};
@@ -57,7 +57,7 @@ public:
SspiAuthenticator(Connection& connection);
~SspiAuthenticator();
void getMechanisms(framing::Array& mechanisms);
- void start(const std::string& mechanism, const std::string& response);
+ void start(const std::string& mechanism, const std::string* response);
void step(const std::string& response);
std::auto_ptr<SecurityLayer> getSecurityLayer(uint16_t maxFrameSize);
};
@@ -93,14 +93,15 @@ NullAuthenticator::~NullAuthenticator() {}
void NullAuthenticator::getMechanisms(Array& mechanisms)
{
mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value("ANONYMOUS")));
+ mechanisms.add(boost::shared_ptr<FieldValue>(new Str16Value("PLAIN")));
}
-void NullAuthenticator::start(const string& mechanism, const string& response)
+void NullAuthenticator::start(const string& mechanism, const string* response)
{
QPID_LOG(warning, "SASL: No Authentication Performed");
if (mechanism == "PLAIN") { // Old behavior
- if (response.size() > 0 && response[0] == (char) 0) {
- string temp = response.substr(1);
+ if (response && response->size() > 0 && (*response).c_str()[0] == (char) 0) {
+ string temp = response->substr(1);
string::size_type i = temp.find((char)0);
string uid = temp.substr(0, i);
string pwd = temp.substr(i + 1);
@@ -138,7 +139,7 @@ void SspiAuthenticator::getMechanisms(Array& mechanisms)
QPID_LOG(info, "SASL: Mechanism list: ANONYMOUS PLAIN");
}
-void SspiAuthenticator::start(const string& mechanism, const string& response)
+void SspiAuthenticator::start(const string& mechanism, const string* response)
{
QPID_LOG(info, "SASL: Starting authentication with mechanism: " << mechanism);
if (mechanism == "ANONYMOUS") {
@@ -151,16 +152,19 @@ void SspiAuthenticator::start(const string& mechanism, const string& response)
// PLAIN's response is composed of 3 strings separated by 0 bytes:
// authorization id, authentication id (user), clear-text password.
- if (response.size() == 0)
+ if (!response || response->size() == 0)
throw ConnectionForcedException("Authentication failed");
- string::size_type i = response.find((char)0);
- string auth = response.substr(0, i);
- string::size_type j = response.find((char)0, i+1);
- string uid = response.substr(i+1, j-1);
- string pwd = response.substr(j+1);
+ string::size_type i = response->find((char)0);
+ string auth = response->substr(0, i);
+ string::size_type j = response->find((char)0, i+1);
+ string uid = response->substr(i+1, j-1);
+ string pwd = response->substr(j+1);
+ string dot(".");
int error = 0;
- if (!LogonUser(uid.c_str(), ".", pwd.c_str(),
+ if (!LogonUser(const_cast<char*>(uid.c_str()),
+ const_cast<char*>(dot.c_str()),
+ const_cast<char*>(pwd.c_str()),
LOGON32_LOGON_NETWORK,
LOGON32_PROVIDER_DEFAULT,
&userToken))
@@ -176,7 +180,7 @@ void SspiAuthenticator::start(const string& mechanism, const string& response)
client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, 0);
}
-void SspiAuthenticator::step(const string& response)
+void SspiAuthenticator::step(const string& /*response*/)
{
QPID_LOG(info, "SASL: Need another step!!!");
}
diff --git a/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp b/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp
index fd0e537192..1dff1ddc8f 100644
--- a/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp
+++ b/cpp/src/qpid/broker/windows/SslProtocolFactory.cpp
@@ -27,10 +27,14 @@
#include "qpid/sys/AsynchIOHandler.h"
#include "qpid/sys/ConnectionCodec.h"
#include "qpid/sys/Socket.h"
+#include "qpid/sys/SocketAddress.h"
#include "qpid/sys/SystemInfo.h"
#include "qpid/sys/windows/SslAsynchIO.h"
+
#include <boost/bind.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
#include <memory>
+
// security.h needs to see this to distinguish from kernel use.
#define SECURITY_WIN32
#include <security.h>
@@ -68,9 +72,10 @@ struct SslServerOptions : qpid::Options
};
class SslProtocolFactory : public qpid::sys::ProtocolFactory {
- qpid::sys::Socket listener;
const bool tcpNoDelay;
- const uint16_t listeningPort;
+ boost::ptr_vector<Socket> listeners;
+ boost::ptr_vector<AsynchAcceptor> acceptors;
+ uint16_t listeningPort;
std::string brokerHost;
const bool clientAuthSelected;
std::auto_ptr<qpid::sys::AsynchAcceptor> acceptor;
@@ -78,15 +83,14 @@ class SslProtocolFactory : public qpid::sys::ProtocolFactory {
CredHandle credHandle;
public:
- SslProtocolFactory(const SslServerOptions&, int backlog, bool nodelay);
+ SslProtocolFactory(const SslServerOptions&, const std::string& host, const std::string& port, int backlog, bool nodelay);
~SslProtocolFactory();
void accept(sys::Poller::shared_ptr, sys::ConnectionCodec::Factory*);
- void connect(sys::Poller::shared_ptr, const std::string& host, int16_t port,
+ void connect(sys::Poller::shared_ptr, const std::string& host, const std::string& port,
sys::ConnectionCodec::Factory*,
ConnectFailedCallback failed);
uint16_t getPort() const;
- std::string getHost() const;
bool supports(const std::string& capability);
private:
@@ -115,6 +119,7 @@ static struct SslPlugin : public Plugin {
try {
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));
QPID_LOG(notice, "Listening for SSL connections on TCP port " << protocol->getPort());
@@ -127,12 +132,13 @@ static struct SslPlugin : public Plugin {
} sslPlugin;
SslProtocolFactory::SslProtocolFactory(const SslServerOptions& options,
- int backlog,
+ const std::string& host, const std::string& port, int backlog,
bool nodelay)
: tcpNoDelay(nodelay),
- listeningPort(listener.listen(options.port, backlog)),
clientAuthSelected(options.clientAuth) {
+ // Make sure that certificate store is good before listening to sockets
+ // to avoid having open and listening sockets when there is no cert store
SecInvalidateHandle(&credHandle);
// Get the certificate for this server.
@@ -177,6 +183,23 @@ SslProtocolFactory::SslProtocolFactory(const SslServerOptions& options,
throw QPID_WINDOWS_ERROR(status);
::CertFreeCertificateContext(certContext);
::CertCloseStore(certStoreHandle, 0);
+
+ // Listen to socket(s)
+ SocketAddress sa(host, port);
+
+ // We must have at least one resolved address
+ QPID_LOG(info, "SSL Listening to: " << sa.asString())
+ Socket* s = new Socket;
+ listeningPort = s->listen(sa, backlog);
+ listeners.push_back(s);
+
+ // Try any other resolved addresses
+ while (sa.nextAddress()) {
+ QPID_LOG(info, "SSL Listening to: " << sa.asString())
+ Socket* s = new Socket;
+ s->listen(sa, backlog);
+ listeners.push_back(s);
+ }
}
SslProtocolFactory::~SslProtocolFactory() {
@@ -237,21 +260,19 @@ uint16_t SslProtocolFactory::getPort() const {
return listeningPort; // Immutable no need for lock.
}
-std::string SslProtocolFactory::getHost() const {
- return listener.getSockname();
-}
-
void SslProtocolFactory::accept(sys::Poller::shared_ptr poller,
sys::ConnectionCodec::Factory* fact) {
- acceptor.reset(
- AsynchAcceptor::create(listener,
- boost::bind(&SslProtocolFactory::established, this, poller, _1, fact, false)));
- acceptor->start(poller);
+ for (unsigned i = 0; i<listeners.size(); ++i) {
+ acceptors.push_back(
+ AsynchAcceptor::create(listeners[i],
+ boost::bind(&SslProtocolFactory::established, this, poller, _1, fact, false)));
+ acceptors[i].start(poller);
+ }
}
void SslProtocolFactory::connect(sys::Poller::shared_ptr poller,
const std::string& host,
- int16_t port,
+ const std::string& port,
sys::ConnectionCodec::Factory* fact,
ConnectFailedCallback failed)
{
diff --git a/cpp/src/qpid/client/ConnectionHandler.cpp b/cpp/src/qpid/client/ConnectionHandler.cpp
index 8dc1e8338a..ab0d8e0700 100644
--- a/cpp/src/qpid/client/ConnectionHandler.cpp
+++ b/cpp/src/qpid/client/ConnectionHandler.cpp
@@ -22,6 +22,7 @@
#include "qpid/client/ConnectionHandler.h"
#include "qpid/SaslFactory.h"
+#include "qpid/StringUtils.h"
#include "qpid/client/Bounds.h"
#include "qpid/framing/amqp_framing.h"
#include "qpid/framing/all_method_bodies.h"
@@ -142,7 +143,9 @@ void ConnectionHandler::outgoing(AMQFrame& frame)
void ConnectionHandler::waitForOpen()
{
waitFor(ESTABLISHED);
- if (getState() == FAILED || getState() == CLOSED) {
+ if (getState() == FAILED) {
+ throw TransportFailure(errorText);
+ } else if (getState() == CLOSED) {
throw ConnectionException(errorCode, errorText);
}
}
@@ -202,6 +205,24 @@ void ConnectionHandler::fail(const std::string& message)
namespace {
std::string SPACE(" ");
+
+std::string join(const std::vector<std::string>& in)
+{
+ std::string result;
+ for (std::vector<std::string>::const_iterator i = in.begin(); i != in.end(); ++i) {
+ if (result.size()) result += SPACE;
+ result += *i;
+ }
+ return result;
+}
+
+void intersection(const std::vector<std::string>& a, const std::vector<std::string>& b, std::vector<std::string>& results)
+{
+ for (std::vector<std::string>::const_iterator i = a.begin(); i != a.end(); ++i) {
+ if (std::find(b.begin(), b.end(), *i) != b.end()) results.push_back(*i);
+ }
+}
+
}
void ConnectionHandler::start(const FieldTable& /*serverProps*/, const Array& mechanisms, const Array& /*locales*/)
@@ -216,26 +237,35 @@ void ConnectionHandler::start(const FieldTable& /*serverProps*/, const Array& me
maxSsf
);
- std::string mechlist;
- bool chosenMechanismSupported = mechanism.empty();
- for (Array::const_iterator i = mechanisms.begin(); i != mechanisms.end(); ++i) {
- if (!mechanism.empty() && mechanism == (*i)->get<std::string>()) {
- chosenMechanismSupported = true;
- mechlist = (*i)->get<std::string>() + SPACE + mechlist;
- } else {
- if (i != mechanisms.begin()) mechlist += SPACE;
- mechlist += (*i)->get<std::string>();
+ std::vector<std::string> mechlist;
+ if (mechanism.empty()) {
+ //mechlist is simply what the server offers
+ mechanisms.collect(mechlist);
+ } else {
+ //mechlist is the intersection of those indicated by user and
+ //those supported by server, in the order listed by user
+ std::vector<std::string> allowed = split(mechanism, " ");
+ std::vector<std::string> supported;
+ mechanisms.collect(supported);
+ intersection(allowed, supported, mechlist);
+ if (mechlist.empty()) {
+ throw Exception(QPID_MSG("Desired mechanism(s) not valid: " << mechanism << " (supported: " << join(supported) << ")"));
}
}
- if (!chosenMechanismSupported) {
- fail("Selected mechanism not supported: " + mechanism);
- }
-
if (sasl.get()) {
- string response = sasl->start(mechanism.empty() ? mechlist : mechanism,
- getSecuritySettings ? getSecuritySettings() : 0);
- proxy.startOk(properties, sasl->getMechanism(), response, locale);
+ string response;
+ if (sasl->start(join(mechlist), response, getSecuritySettings ? getSecuritySettings() : 0)) {
+ proxy.startOk(properties, sasl->getMechanism(), response, locale);
+ } else {
+ //response was null
+ ConnectionStartOkBody body;
+ body.setClientProperties(properties);
+ body.setMechanism(sasl->getMechanism());
+ //Don't set response, as none was given
+ body.setLocale(locale);
+ proxy.send(body);
+ }
} else {
//TODO: verify that desired mechanism and locale are supported
string response = ((char)0) + username + ((char)0) + password;
diff --git a/cpp/src/qpid/client/ConnectionImpl.cpp b/cpp/src/qpid/client/ConnectionImpl.cpp
index 40c004f166..db97f1e0f4 100644
--- a/cpp/src/qpid/client/ConnectionImpl.cpp
+++ b/cpp/src/qpid/client/ConnectionImpl.cpp
@@ -36,6 +36,7 @@
#include <boost/bind.hpp>
#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
#include <boost/shared_ptr.hpp>
#include <limits>
@@ -258,16 +259,16 @@ void ConnectionImpl::open()
connector->setInputHandler(&handler);
connector->setShutdownHandler(this);
try {
- connector->connect(host, port);
-
+ std::string p = boost::lexical_cast<std::string>(port);
+ connector->connect(host, p);
+
} catch (const std::exception& e) {
QPID_LOG(debug, "Failed to connect to " << protocol << ":" << host << ":" << port << " " << e.what());
connector.reset();
- throw;
+ throw TransportFailure(e.what());
}
connector->init();
- QPID_LOG(info, *this << " connected to " << protocol << ":" << host << ":" << port);
-
+
// Enable heartbeat if requested
uint16_t heartbeat = static_cast<ConnectionSettings&>(handler).heartbeat;
if (heartbeat) {
@@ -281,6 +282,7 @@ void ConnectionImpl::open()
// - in that case in connector.reset() above;
// - or when we are deleted
handler.waitForOpen();
+ QPID_LOG(info, *this << " connected to " << protocol << ":" << host << ":" << port);
// If the SASL layer has provided an "operational" userId for the connection,
// put it in the negotiated settings.
diff --git a/cpp/src/qpid/client/Connector.h b/cpp/src/qpid/client/Connector.h
index 586012f9d6..bc611ffe0d 100644
--- a/cpp/src/qpid/client/Connector.h
+++ b/cpp/src/qpid/client/Connector.h
@@ -61,7 +61,7 @@ class Connector : public framing::OutputHandler
static void registerFactory(const std::string& proto, Factory* connectorFactory);
virtual ~Connector() {};
- virtual void connect(const std::string& host, int port) = 0;
+ virtual void connect(const std::string& host, const std::string& port) = 0;
virtual void init() {};
virtual void close() = 0;
virtual void send(framing::AMQFrame& frame) = 0;
diff --git a/cpp/src/qpid/client/RdmaConnector.cpp b/cpp/src/qpid/client/RdmaConnector.cpp
index 6af607198c..664640f5e7 100644
--- a/cpp/src/qpid/client/RdmaConnector.cpp
+++ b/cpp/src/qpid/client/RdmaConnector.cpp
@@ -95,7 +95,7 @@ class RdmaConnector : public Connector, public sys::Codec
std::string identifier;
- void connect(const std::string& host, int port);
+ void connect(const std::string& host, const std::string& port);
void close();
void send(framing::AMQFrame& frame);
void abort() {} // TODO: need to fix this for heartbeat timeouts to work
@@ -173,7 +173,7 @@ RdmaConnector::~RdmaConnector() {
}
}
-void RdmaConnector::connect(const std::string& host, int port){
+void RdmaConnector::connect(const std::string& host, const std::string& port){
Mutex::ScopedLock l(dataConnectedLock);
assert(!dataConnected);
@@ -184,7 +184,7 @@ void RdmaConnector::connect(const std::string& host, int port){
boost::bind(&RdmaConnector::disconnected, this),
boost::bind(&RdmaConnector::rejected, this, poller, _1, _2));
- SocketAddress sa(host, boost::lexical_cast<std::string>(port));
+ SocketAddress sa(host, port);
acon->start(poller, sa);
}
diff --git a/cpp/src/qpid/client/SessionImpl.cpp b/cpp/src/qpid/client/SessionImpl.cpp
index b507625b11..7cf4ef648e 100644
--- a/cpp/src/qpid/client/SessionImpl.cpp
+++ b/cpp/src/qpid/client/SessionImpl.cpp
@@ -170,6 +170,7 @@ Demux& SessionImpl::getDemux()
void SessionImpl::waitForCompletion(const SequenceNumber& id)
{
Lock l(state);
+ sys::Waitable::ScopedWait w(state);
waitForCompletionImpl(id);
}
diff --git a/cpp/src/qpid/client/SslConnector.cpp b/cpp/src/qpid/client/SslConnector.cpp
index 35c7e6bdf6..26c2335eda 100644
--- a/cpp/src/qpid/client/SslConnector.cpp
+++ b/cpp/src/qpid/client/SslConnector.cpp
@@ -114,7 +114,7 @@ class SslConnector : public Connector
std::string identifier;
- void connect(const std::string& host, int port);
+ void connect(const std::string& host, const std::string& port);
void init();
void close();
void send(framing::AMQFrame& frame);
@@ -190,14 +190,14 @@ SslConnector::~SslConnector() {
close();
}
-void SslConnector::connect(const std::string& host, int port){
+void SslConnector::connect(const std::string& host, const std::string& port){
Mutex::ScopedLock l(closedLock);
assert(closed);
try {
socket.connect(host, port);
} catch (const std::exception& e) {
socket.close();
- throw ConnectionException(framing::connection::CLOSE_CODE_FRAMING_ERROR, e.what());
+ throw TransportFailure(e.what());
}
identifier = str(format("[%1% %2%]") % socket.getLocalPort() % socket.getPeerAddress());
diff --git a/cpp/src/qpid/client/TCPConnector.cpp b/cpp/src/qpid/client/TCPConnector.cpp
index e284d57bec..0070b24ec0 100644
--- a/cpp/src/qpid/client/TCPConnector.cpp
+++ b/cpp/src/qpid/client/TCPConnector.cpp
@@ -88,7 +88,7 @@ TCPConnector::~TCPConnector() {
close();
}
-void TCPConnector::connect(const std::string& host, int port) {
+void TCPConnector::connect(const std::string& host, const std::string& port) {
Mutex::ScopedLock l(lock);
assert(closed);
connector = AsynchConnector::create(
@@ -117,11 +117,11 @@ void TCPConnector::connected(const Socket&) {
void TCPConnector::start(sys::AsynchIO* aio_) {
aio = aio_;
- for (int i = 0; i < 32; i++) {
+ for (int i = 0; i < 4; i++) {
aio->queueReadBuffer(new Buff(maxFrameSize));
}
- identifier = str(format("[%1% %2%]") % socket.getLocalPort() % socket.getPeerAddress());
+ identifier = str(format("[%1%]") % socket.getFullAddress());
}
void TCPConnector::initAmqp() {
diff --git a/cpp/src/qpid/client/TCPConnector.h b/cpp/src/qpid/client/TCPConnector.h
index c756469182..eb3f696013 100644
--- a/cpp/src/qpid/client/TCPConnector.h
+++ b/cpp/src/qpid/client/TCPConnector.h
@@ -98,7 +98,7 @@ class TCPConnector : public Connector, public sys::Codec
protected:
virtual ~TCPConnector();
- void connect(const std::string& host, int port);
+ void connect(const std::string& host, const std::string& port);
void start(sys::AsynchIO* aio_);
void initAmqp();
virtual void connectFailed(const std::string& msg);
diff --git a/cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp b/cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp
index bfb20118b5..d2accddcd0 100644
--- a/cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp
+++ b/cpp/src/qpid/client/amqp0_10/AcceptTracker.cpp
@@ -30,12 +30,23 @@ void AcceptTracker::State::accept()
unaccepted.clear();
}
-void AcceptTracker::State::accept(qpid::framing::SequenceNumber id)
+SequenceSet AcceptTracker::State::accept(qpid::framing::SequenceNumber id, bool cumulative)
{
- if (unaccepted.contains(id)) {
- unaccepted.remove(id);
- unconfirmed.add(id);
+ SequenceSet accepting;
+ if (cumulative) {
+ for (SequenceSet::iterator i = unaccepted.begin(); i != unaccepted.end() && *i <= id; ++i) {
+ accepting.add(*i);
+ }
+ unconfirmed.add(accepting);
+ unaccepted.remove(accepting);
+ } else {
+ if (unaccepted.contains(id)) {
+ unaccepted.remove(id);
+ unconfirmed.add(id);
+ accepting.add(id);
+ }
}
+ return accepting;
}
void AcceptTracker::State::release()
@@ -59,6 +70,18 @@ void AcceptTracker::delivered(const std::string& destination, const qpid::framin
destinationState[destination].unaccepted.add(id);
}
+namespace
+{
+const size_t FLUSH_FREQUENCY = 1024;
+}
+
+void AcceptTracker::addToPending(qpid::client::AsyncSession& session, const Record& record)
+{
+ pending.push_back(record);
+ if (pending.size() % FLUSH_FREQUENCY == 0) session.flush();
+}
+
+
void AcceptTracker::accept(qpid::client::AsyncSession& session)
{
for (StateMap::iterator i = destinationState.begin(); i != destinationState.end(); ++i) {
@@ -67,20 +90,19 @@ void AcceptTracker::accept(qpid::client::AsyncSession& session)
Record record;
record.status = session.messageAccept(aggregateState.unaccepted);
record.accepted = aggregateState.unaccepted;
- pending.push_back(record);
+ addToPending(session, record);
aggregateState.accept();
}
-void AcceptTracker::accept(qpid::framing::SequenceNumber id, qpid::client::AsyncSession& session)
+void AcceptTracker::accept(qpid::framing::SequenceNumber id, qpid::client::AsyncSession& session, bool cumulative)
{
for (StateMap::iterator i = destinationState.begin(); i != destinationState.end(); ++i) {
- i->second.accept(id);
+ i->second.accept(id, cumulative);
}
Record record;
- record.accepted.add(id);
+ record.accepted = aggregateState.accept(id, cumulative);
record.status = session.messageAccept(record.accepted);
- pending.push_back(record);
- aggregateState.accept(id);
+ addToPending(session, record);
}
void AcceptTracker::release(qpid::client::AsyncSession& session)
diff --git a/cpp/src/qpid/client/amqp0_10/AcceptTracker.h b/cpp/src/qpid/client/amqp0_10/AcceptTracker.h
index 87890e41cc..85209c3b87 100644
--- a/cpp/src/qpid/client/amqp0_10/AcceptTracker.h
+++ b/cpp/src/qpid/client/amqp0_10/AcceptTracker.h
@@ -42,7 +42,7 @@ class AcceptTracker
public:
void delivered(const std::string& destination, const qpid::framing::SequenceNumber& id);
void accept(qpid::client::AsyncSession&);
- void accept(qpid::framing::SequenceNumber, qpid::client::AsyncSession&);
+ void accept(qpid::framing::SequenceNumber, qpid::client::AsyncSession&, bool cumulative);
void release(qpid::client::AsyncSession&);
uint32_t acceptsPending();
uint32_t acceptsPending(const std::string& destination);
@@ -62,7 +62,7 @@ class AcceptTracker
qpid::framing::SequenceSet unconfirmed;
void accept();
- void accept(qpid::framing::SequenceNumber);
+ qpid::framing::SequenceSet accept(qpid::framing::SequenceNumber, bool cumulative);
void release();
uint32_t acceptsPending();
void completed(qpid::framing::SequenceSet&);
@@ -79,6 +79,7 @@ class AcceptTracker
StateMap destinationState;
Records pending;
+ void addToPending(qpid::client::AsyncSession&, const Record&);
void checkPending();
void completed(qpid::framing::SequenceSet&);
};
diff --git a/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp b/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp
index f1295a3b66..16e5fde075 100644
--- a/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp
+++ b/cpp/src/qpid/client/amqp0_10/AddressResolution.cpp
@@ -129,6 +129,10 @@ const std::string HEADERS_EXCHANGE("headers");
const std::string XML_EXCHANGE("xml");
const std::string WILDCARD_ANY("#");
+//exchange prefixes:
+const std::string PREFIX_AMQ("amq.");
+const std::string PREFIX_QPID("qpid.");
+
const Verifier verifier;
}
@@ -199,6 +203,7 @@ class Exchange : protected Node
void checkCreate(qpid::client::AsyncSession&, CheckMode);
void checkAssert(qpid::client::AsyncSession&, CheckMode);
void checkDelete(qpid::client::AsyncSession&, CheckMode);
+ bool isReservedName();
protected:
const std::string specifiedType;
@@ -233,6 +238,8 @@ class Subscription : public Exchange, public MessageSource
const bool reliable;
const bool durable;
const std::string actualType;
+ const bool exclusiveQueue;
+ const bool exclusiveSubscription;
FieldTable queueOptions;
FieldTable subscriptionOptions;
Bindings bindings;
@@ -307,6 +314,7 @@ struct Opt
Opt& operator/(const std::string& name);
operator bool() const;
std::string str() const;
+ bool asBool(bool defaultValue) const;
const Variant::List& asList() const;
void collect(qpid::framing::FieldTable& args) const;
@@ -338,6 +346,12 @@ Opt::operator bool() const
return value && !value->isVoid() && value->asBool();
}
+bool Opt::asBool(bool defaultValue) const
+{
+ if (value) return value->asBool();
+ else return defaultValue;
+}
+
std::string Opt::str() const
{
if (value) return value->asString();
@@ -481,7 +495,7 @@ std::string Subscription::getSubscriptionName(const std::string& base, const std
if (name.empty()) {
return (boost::format("%1%_%2%") % base % Uuid(true).str()).str();
} else {
- return (boost::format("%1%_%2%") % base % name).str();
+ return name;
}
}
@@ -490,7 +504,9 @@ Subscription::Subscription(const Address& address, const std::string& type)
queue(getSubscriptionName(name, (Opt(address)/LINK/NAME).str())),
reliable(AddressResolution::is_reliable(address)),
durable(Opt(address)/LINK/DURABLE),
- actualType(type.empty() ? (specifiedType.empty() ? TOPIC_EXCHANGE : specifiedType) : type)
+ 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))
{
(Opt(address)/LINK/X_DECLARE/ARGUMENTS).collect(queueOptions);
(Opt(address)/LINK/X_SUBSCRIBE/ARGUMENTS).collect(subscriptionOptions);
@@ -550,7 +566,7 @@ void Subscription::subscribe(qpid::client::AsyncSession& session, const std::str
checkAssert(session, FOR_RECEIVER);
//create subscription queue:
- session.queueDeclare(arg::queue=queue, arg::exclusive=true,
+ session.queueDeclare(arg::queue=queue, arg::exclusive=exclusiveQueue,
arg::autoDelete=!reliable, arg::durable=durable, arg::arguments=queueOptions);
//'default' binding:
bindings.bind(session);
@@ -559,15 +575,15 @@ void Subscription::subscribe(qpid::client::AsyncSession& session, const std::str
linkBindings.bind(session);
//subscribe to subscription queue:
AcceptMode accept = reliable ? ACCEPT_MODE_EXPLICIT : ACCEPT_MODE_NONE;
- session.messageSubscribe(arg::queue=queue, arg::destination=destination,
- arg::exclusive=true, arg::acceptMode=accept, arg::arguments=subscriptionOptions);
+ session.messageSubscribe(arg::queue=queue, arg::destination=destination,
+ arg::exclusive=exclusiveSubscription, arg::acceptMode=accept, arg::arguments=subscriptionOptions);
}
void Subscription::cancel(qpid::client::AsyncSession& session, const std::string& destination)
{
linkBindings.unbind(session);
session.messageCancel(destination);
- session.queueDelete(arg::queue=queue);
+ if (exclusiveQueue) session.queueDelete(arg::queue=queue, arg::ifUnused=true);
checkDelete(session, FOR_RECEIVER);
}
@@ -761,18 +777,32 @@ Exchange::Exchange(const Address& a) : Node(a),
linkBindings.setDefaultExchange(name);
}
+bool Exchange::isReservedName()
+{
+ return name.find(PREFIX_AMQ) != std::string::npos || name.find(PREFIX_QPID) != std::string::npos;
+}
+
void Exchange::checkCreate(qpid::client::AsyncSession& session, CheckMode mode)
{
if (enabled(createPolicy, mode)) {
try {
- std::string type = specifiedType;
- if (type.empty()) type = TOPIC_EXCHANGE;
- session.exchangeDeclare(arg::exchange=name,
- arg::type=type,
- arg::durable=durable,
- arg::autoDelete=autoDelete,
- arg::alternateExchange=alternateExchange,
- arg::arguments=arguments);
+ if (isReservedName()) {
+ try {
+ sync(session).exchangeDeclare(arg::exchange=name, arg::passive=true);
+ } catch (const qpid::framing::NotFoundException& /*e*/) {
+ throw ResolutionError((boost::format("Cannot create exchange %1%; names beginning with \"amq.\" or \"qpid.\" are reserved.") % name).str());
+ }
+
+ } else {
+ std::string type = specifiedType;
+ if (type.empty()) type = TOPIC_EXCHANGE;
+ session.exchangeDeclare(arg::exchange=name,
+ arg::type=type,
+ arg::durable=durable,
+ arg::autoDelete=autoDelete,
+ arg::alternateExchange=alternateExchange,
+ arg::arguments=arguments);
+ }
nodeBindings.bind(session);
session.sync();
} catch (const qpid::framing::NotAllowedException& e) {
@@ -822,7 +852,7 @@ void Exchange::checkAssert(qpid::client::AsyncSession& session, CheckMode mode)
FieldTable::ValuePtr v = result.getArguments().get(i->first);
if (!v) {
throw AssertionFailed((boost::format("Option %1% not set for %2%") % i->first % name).str());
- } else if (i->second != v) {
+ } else if (*i->second != *v) {
throw AssertionFailed((boost::format("Option %1% does not match for %2%, expected %3%, got %4%")
% i->first % name % *(i->second) % *v).str());
}
diff --git a/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp b/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp
index 5a545c1f6a..cc6e9b9ab2 100644
--- a/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp
+++ b/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp
@@ -20,7 +20,6 @@
*/
#include "ConnectionImpl.h"
#include "SessionImpl.h"
-#include "SimpleUrlParser.h"
#include "qpid/messaging/exceptions.h"
#include "qpid/messaging/Session.h"
#include "qpid/messaging/PrivateImplRef.h"
@@ -39,26 +38,18 @@ using qpid::types::Variant;
using qpid::types::VAR_LIST;
using qpid::framing::Uuid;
-void convert(const Variant::List& from, std::vector<std::string>& to)
-{
- for (Variant::List::const_iterator i = from.begin(); i != from.end(); ++i) {
- to.push_back(i->asString());
- }
+namespace {
+void merge(const std::string& value, std::vector<std::string>& list) {
+ if (std::find(list.begin(), list.end(), value) == list.end())
+ list.push_back(value);
}
-template <class T> bool setIfFound(const Variant::Map& map, const std::string& key, T& value)
+void merge(const Variant::List& from, std::vector<std::string>& to)
{
- Variant::Map::const_iterator i = map.find(key);
- if (i != map.end()) {
- value = (T) i->second;
- QPID_LOG(debug, "option " << key << " specified as " << i->second);
- return true;
- } else {
- return false;
- }
+ for (Variant::List::const_iterator i = from.begin(); i != from.end(); ++i)
+ merge(i->asString(), to);
}
-namespace {
std::string asString(const std::vector<std::string>& v) {
std::stringstream os;
os << "[";
@@ -71,49 +62,8 @@ std::string asString(const std::vector<std::string>& v) {
}
}
-template <> bool setIfFound< std::vector<std::string> >(const Variant::Map& map,
- const std::string& key,
- std::vector<std::string>& value)
-{
- Variant::Map::const_iterator i = map.find(key);
- if (i != map.end()) {
- value.clear();
- if (i->second.getType() == VAR_LIST) {
- convert(i->second.asList(), value);
- } else {
- value.push_back(i->second.asString());
- }
- QPID_LOG(debug, "option " << key << " specified as " << asString(value));
- return true;
- } else {
- return false;
- }
-}
-
-void convert(const Variant::Map& from, ConnectionSettings& to)
-{
- setIfFound(from, "username", to.username);
- setIfFound(from, "password", to.password);
- setIfFound(from, "sasl-mechanism", to.mechanism);
- setIfFound(from, "sasl-service", to.service);
- setIfFound(from, "sasl-min-ssf", to.minSsf);
- setIfFound(from, "sasl-max-ssf", to.maxSsf);
-
- setIfFound(from, "heartbeat", to.heartbeat);
- setIfFound(from, "tcp-nodelay", to.tcpNoDelay);
-
- setIfFound(from, "locale", to.locale);
- setIfFound(from, "max-channels", to.maxChannels);
- setIfFound(from, "max-frame-size", to.maxFrameSize);
- setIfFound(from, "bounds", to.bounds);
-
- setIfFound(from, "transport", to.protocol);
-
- setIfFound(from, "ssl-cert-name", to.sslCertName);
-}
-
ConnectionImpl::ConnectionImpl(const std::string& url, const Variant::Map& options) :
- reconnect(false), timeout(-1), limit(-1),
+ replaceUrls(false), reconnect(false), timeout(-1), limit(-1),
minReconnectInterval(3), maxReconnectInterval(60),
retries(0), reconnectOnLimitExceeded(true)
{
@@ -124,27 +74,69 @@ ConnectionImpl::ConnectionImpl(const std::string& url, const Variant::Map& optio
void ConnectionImpl::setOptions(const Variant::Map& options)
{
- sys::Mutex::ScopedLock l(lock);
- convert(options, settings);
- setIfFound(options, "reconnect", reconnect);
- setIfFound(options, "reconnect-timeout", timeout);
- setIfFound(options, "reconnect-limit", limit);
- int64_t reconnectInterval;
- if (setIfFound(options, "reconnect-interval", reconnectInterval)) {
- minReconnectInterval = maxReconnectInterval = reconnectInterval;
- } else {
- setIfFound(options, "reconnect-interval-min", minReconnectInterval);
- setIfFound(options, "reconnect-interval-max", maxReconnectInterval);
+ for (Variant::Map::const_iterator i = options.begin(); i != options.end(); ++i) {
+ setOption(i->first, i->second);
}
- setIfFound(options, "reconnect-urls", urls);
- setIfFound(options, "x-reconnect-on-limit-exceeded", reconnectOnLimitExceeded);
}
void ConnectionImpl::setOption(const std::string& name, const Variant& value)
{
- Variant::Map options;
- options[name] = value;
- setOptions(options);
+ sys::Mutex::ScopedLock l(lock);
+ if (name == "reconnect") {
+ reconnect = value;
+ } else if (name == "reconnect-timeout" || name == "reconnect_timeout") {
+ timeout = value;
+ } else if (name == "reconnect-limit" || name == "reconnect_limit") {
+ limit = value;
+ } else if (name == "reconnect-interval" || name == "reconnect_interval") {
+ maxReconnectInterval = minReconnectInterval = value;
+ } else if (name == "reconnect-interval-min" || name == "reconnect_interval_min") {
+ minReconnectInterval = value;
+ } else if (name == "reconnect-interval-max" || name == "reconnect_interval_max") {
+ maxReconnectInterval = value;
+ } else if (name == "reconnect-urls-replace" || name == "reconnect_urls_replace") {
+ replaceUrls = value.asBool();
+ } else if (name == "reconnect-urls" || name == "reconnect_urls") {
+ if (replaceUrls) urls.clear();
+ if (value.getType() == VAR_LIST) {
+ merge(value.asList(), urls);
+ } else {
+ merge(value.asString(), urls);
+ }
+ } else if (name == "username") {
+ settings.username = value.asString();
+ } else if (name == "password") {
+ settings.password = value.asString();
+ } else if (name == "sasl-mechanism" || name == "sasl_mechanism" ||
+ name == "sasl-mechanisms" || name == "sasl_mechanisms") {
+ settings.mechanism = value.asString();
+ } else if (name == "sasl-service" || name == "sasl_service") {
+ settings.service = value.asString();
+ } else if (name == "sasl-min-ssf" || name == "sasl_min_ssf") {
+ settings.minSsf = value;
+ } else if (name == "sasl-max-ssf" || name == "sasl_max_ssf") {
+ settings.maxSsf = value;
+ } else if (name == "heartbeat") {
+ settings.heartbeat = value;
+ } else if (name == "tcp-nodelay" || name == "tcp_nodelay") {
+ settings.tcpNoDelay = value;
+ } else if (name == "locale") {
+ settings.locale = value.asString();
+ } else if (name == "max-channels" || name == "max_channels") {
+ settings.maxChannels = value;
+ } else if (name == "max-frame-size" || name == "max_frame_size") {
+ settings.maxFrameSize = value;
+ } else if (name == "bounds") {
+ settings.bounds = value;
+ } else if (name == "transport") {
+ settings.protocol = value.asString();
+ } else if (name == "ssl-cert-name" || name == "ssl_cert_name") {
+ settings.sslCertName = value.asString();
+ } else if (name == "x-reconnect-on-limit-exceeded" || name == "x_reconnect_on_limit_exceeded") {
+ reconnectOnLimitExceeded = value;
+ } else {
+ throw qpid::messaging::MessagingException(QPID_MSG("Invalid option: " << name << " not recognised"));
+ }
}
@@ -214,7 +206,7 @@ qpid::messaging::Session ConnectionImpl::newSession(bool transactional, const st
sessions[name] = impl;
break;
} catch (const qpid::TransportFailure&) {
- open();
+ reopen();
} catch (const qpid::SessionException& e) {
throw qpid::messaging::SessionError(e.what());
} catch (const std::exception& e) {
@@ -235,6 +227,15 @@ void ConnectionImpl::open()
catch (const qpid::Exception& e) { throw messaging::ConnectionError(e.what()); }
}
+void ConnectionImpl::reopen()
+{
+ if (!reconnect) {
+ throw qpid::messaging::TransportFailure("Failed to connect (reconnect disabled)");
+ }
+ open();
+}
+
+
bool expired(const qpid::sys::AbsTime& start, int64_t timeout)
{
if (timeout == 0) return true;
@@ -262,14 +263,9 @@ void ConnectionImpl::connect(const qpid::sys::AbsTime& started)
}
void ConnectionImpl::mergeUrls(const std::vector<Url>& more, const sys::Mutex::ScopedLock&) {
- if (more.size()) {
- for (size_t i = 0; i < more.size(); ++i) {
- if (std::find(urls.begin(), urls.end(), more[i].str()) == urls.end()) {
- urls.push_back(more[i].str());
- }
- }
- QPID_LOG(debug, "Added known-hosts, reconnect-urls=" << asString(urls));
- }
+ for (std::vector<Url>::const_iterator i = more.begin(); i != more.end(); ++i)
+ merge(i->str(), urls);
+ QPID_LOG(debug, "Added known-hosts, reconnect-urls=" << asString(urls));
}
bool ConnectionImpl::tryConnect()
@@ -278,21 +274,14 @@ bool ConnectionImpl::tryConnect()
for (std::vector<std::string>::const_iterator i = urls.begin(); i != urls.end(); ++i) {
try {
QPID_LOG(info, "Trying to connect to " << *i << "...");
- //TODO: when url support is more complete can avoid this test here
- if (i->find("amqp:") == 0) {
- Url url(*i);
- connection.open(url, settings);
- } else {
- SimpleUrlParser::parse(*i, settings);
- connection.open(settings);
- }
+ Url url(*i);
+ if (url.getUser().size()) settings.username = url.getUser();
+ if (url.getPass().size()) settings.password = url.getPass();
+ connection.open(url, settings);
QPID_LOG(info, "Connected to " << *i);
mergeUrls(connection.getInitialBrokers(), l);
return resetSessions(l);
- } catch (const qpid::ConnectionException& e) {
- //TODO: need to fix timeout on
- //qpid::client::Connection::open() so that it throws
- //TransportFailure rather than a ConnectionException
+ } catch (const qpid::TransportFailure& e) {
QPID_LOG(info, "Failed to connect to " << *i << ": " << e.what());
}
}
diff --git a/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h b/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h
index 09f2038312..1b58cbbe3e 100644
--- a/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h
+++ b/cpp/src/qpid/client/amqp0_10/ConnectionImpl.h
@@ -43,6 +43,7 @@ class ConnectionImpl : public qpid::messaging::ConnectionImpl
public:
ConnectionImpl(const std::string& url, const qpid::types::Variant::Map& options);
void open();
+ void reopen();
bool isOpen() const;
void close();
qpid::messaging::Session newSession(bool transactional, const std::string& name);
@@ -59,6 +60,7 @@ class ConnectionImpl : public qpid::messaging::ConnectionImpl
qpid::sys::Semaphore semaphore;//used to coordinate reconnection
Sessions sessions;
qpid::client::Connection connection;
+ bool replaceUrls; // Replace rather than merging with reconnect-urls
std::vector<std::string> urls;
qpid::client::ConnectionSettings settings;
bool reconnect;
diff --git a/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp b/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp
index 71e89bdba1..3badaf40ba 100644
--- a/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp
+++ b/cpp/src/qpid/client/amqp0_10/IncomingMessages.cpp
@@ -144,10 +144,10 @@ void IncomingMessages::accept()
acceptTracker.accept(session);
}
-void IncomingMessages::accept(qpid::framing::SequenceNumber id)
+void IncomingMessages::accept(qpid::framing::SequenceNumber id, bool cumulative)
{
sys::Mutex::ScopedLock l(lock);
- acceptTracker.accept(id, session);
+ acceptTracker.accept(id, session, cumulative);
}
@@ -301,6 +301,7 @@ const std::string SUBJECT("qpid.subject");
const std::string X_APP_ID("x-amqp-0-10.app-id");
const std::string X_ROUTING_KEY("x-amqp-0-10.routing-key");
const std::string X_CONTENT_ENCODING("x-amqp-0-10.content-encoding");
+const std::string X_TIMESTAMP("x-amqp-0-10.timestamp");
}
void populateHeaders(qpid::messaging::Message& message,
@@ -334,10 +335,13 @@ void populateHeaders(qpid::messaging::Message& message,
if (messageProperties->hasContentEncoding()) {
message.getProperties()[X_CONTENT_ENCODING] = messageProperties->getContentEncoding();
}
- // routing-key, others?
+ // routing-key, timestamp, others?
if (deliveryProperties && deliveryProperties->hasRoutingKey()) {
message.getProperties()[X_ROUTING_KEY] = deliveryProperties->getRoutingKey();
}
+ if (deliveryProperties && deliveryProperties->hasTimestamp()) {
+ message.getProperties()[X_TIMESTAMP] = deliveryProperties->getTimestamp();
+ }
}
}
diff --git a/cpp/src/qpid/client/amqp0_10/IncomingMessages.h b/cpp/src/qpid/client/amqp0_10/IncomingMessages.h
index f6a291bc68..9053b70312 100644
--- a/cpp/src/qpid/client/amqp0_10/IncomingMessages.h
+++ b/cpp/src/qpid/client/amqp0_10/IncomingMessages.h
@@ -72,7 +72,7 @@ class IncomingMessages
bool get(Handler& handler, qpid::sys::Duration timeout);
bool getNextDestination(std::string& destination, qpid::sys::Duration timeout);
void accept();
- void accept(qpid::framing::SequenceNumber id);
+ void accept(qpid::framing::SequenceNumber id, bool cumulative);
void releaseAll();
void releasePending(const std::string& destination);
diff --git a/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp b/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp
index 82358961c8..d93416da75 100644
--- a/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp
+++ b/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp
@@ -59,7 +59,9 @@ void OutgoingMessage::convert(const qpid::messaging::Message& from)
message.getMessageProperties().setReplyTo(AddressResolution::convert(address));
}
translate(from.getProperties(), message.getMessageProperties().getApplicationHeaders());
- message.getDeliveryProperties().setTtl(from.getTtl().getMilliseconds());
+ if (from.getTtl().getMilliseconds()) {
+ message.getDeliveryProperties().setTtl(from.getTtl().getMilliseconds());
+ }
if (from.getDurable()) {
message.getDeliveryProperties().setDeliveryMode(DELIVERY_MODE_PERSISTENT);
}
diff --git a/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp b/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp
index e1b75ec0cf..f2f0f1a9e5 100644
--- a/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp
+++ b/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp
@@ -135,6 +135,7 @@ void SenderImpl::sendUnreliable(const qpid::messaging::Message& m)
void SenderImpl::replay(const sys::Mutex::ScopedLock&)
{
for (OutgoingMessages::iterator i = outgoing.begin(); i != outgoing.end(); ++i) {
+ i->message.setRedelivered(true);
sink->send(session, name, *i);
}
}
@@ -147,7 +148,7 @@ uint32_t SenderImpl::checkPendingSends(bool flush) {
uint32_t SenderImpl::checkPendingSends(bool flush, const sys::Mutex::ScopedLock&)
{
if (flush) {
- session.flush();
+ session.flush();
flushed = true;
} else {
flushed = false;
diff --git a/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp b/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp
index 75a71997fd..be5eab1f2b 100644
--- a/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp
+++ b/cpp/src/qpid/client/amqp0_10/SessionImpl.cpp
@@ -60,12 +60,14 @@ SessionImpl::SessionImpl(ConnectionImpl& c, bool t) : connection(&c), transactio
void SessionImpl::checkError()
{
+ ScopedLock l(lock);
qpid::client::SessionBase_0_10Access s(session);
s.get()->assertOpen();
}
bool SessionImpl::hasError()
{
+ ScopedLock l(lock);
qpid::client::SessionBase_0_10Access s(session);
return s.get()->hasError();
}
@@ -112,13 +114,14 @@ void SessionImpl::release(qpid::messaging::Message& m)
execute1<Release>(m);
}
-void SessionImpl::acknowledge(qpid::messaging::Message& m)
+void SessionImpl::acknowledge(qpid::messaging::Message& m, bool cumulative)
{
//Should probably throw an exception on failure here, or indicate
//it through a return type at least. Failure means that the
//message may be redelivered; i.e. the application cannot delete
//any state necessary for preventing reprocessing of the message
- execute1<Acknowledge1>(m);
+ Acknowledge2 ack(*this, m, cumulative);
+ execute(ack);
}
void SessionImpl::close()
@@ -128,27 +131,29 @@ void SessionImpl::close()
senders.clear();
receivers.clear();
} else {
- while (true) {
- Sender s;
- {
- ScopedLock l(lock);
- if (senders.empty()) break;
- s = senders.begin()->second;
- }
- s.close(); // outside the lock, will call senderCancelled
+ Senders sCopy;
+ Receivers rCopy;
+ {
+ ScopedLock l(lock);
+ senders.swap(sCopy);
+ receivers.swap(rCopy);
}
- while (true) {
- Receiver r;
- {
- ScopedLock l(lock);
- if (receivers.empty()) break;
- r = receivers.begin()->second;
- }
- r.close(); // outside the lock, will call receiverCancelled
+ for (Senders::iterator i = sCopy.begin(); i != sCopy.end(); ++i)
+ {
+ // outside the lock, will call senderCancelled
+ i->second.close();
+ }
+ for (Receivers::iterator i = rCopy.begin(); i != rCopy.end(); ++i)
+ {
+ // outside the lock, will call receiverCancelled
+ i->second.close();
}
}
connection->closed(*this);
- if (!hasError()) session.close();
+ if (!hasError()) {
+ ScopedLock l(lock);
+ session.close();
+ }
}
template <class T, class S> boost::intrusive_ptr<S> getImplPtr(T& t)
@@ -431,8 +436,11 @@ uint32_t SessionImpl::getUnsettledAcksImpl(const std::string* destination)
void SessionImpl::syncImpl(bool block)
{
- if (block) session.sync();
- else session.flush();
+ {
+ ScopedLock l(lock);
+ if (block) session.sync();
+ else session.flush();
+ }
//cleanup unconfirmed accept records:
incoming.pendingAccept();
}
@@ -467,10 +475,10 @@ void SessionImpl::acknowledgeImpl()
if (!transactional) incoming.accept();
}
-void SessionImpl::acknowledgeImpl(qpid::messaging::Message& m)
+void SessionImpl::acknowledgeImpl(qpid::messaging::Message& m, bool cumulative)
{
ScopedLock l(lock);
- if (!transactional) incoming.accept(MessageImplAccess::get(m).getInternalId());
+ if (!transactional) incoming.accept(MessageImplAccess::get(m).getInternalId(), cumulative);
}
void SessionImpl::rejectImpl(qpid::messaging::Message& m)
@@ -509,7 +517,7 @@ void SessionImpl::senderCancelled(const std::string& name)
void SessionImpl::reconnect()
{
- connection->open();
+ connection->reopen();
}
bool SessionImpl::backoff()
diff --git a/cpp/src/qpid/client/amqp0_10/SessionImpl.h b/cpp/src/qpid/client/amqp0_10/SessionImpl.h
index 2a2aa47df6..c7dea77d18 100644
--- a/cpp/src/qpid/client/amqp0_10/SessionImpl.h
+++ b/cpp/src/qpid/client/amqp0_10/SessionImpl.h
@@ -63,7 +63,7 @@ class SessionImpl : public qpid::messaging::SessionImpl
void acknowledge(bool sync);
void reject(qpid::messaging::Message&);
void release(qpid::messaging::Message&);
- void acknowledge(qpid::messaging::Message& msg);
+ void acknowledge(qpid::messaging::Message& msg, bool cumulative);
void close();
void sync(bool block);
qpid::messaging::Sender createSender(const qpid::messaging::Address& address);
@@ -139,7 +139,7 @@ class SessionImpl : public qpid::messaging::SessionImpl
void commitImpl();
void rollbackImpl();
void acknowledgeImpl();
- void acknowledgeImpl(qpid::messaging::Message&);
+ void acknowledgeImpl(qpid::messaging::Message&, bool cumulative);
void rejectImpl(qpid::messaging::Message&);
void releaseImpl(qpid::messaging::Message&);
void closeImpl();
@@ -204,12 +204,13 @@ class SessionImpl : public qpid::messaging::SessionImpl
void operator()() { impl.releaseImpl(message); }
};
- struct Acknowledge1 : Command
+ struct Acknowledge2 : Command
{
qpid::messaging::Message& message;
+ bool cumulative;
- Acknowledge1(SessionImpl& i, qpid::messaging::Message& m) : Command(i), message(m) {}
- void operator()() { impl.acknowledgeImpl(message); }
+ Acknowledge2(SessionImpl& i, qpid::messaging::Message& m, bool c) : Command(i), message(m), cumulative(c) {}
+ void operator()() { impl.acknowledgeImpl(message, cumulative); }
};
struct CreateSender;
diff --git a/cpp/src/qpid/client/amqp0_10/SimpleUrlParser.cpp b/cpp/src/qpid/client/amqp0_10/SimpleUrlParser.cpp
deleted file mode 100644
index 327c2274a6..0000000000
--- a/cpp/src/qpid/client/amqp0_10/SimpleUrlParser.cpp
+++ /dev/null
@@ -1,79 +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 "SimpleUrlParser.h"
-#include "qpid/client/ConnectionSettings.h"
-#include "qpid/Exception.h"
-#include <boost/lexical_cast.hpp>
-
-namespace qpid {
-namespace client {
-namespace amqp0_10 {
-
-bool split(const std::string& in, char delim, std::pair<std::string, std::string>& result)
-{
- std::string::size_type i = in.find(delim);
- if (i != std::string::npos) {
- result.first = in.substr(0, i);
- if (i+1 < in.size()) {
- result.second = in.substr(i+1);
- }
- return true;
- } else {
- return false;
- }
-}
-
-void parseUsernameAndPassword(const std::string& in, qpid::client::ConnectionSettings& result)
-{
- std::pair<std::string, std::string> parts;
- if (!split(in, '/', parts)) {
- result.username = in;
- } else {
- result.username = parts.first;
- result.password = parts.second;
- }
-}
-
-void parseHostAndPort(const std::string& in, qpid::client::ConnectionSettings& result)
-{
- std::pair<std::string, std::string> parts;
- if (!split(in, ':', parts)) {
- result.host = in;
- } else {
- result.host = parts.first;
- if (parts.second.size()) {
- result.port = boost::lexical_cast<uint16_t>(parts.second);
- }
- }
-}
-
-void SimpleUrlParser::parse(const std::string& url, qpid::client::ConnectionSettings& result)
-{
- std::pair<std::string, std::string> parts;
- if (!split(url, '@', parts)) {
- parseHostAndPort(url, result);
- } else {
- parseUsernameAndPassword(parts.first, result);
- parseHostAndPort(parts.second, result);
- }
-}
-
-}}} // namespace qpid::client::amqp0_10
diff --git a/cpp/src/qpid/client/windows/SaslFactory.cpp b/cpp/src/qpid/client/windows/SaslFactory.cpp
index 63c7fa3d1f..53d825771b 100644
--- a/cpp/src/qpid/client/windows/SaslFactory.cpp
+++ b/cpp/src/qpid/client/windows/SaslFactory.cpp
@@ -71,7 +71,7 @@ class WindowsSasl : public Sasl
public:
WindowsSasl( const std::string &, const std::string &, const std::string &, const std::string &, int, int );
~WindowsSasl();
- std::string start(const std::string& mechanisms, const SecuritySettings* externalSettings);
+ bool start(const std::string& mechanisms, std::string& response, const SecuritySettings* externalSettings);
std::string step(const std::string& challenge);
std::string getMechanism();
std::string getUserId();
@@ -121,8 +121,8 @@ WindowsSasl::~WindowsSasl()
{
}
-std::string WindowsSasl::start(const std::string& mechanisms,
- const SecuritySettings* /*externalSettings*/)
+bool WindowsSasl::start(const std::string& mechanisms, std::string& response,
+ const SecuritySettings* /*externalSettings*/)
{
QPID_LOG(debug, "WindowsSasl::start(" << mechanisms << ")");
@@ -142,18 +142,18 @@ std::string WindowsSasl::start(const std::string& mechanisms,
if (!haveAnon && !havePlain)
throw InternalErrorException(QPID_MSG("Sasl error: no common mechanism"));
- std::string resp = "";
if (havePlain) {
mechanism = PLAIN;
- resp = ((char)0) + settings.username + ((char)0) + settings.password;
+ response = ((char)0) + settings.username + ((char)0) + settings.password;
}
else {
mechanism = ANONYMOUS;
+ response = "";
}
- return resp;
+ return true;
}
-std::string WindowsSasl::step(const std::string& challenge)
+std::string WindowsSasl::step(const std::string& /*challenge*/)
{
// Shouldn't get this for PLAIN...
throw InternalErrorException(QPID_MSG("Sasl step error"));
@@ -169,7 +169,7 @@ std::string WindowsSasl::getUserId()
return std::string(); // TODO - when GSSAPI is supported, return userId for connection.
}
-std::auto_ptr<SecurityLayer> WindowsSasl::getSecurityLayer(uint16_t maxFrameSize)
+std::auto_ptr<SecurityLayer> WindowsSasl::getSecurityLayer(uint16_t /*maxFrameSize*/)
{
return std::auto_ptr<SecurityLayer>(0);
}
diff --git a/cpp/src/qpid/client/windows/SslConnector.cpp b/cpp/src/qpid/client/windows/SslConnector.cpp
index a33713e1a8..785c817928 100644
--- a/cpp/src/qpid/client/windows/SslConnector.cpp
+++ b/cpp/src/qpid/client/windows/SslConnector.cpp
@@ -77,7 +77,7 @@ public:
framing::ProtocolVersion pVersion,
const ConnectionSettings&,
ConnectionImpl*);
- virtual void connect(const std::string& host, int port);
+ virtual void connect(const std::string& host, const std::string& port);
virtual void connected(const Socket&);
unsigned int getSSF();
};
@@ -153,7 +153,7 @@ SslConnector::~SslConnector()
// Will this get reach via virtual method via boost::bind????
-void SslConnector::connect(const std::string& host, int port) {
+void SslConnector::connect(const std::string& host, const std::string& port) {
brokerHost = host;
TCPConnector::connect(host, port);
}
diff --git a/cpp/src/qpid/cluster/Cluster.cpp b/cpp/src/qpid/cluster/Cluster.cpp
index dd4882774b..e6e3de64f2 100644
--- a/cpp/src/qpid/cluster/Cluster.cpp
+++ b/cpp/src/qpid/cluster/Cluster.cpp
@@ -36,45 +36,45 @@
*
* IMPORTANT NOTE: any time code is added to the broker that uses timers,
* the cluster may need to be updated to take account of this.
- *
+ *
*
* USE OF TIMESTAMPS IN THE BROKER
- *
+ *
* The following are the current areas where broker uses timers or timestamps:
- *
+ *
* - Producer flow control: broker::SemanticState uses
* connection::getClusterOrderOutput. a FrameHandler that sends
* frames to the client via the cluster. Used by broker::SessionState
- *
+ *
* - QueueCleaner, Message TTL: uses ExpiryPolicy, which is
* implemented by cluster::ExpiryPolicy.
- *
+ *
* - Connection heartbeat: sends connection controls, not part of
* session command counting so OK to ignore.
- *
+ *
* - LinkRegistry: only cluster elder is ever active for links.
- *
+ *
* - management::ManagementBroker: uses MessageHandler supplied by cluster
* to send messages to the broker via the cluster.
- *
- * - Dtx: not yet supported with cluster.
*
- * cluster::ExpiryPolicy implements the strategy for message expiry.
+ * cluster::ExpiryPolicy uses cluster time.
*
* ClusterTimer implements periodic timed events in the cluster context.
- * Used for periodic management events.
+ * Used for:
+ * - periodic management events.
+ * - DTX transaction timeouts.
*
* <h1>CLUSTER PROTOCOL OVERVIEW</h1>
- *
+ *
* Messages sent to/from CPG are called Events.
*
* An Event carries a ConnectionId, which includes a MemberId and a
* connection number.
- *
+ *
* Events are either
* - Connection events: non-0 connection number and are associated with a connection.
* - Cluster Events: 0 connection number, are not associated with a connection.
- *
+ *
* Events are further categorized as:
* - Control: carries method frame(s) that affect cluster behavior.
* - Data: carries raw data received from a client connection.
@@ -146,6 +146,7 @@
#include "qpid/framing/AMQP_AllOperations.h"
#include "qpid/framing/AllInvoker.h"
#include "qpid/framing/ClusterConfigChangeBody.h"
+#include "qpid/framing/ClusterClockBody.h"
#include "qpid/framing/ClusterConnectionDeliverCloseBody.h"
#include "qpid/framing/ClusterConnectionAbortBody.h"
#include "qpid/framing/ClusterRetractOfferBody.h"
@@ -198,7 +199,7 @@ namespace _qmf = ::qmf::org::apache::qpid::cluster;
* Currently use SVN revision to avoid clashes with versions from
* different branches.
*/
-const uint32_t Cluster::CLUSTER_VERSION = 1058747;
+const uint32_t Cluster::CLUSTER_VERSION = 1159329;
struct ClusterDispatcher : public framing::AMQP_AllOperations::ClusterHandler {
qpid::cluster::Cluster& cluster;
@@ -214,7 +215,7 @@ struct ClusterDispatcher : public framing::AMQP_AllOperations::ClusterHandler {
{
cluster.initialStatus(
member, version, active, clusterId,
- framing::cluster::StoreState(storeState), shutdownId,
+ framing::cluster::StoreState(storeState), shutdownId,
firstConfig, l);
}
void ready(const std::string& url) {
@@ -230,21 +231,21 @@ struct ClusterDispatcher : public framing::AMQP_AllOperations::ClusterHandler {
cluster.updateOffer(member, updatee, l);
}
void retractOffer(uint64_t updatee) { cluster.retractOffer(member, updatee, l); }
- void messageExpired(uint64_t id) { cluster.messageExpired(member, id, l); }
void errorCheck(uint8_t type, const framing::SequenceNumber& frameSeq) {
cluster.errorCheck(member, type, frameSeq, l);
}
void timerWakeup(const std::string& name) { cluster.timerWakeup(member, name, l); }
- void timerDrop(const std::string& name) { cluster.timerWakeup(member, name, l); }
+ void timerDrop(const std::string& name) { cluster.timerDrop(member, name, l); }
void shutdown(const Uuid& id) { cluster.shutdown(member, id, l); }
void deliverToQueue(const std::string& queue, const std::string& message) {
cluster.deliverToQueue(queue, message, l);
}
+ void clock(uint64_t time) { cluster.clock(time, l); }
bool invoke(AMQBody& body) { return framing::invoke(*this, body).wasHandled(); }
};
Cluster::Cluster(const ClusterSettings& set, broker::Broker& b) :
- settings(set),
+ settings(set),
broker(b),
mgmtObject(0),
poller(b.getPoller()),
@@ -253,7 +254,7 @@ Cluster::Cluster(const ClusterSettings& set, broker::Broker& b) :
self(cpg.self()),
clusterId(true),
mAgent(0),
- expiryPolicy(new ExpiryPolicy(mcast, self, broker.getTimer())),
+ expiryPolicy(new ExpiryPolicy(*this)),
mcast(cpg, poller, boost::bind(&Cluster::leave, this)),
dispatcher(cpg, poller, boost::bind(&Cluster::leave, this)),
deliverEventQueue(boost::bind(&Cluster::deliveredEvent, this, _1),
@@ -277,8 +278,11 @@ Cluster::Cluster(const ClusterSettings& set, broker::Broker& b) :
lastBroker(false),
updateRetracted(false),
updateClosed(false),
- error(*this)
+ error(*this),
+ acl(0)
{
+ broker.setInCluster(true);
+
// We give ownership of the timer to the broker and keep a plain pointer.
// This is OK as it means the timer has the same lifetime as the broker.
timer = new ClusterTimer(*this);
@@ -299,7 +303,7 @@ Cluster::Cluster(const ClusterSettings& set, broker::Broker& b) :
// Load my store status before we go into initialization
if (! broker::NullMessageStore::isNullStore(&broker.getStore())) {
store.load();
- clusterId = store.getClusterId();
+ clusterId = store.getClusterId();
QPID_LOG(notice, "Cluster store state: " << store)
}
cpg.join(name);
@@ -360,14 +364,15 @@ void Cluster::addShadowConnection(const boost::intrusive_ptr<Connection>& c) {
// Safe to use connections here because we're pre-catchup, stalled
// and discarding, so deliveredFrame is not processing any
// connection events.
- assert(discarding);
+ assert(discarding);
pair<ConnectionMap::iterator, bool> ib
= connections.insert(ConnectionMap::value_type(c->getId(), c));
- assert(ib.second);
+ // Like this to avoid tripping up unused variable warning when NDEBUG set
+ if (!ib.second) assert(ib.second);
}
void Cluster::erase(const ConnectionId& id) {
- Lock l(lock);
+ Lock l(lock);
erase(id,l);
}
@@ -393,9 +398,9 @@ std::vector<Url> Cluster::getUrls() const {
std::vector<Url> Cluster::getUrls(Lock&) const {
return map.memberUrls();
-}
+}
-void Cluster::leave() {
+void Cluster::leave() {
Lock l(lock);
leave(l);
}
@@ -405,7 +410,7 @@ void Cluster::leave() {
QPID_LOG(warning, *this << " error leaving cluster: " << e.what()); \
} do {} while(0)
-void Cluster::leave(Lock&) {
+void Cluster::leave(Lock&) {
if (state != LEFT) {
state = LEFT;
QPID_LOG(notice, *this << " leaving cluster " << name);
@@ -424,7 +429,7 @@ void Cluster::deliver(
uint32_t nodeid,
uint32_t pid,
void* msg,
- int msg_len)
+ int msg_len)
{
MemberId from(nodeid, pid);
framing::Buffer buf(static_cast<char*>(msg), msg_len);
@@ -455,7 +460,7 @@ void Cluster::deliveredEvent(const Event& e) {
EventFrame ef(e, e.getFrame());
// Stop the deliverEventQueue on update offers.
// This preserves the connection decoder fragments for an update.
- // Only do this for the two brokers that are directly involved in this
+ // Only do this for the two brokers that are directly involved in this
// offer: the one making the offer, or the one receiving it.
const ClusterUpdateOfferBody* offer = castUpdateOffer(ef.frame.getBody());
if (offer && ( e.getMemberId() == self || MemberId(offer->getUpdatee()) == self) ) {
@@ -465,7 +470,7 @@ void Cluster::deliveredEvent(const Event& e) {
}
deliverFrame(ef);
}
- else if(!discarding) {
+ else if(!discarding) {
if (e.isControl())
deliverFrame(EventFrame(e, e.getFrame()));
else {
@@ -507,7 +512,7 @@ void Cluster::deliveredFrame(const EventFrame& efConst) {
// the event queue.
e.frame = AMQFrame(
ClusterRetractOfferBody(ProtocolVersion(), offer->getUpdatee()));
- deliverEventQueue.start();
+ deliverEventQueue.start();
}
// Process each frame through the error checker.
if (error.isUnresolved()) {
@@ -515,14 +520,14 @@ void Cluster::deliveredFrame(const EventFrame& efConst) {
while (error.canProcess()) // There is a frame ready to process.
processFrame(error.getNext(), l);
}
- else
+ else
processFrame(e, l);
}
void Cluster::processFrame(const EventFrame& e, Lock& l) {
if (e.isCluster()) {
- QPID_LOG(trace, *this << " DLVR: " << e);
+ QPID_LOG_IF(trace, loggable(e.frame), *this << " DLVR: " << e);
ClusterDispatcher dispatch(*this, e.connectionId.getMember(), l);
if (!framing::invoke(dispatch, *e.frame.getBody()).wasHandled())
throw Exception(QPID_MSG("Invalid cluster control"));
@@ -531,14 +536,15 @@ void Cluster::processFrame(const EventFrame& e, Lock& l) {
map.incrementFrameSeq();
ConnectionPtr connection = getConnection(e, l);
if (connection) {
- QPID_LOG(trace, *this << " DLVR " << map.getFrameSeq() << ": " << e);
+ QPID_LOG_IF(trace, loggable(e.frame),
+ *this << " DLVR " << map.getFrameSeq() << ": " << e);
connection->deliveredFrame(e);
}
else
- QPID_LOG(trace, *this << " DROP (no connection): " << e);
+ throw Exception(QPID_MSG("Unknown connection: " << e));
}
else // Drop connection frames while state < CATCHUP
- QPID_LOG(trace, *this << " DROP (joining): " << e);
+ QPID_LOG_IF(trace, loggable(e.frame), *this << " DROP (joining): " << e);
}
// Called in deliverFrameQueue thread
@@ -577,7 +583,7 @@ Cluster::ConnectionVector Cluster::getConnections(Lock&) {
}
// CPG config-change callback.
-void Cluster::configChange (
+void Cluster::configChange (
cpg_handle_t /*handle*/,
const cpg_name */*group*/,
const cpg_address *members, int nMembers,
@@ -607,7 +613,7 @@ void Cluster::setReady(Lock&) {
}
// Set the management status from the Cluster::state.
-//
+//
// NOTE: Management updates are sent based on property changes. In
// order to keep consistency across the cluster, we touch the local
// management status property even if it is locally unchanged for any
@@ -618,7 +624,7 @@ void Cluster::setMgmtStatus(Lock&) {
}
void Cluster::initMapCompleted(Lock& l) {
- // Called on completion of the initial status map.
+ // Called on completion of the initial status map.
QPID_LOG(debug, *this << " initial status map complete. ");
setMgmtStatus(l);
if (state == PRE_INIT) {
@@ -665,6 +671,8 @@ void Cluster::initMapCompleted(Lock& l) {
else { // I can go ready.
discarding = false;
setReady(l);
+ // Must be called *before* memberUpdate so first update will be generated.
+ failoverExchange->setReady();
memberUpdate(l);
updateMgmtMembership(l);
mcast.mcastControl(ClusterReadyBody(ProtocolVersion(), myUrl.str()), self);
@@ -701,8 +709,8 @@ void Cluster::configChange(const MemberId&,
if (initMap.isResendNeeded()) {
mcast.mcastControl(
ClusterInitialStatusBody(
- ProtocolVersion(), CLUSTER_VERSION, state > INIT, clusterId,
- store.getState(), store.getShutdownId(),
+ ProtocolVersion(), CLUSTER_VERSION, state > INIT, clusterId,
+ store.getState(), store.getShutdownId(),
initMap.getFirstConfigStr()
),
self);
@@ -717,6 +725,20 @@ void Cluster::configChange(const MemberId&,
updateMgmtMembership(l); // Update on every config change for consistency
}
+struct ClusterClockTask : public sys::TimerTask {
+ Cluster& cluster;
+ sys::Timer& timer;
+
+ ClusterClockTask(Cluster& cluster, sys::Timer& timer, uint16_t clockInterval)
+ : TimerTask(Duration(clockInterval * TIME_MSEC),"ClusterClock"), cluster(cluster), timer(timer) {}
+
+ void fire() {
+ cluster.sendClockUpdate();
+ setupNextFire();
+ timer.add(this);
+ }
+};
+
void Cluster::becomeElder(Lock&) {
if (elder) return; // We were already the elder.
// We are the oldest, reactive links if necessary
@@ -724,6 +746,8 @@ void Cluster::becomeElder(Lock&) {
elder = true;
broker.getLinks().setPassive(false);
timer->becomeElder();
+
+ clockTimer.add(new ClusterClockTask(*this, clockTimer, settings.clockInterval));
}
void Cluster::makeOffer(const MemberId& id, Lock& ) {
@@ -759,7 +783,7 @@ std::string Cluster::debugSnapshot() {
// point we know the poller has stopped so no poller callbacks will be
// invoked. We must ensure that CPG has also shut down so no CPG
// callbacks will be invoked.
-//
+//
void Cluster::brokerShutdown() {
sys::ClusterSafeScope css; // Don't trigger cluster-safe asserts.
try { cpg.shutdown(); }
@@ -775,7 +799,7 @@ void Cluster::updateRequest(const MemberId& id, const std::string& url, Lock& l)
}
void Cluster::initialStatus(const MemberId& member, uint32_t version, bool active,
- const framing::Uuid& id,
+ const framing::Uuid& id,
framing::cluster::StoreState store,
const framing::Uuid& shutdownId,
const std::string& firstConfig,
@@ -833,6 +857,8 @@ void Cluster::updateOffer(const MemberId& updater, uint64_t updateeInt, Lock& l)
else if (updatee == self && url) {
assert(state == JOINER);
state = UPDATEE;
+ acl = broker.getAcl();
+ broker.setAcl(0); // Disable ACL during update
QPID_LOG(notice, *this << " receiving update from " << updater);
checkUpdateIn(l);
}
@@ -844,7 +870,7 @@ void Cluster::updateOffer(const MemberId& updater, uint64_t updateeInt, Lock& l)
if (updatee != self && url) {
QPID_LOG(debug, debugSnapshot());
if (mAgent) mAgent->clusterUpdate();
- // Updatee will call clusterUpdate when update completes
+ // Updatee will call clusterUpdate() via checkUpdateIn() when update completes
}
}
@@ -925,13 +951,15 @@ void Cluster::checkUpdateIn(Lock& l) {
if (!updateClosed) return; // Wait till update connection closes.
if (updatedMap) { // We're up to date
map = *updatedMap;
- failoverExchange->setUrls(getUrls(l));
mcast.mcastControl(ClusterReadyBody(ProtocolVersion(), myUrl.str()), self);
state = CATCHUP;
memberUpdate(l);
+ // Must be called *after* memberUpdate() to avoid sending an extra update.
+ failoverExchange->setReady();
// NB: don't updateMgmtMembership() here as we are not in the deliver
// thread. It will be updated on delivery of the "ready" we just mcast.
broker.setClusterUpdatee(false);
+ broker.setAcl(acl); // Restore ACL
discarding = false; // OK to set, we're stalled for update.
QPID_LOG(notice, *this << " update complete, starting catch-up.");
QPID_LOG(debug, debugSnapshot()); // OK to call because we're stalled.
@@ -941,6 +969,10 @@ void Cluster::checkUpdateIn(Lock& l) {
mAgent->suppress(false); // Enable management output.
mAgent->clusterUpdate();
}
+ // Restore alternate exchange settings on exchanges.
+ broker.getExchanges().eachExchange(
+ boost::bind(&broker::Exchange::recoveryComplete, _1,
+ boost::ref(broker.getExchanges())));
enableClusterSafe(); // Enable cluster-safe assertions
deliverEventQueue.start();
}
@@ -969,7 +1001,7 @@ void Cluster::updateOutDone(Lock& l) {
void Cluster::updateOutError(const std::exception& e) {
Monitor::ScopedLock l(lock);
- QPID_LOG(error, *this << " error sending update: " << e.what());
+ QPID_LOG(error, *this << " error sending update: " << e.what());
updateOutDone(l);
}
@@ -1067,7 +1099,7 @@ void Cluster::memberUpdate(Lock& l) {
void Cluster::updateMgmtMembership(Lock& l) {
if (!mgmtObject) return;
std::vector<Url> urls = getUrls(l);
- mgmtObject->set_clusterSize(urls.size());
+ mgmtObject->set_clusterSize(urls.size());
string urlstr;
for(std::vector<Url>::iterator i = urls.begin(); i != urls.end(); i++ ) {
if (i != urls.begin()) urlstr += ";";
@@ -1114,10 +1146,6 @@ void Cluster::setClusterId(const Uuid& uuid, Lock&) {
QPID_LOG(notice, *this << " cluster-uuid = " << clusterId);
}
-void Cluster::messageExpired(const MemberId&, uint64_t id, Lock&) {
- expiryPolicy->deliverExpire(id);
-}
-
void Cluster::errorCheck(const MemberId& from, uint8_t type, framing::SequenceNumber frameSeq, Lock&) {
// If we see an errorCheck here (rather than in the ErrorCheck
// class) then we have processed succesfully past the point of the
@@ -1155,6 +1183,35 @@ void Cluster::deliverToQueue(const std::string& queue, const std::string& messag
q->deliver(msg);
}
+sys::AbsTime Cluster::getClusterTime() {
+ Mutex::ScopedLock l(lock);
+ return clusterTime;
+}
+
+// This method is called during update on the updatee to set the initial cluster time.
+void Cluster::clock(const uint64_t time) {
+ Mutex::ScopedLock l(lock);
+ clock(time, l);
+}
+
+// called when broadcast message received
+void Cluster::clock(const uint64_t time, Lock&) {
+ clusterTime = AbsTime(EPOCH, time);
+ AbsTime now = AbsTime::now();
+
+ if (!elder) {
+ clusterTimeOffset = Duration(now, clusterTime);
+ }
+}
+
+// called by elder timer to send clock broadcast
+void Cluster::sendClockUpdate() {
+ Mutex::ScopedLock l(lock);
+ int64_t nanosecondsSinceEpoch = Duration(EPOCH, now());
+ nanosecondsSinceEpoch += clusterTimeOffset;
+ mcast.mcastControl(ClusterClockBody(ProtocolVersion(), nanosecondsSinceEpoch), self);
+}
+
bool Cluster::deferDeliveryImpl(const std::string& queue,
const boost::intrusive_ptr<broker::Message>& msg)
{
@@ -1167,4 +1224,12 @@ bool Cluster::deferDeliveryImpl(const std::string& queue,
return true;
}
+bool Cluster::loggable(const AMQFrame& f) {
+ const AMQMethodBody* method = (f.getMethod());
+ if (!method) return true; // Not a method
+ bool isClock = method->amqpClassId() == ClusterClockBody::CLASS_ID
+ && method->amqpMethodId() == ClusterClockBody::METHOD_ID;
+ return !isClock;
+}
+
}} // namespace qpid::cluster
diff --git a/cpp/src/qpid/cluster/Cluster.h b/cpp/src/qpid/cluster/Cluster.h
index 8f73c6acca..ccec4948e6 100644
--- a/cpp/src/qpid/cluster/Cluster.h
+++ b/cpp/src/qpid/cluster/Cluster.h
@@ -56,17 +56,25 @@ namespace qpid {
namespace broker {
class Message;
+class AclModule;
}
namespace framing {
+class AMQFrame;
class AMQBody;
-class Uuid;
+struct Uuid;
+}
+
+namespace sys {
+class Timer;
+class AbsTime;
+class Duration;
}
namespace cluster {
class Connection;
-class EventFrame;
+struct EventFrame;
class ClusterTimer;
class UpdateDataExchange;
@@ -89,10 +97,10 @@ class Cluster : private Cpg::Handler, public management::Manageable {
void initialize();
// Connection map.
- void addLocalConnection(const ConnectionPtr&);
- void addShadowConnection(const ConnectionPtr&);
- void erase(const ConnectionId&);
-
+ void addLocalConnection(const ConnectionPtr&);
+ void addShadowConnection(const ConnectionPtr&);
+ void erase(const ConnectionId&);
+
// URLs of current cluster members.
std::vector<std::string> getIds() const;
std::vector<Url> getUrls() const;
@@ -107,7 +115,7 @@ class Cluster : private Cpg::Handler, public management::Manageable {
void updateInRetracted();
// True if we are expecting to receive catch-up connections.
bool isExpectingUpdate();
-
+
MemberId getId() const;
broker::Broker& getBroker() const;
Multicaster& getMulticast() { return mcast; }
@@ -135,6 +143,12 @@ class Cluster : private Cpg::Handler, public management::Manageable {
bool deferDeliveryImpl(const std::string& queue,
const boost::intrusive_ptr<broker::Message>& msg);
+ sys::AbsTime getClusterTime();
+ void sendClockUpdate();
+ void clock(const uint64_t time);
+
+ static bool loggable(const framing::AMQFrame&); // True if the frame should be logged.
+
private:
typedef sys::Monitor::ScopedLock Lock;
@@ -144,10 +158,10 @@ class Cluster : private Cpg::Handler, public management::Manageable {
/** Version number of the cluster protocol, to avoid mixed versions. */
static const uint32_t CLUSTER_VERSION;
-
+
// NB: A dummy Lock& parameter marks functions that must only be
// called with Cluster::lock locked.
-
+
void leave(Lock&);
std::vector<std::string> getIds(Lock&) const;
std::vector<Url> getUrls(Lock&) const;
@@ -156,11 +170,11 @@ class Cluster : private Cpg::Handler, public management::Manageable {
void brokerShutdown();
// == Called in deliverEventQueue thread
- void deliveredEvent(const Event&);
+ void deliveredEvent(const Event&);
// == Called in deliverFrameQueue thread
- void deliveredFrame(const EventFrame&);
- void processFrame(const EventFrame&, Lock&);
+ void deliveredFrame(const EventFrame&);
+ void processFrame(const EventFrame&, Lock&);
// Cluster controls implement XML methods from cluster.xml.
void updateRequest(const MemberId&, const std::string&, Lock&);
@@ -180,12 +194,12 @@ class Cluster : private Cpg::Handler, public management::Manageable {
const std::string& left,
const std::string& joined,
Lock& l);
- void messageExpired(const MemberId&, uint64_t, Lock& l);
void errorCheck(const MemberId&, uint8_t type, SequenceNumber frameSeq, Lock&);
void timerWakeup(const MemberId&, const std::string& name, Lock&);
void timerDrop(const MemberId&, const std::string& name, Lock&);
void shutdown(const MemberId&, const framing::Uuid& shutdownId, Lock&);
void deliverToQueue(const std::string& queue, const std::string& message, Lock&);
+ void clock(const uint64_t time, Lock&);
// Helper functions
ConnectionPtr getConnection(const EventFrame&, Lock&);
@@ -195,7 +209,7 @@ class Cluster : private Cpg::Handler, public management::Manageable {
void setReady(Lock&);
void memberUpdate(Lock&);
void setClusterId(const framing::Uuid&, Lock&);
- void erase(const ConnectionId&, Lock&);
+ void erase(const ConnectionId&, Lock&);
void requestUpdate(Lock& );
void initMapCompleted(Lock&);
void becomeElder(Lock&);
@@ -203,7 +217,7 @@ class Cluster : private Cpg::Handler, public management::Manageable {
void updateMgmtMembership(Lock&);
// == Called in CPG dispatch thread
- void deliver( // CPG deliver callback.
+ void deliver( // CPG deliver callback.
cpg_handle_t /*handle*/,
const struct cpg_name *group,
uint32_t /*nodeid*/,
@@ -212,7 +226,7 @@ class Cluster : private Cpg::Handler, public management::Manageable {
int /*msg_len*/);
void deliverEvent(const Event&);
-
+
void configChange( // CPG config change callback.
cpg_handle_t /*handle*/,
const struct cpg_name */*group*/,
@@ -263,7 +277,7 @@ class Cluster : private Cpg::Handler, public management::Manageable {
// Used only in deliverEventQueue thread or when stalled for update.
Decoder decoder;
bool discarding;
-
+
// Remaining members are protected by lock.
mutable sys::Monitor lock;
@@ -276,7 +290,7 @@ class Cluster : private Cpg::Handler, public management::Manageable {
JOINER, ///< Sent update request, waiting for update offer.
UPDATEE, ///< Stalled receive queue at update offer, waiting for update to complete.
CATCHUP, ///< Update complete, unstalled but has not yet seen own "ready" event.
- READY, ///< Fully operational
+ READY, ///< Fully operational
OFFER, ///< Sent an offer, waiting for accept/reject.
UPDATER, ///< Offer accepted, sending a state update.
LEFT ///< Final state, left the cluster.
@@ -296,9 +310,13 @@ class Cluster : private Cpg::Handler, public management::Manageable {
ErrorCheck error;
UpdateReceiver updateReceiver;
ClusterTimer* timer;
+ sys::Timer clockTimer;
+ sys::AbsTime clusterTime;
+ sys::Duration clusterTimeOffset;
+ broker::AclModule* acl;
friend std::ostream& operator<<(std::ostream&, const Cluster&);
- friend class ClusterDispatcher;
+ friend struct ClusterDispatcher;
};
}} // namespace qpid::cluster
diff --git a/cpp/src/qpid/cluster/ClusterMap.cpp b/cpp/src/qpid/cluster/ClusterMap.cpp
index 040e129970..a8389095c9 100644
--- a/cpp/src/qpid/cluster/ClusterMap.cpp
+++ b/cpp/src/qpid/cluster/ClusterMap.cpp
@@ -50,11 +50,6 @@ void insertFieldTableFromMapValue(FieldTable& ft, const ClusterMap::Map::value_t
ft.setString(vt.first.str(), vt.second.str());
}
-void assignFieldTable(FieldTable& ft, const ClusterMap::Map& map) {
- ft.clear();
- for_each(map.begin(), map.end(), bind(&insertFieldTableFromMapValue, ref(ft), _1));
-}
-
}
ClusterMap::ClusterMap() : frameSeq(0) {}
diff --git a/cpp/src/qpid/cluster/ClusterPlugin.cpp b/cpp/src/qpid/cluster/ClusterPlugin.cpp
index 2962daaa07..69ba095f16 100644
--- a/cpp/src/qpid/cluster/ClusterPlugin.cpp
+++ b/cpp/src/qpid/cluster/ClusterPlugin.cpp
@@ -72,6 +72,7 @@ struct ClusterOptions : public Options {
("cluster-cman", optValue(settings.quorum), "Integrate with Cluster Manager (CMAN) cluster.")
#endif
("cluster-size", optValue(settings.size, "N"), "Wait for N cluster members before allowing clients to connect.")
+ ("cluster-clock-interval", optValue(settings.clockInterval,"N"), "How often to broadcast the current time to the cluster nodes, in milliseconds. A value between 5 and 1000 is recommended.")
("cluster-read-max", optValue(settings.readMax,"N"), "Experimental: flow-control limit reads per connection. 0=no limit.")
;
}
diff --git a/cpp/src/qpid/cluster/ClusterSettings.h b/cpp/src/qpid/cluster/ClusterSettings.h
index 8e708aa139..2f7b5be20a 100644
--- a/cpp/src/qpid/cluster/ClusterSettings.h
+++ b/cpp/src/qpid/cluster/ClusterSettings.h
@@ -35,8 +35,9 @@ struct ClusterSettings {
size_t readMax;
std::string username, password, mechanism;
size_t size;
+ uint16_t clockInterval;
- ClusterSettings() : quorum(false), readMax(10), size(1)
+ ClusterSettings() : quorum(false), readMax(10), size(1), clockInterval(10)
{}
Url getUrl(uint16_t port) const {
diff --git a/cpp/src/qpid/cluster/ClusterTimer.cpp b/cpp/src/qpid/cluster/ClusterTimer.cpp
index f6e1c7a849..b4f7d00f38 100644
--- a/cpp/src/qpid/cluster/ClusterTimer.cpp
+++ b/cpp/src/qpid/cluster/ClusterTimer.cpp
@@ -70,6 +70,7 @@ void ClusterTimer::add(intrusive_ptr<TimerTask> task)
if (i != map.end())
throw Exception(QPID_MSG("Task already exists with name " << task->getName()));
map[task->getName()] = task;
+
// Only the elder actually activates the task with the Timer base class.
if (cluster.isElder()) {
QPID_LOG(trace, "Elder activating cluster timer task " << task->getName());
@@ -112,6 +113,9 @@ void ClusterTimer::deliverWakeup(const std::string& name) {
else {
intrusive_ptr<TimerTask> t = i->second;
map.erase(i);
+ // Move the nextFireTime so readyToFire() is true. This is to ensure we
+ // don't get an error if the fired task calls setupNextFire()
+ t->setFired();
Timer::fire(t);
}
}
diff --git a/cpp/src/qpid/cluster/Connection.cpp b/cpp/src/qpid/cluster/Connection.cpp
index e9b718e6de..394749aad2 100644
--- a/cpp/src/qpid/cluster/Connection.cpp
+++ b/cpp/src/qpid/cluster/Connection.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
@@ -24,6 +24,8 @@
#include "Cluster.h"
#include "UpdateReceiver.h"
#include "qpid/assert.h"
+#include "qpid/broker/DtxAck.h"
+#include "qpid/broker/DtxBuffer.h"
#include "qpid/broker/SessionState.h"
#include "qpid/broker/SemanticState.h"
#include "qpid/broker/TxBuffer.h"
@@ -35,6 +37,7 @@
#include "qpid/broker/Fairshare.h"
#include "qpid/broker/Link.h"
#include "qpid/broker/Bridge.h"
+#include "qpid/broker/StatefulQueueObserver.h"
#include "qpid/broker/Queue.h"
#include "qpid/framing/enum.h"
#include "qpid/framing/AMQFrame.h"
@@ -78,7 +81,7 @@ const std::string shadowPrefix("[shadow]");
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), output(*this, out),
+ : cluster(c), self(id), catchUp(false), announced(false), output(*this, out),
connectionCtor(&output, cluster.getBroker(), mgmtId, external, false, 0, true),
expectProtocolHeader(false),
mcastFrameHandler(cluster.getMulticast(), self),
@@ -90,13 +93,15 @@ Connection::Connection(Cluster& c, sys::ConnectionOutputHandler& out,
Connection::Connection(Cluster& c, sys::ConnectionOutputHandler& out,
const std::string& mgmtId, MemberId member,
bool isCatchUp, bool isLink, const qpid::sys::SecuritySettings& external
-) : cluster(c), self(member, ++idCounter), catchUp(isCatchUp), output(*this, out),
+) : cluster(c), self(member, ++idCounter), catchUp(isCatchUp), announced(false), output(*this, out),
connectionCtor(&output, cluster.getBroker(),
mgmtId,
external,
isLink,
isCatchUp ? ++catchUpId : 0,
- isCatchUp), // isCatchUp => shadow
+ // The first catch-up connection is not considered a shadow
+ // as it needs to be authenticated.
+ isCatchUp && self.second > 1),
expectProtocolHeader(isLink),
mcastFrameHandler(cluster.getMulticast(), self),
updateIn(c.getUpdateReceiver()),
@@ -113,7 +118,7 @@ Connection::Connection(Cluster& c, sys::ConnectionOutputHandler& out,
if (!updateIn.nextShadowMgmtId.empty())
connectionCtor.mgmtId = updateIn.nextShadowMgmtId;
updateIn.nextShadowMgmtId.clear();
- }
+ }
init();
QPID_LOG(debug, cluster << " local connection " << *this);
}
@@ -143,7 +148,7 @@ void Connection::init() {
// Called when we have consumed a read buffer to give credit to the
// connection layer to continue reading.
void Connection::giveReadCredit(int credit) {
- if (cluster.getSettings().readMax && credit)
+ if (cluster.getSettings().readMax && credit)
output.giveReadCredit(credit);
}
@@ -166,7 +171,7 @@ void Connection::announce(
AMQFrame frame;
while (frame.decode(buf))
connection->received(frame);
- connection->setUserId(username);
+ connection->setUserId(username);
}
// Do managment actions now that the connection is replicated.
connection->raiseConnectEvent();
@@ -193,7 +198,7 @@ void Connection::received(framing::AMQFrame& f) {
<< *this << ": " << f);
return;
}
- QPID_LOG(trace, cluster << " RECV " << *this << ": " << f);
+ QPID_LOG_IF(trace, Cluster::loggable(f), cluster << " RECV " << *this << ": " << f);
if (isLocal()) { // Local catch-up connection.
currentChannel = f.getChannel();
if (!framing::invoke(*this, *f.getBody()).wasHandled())
@@ -201,7 +206,7 @@ void Connection::received(framing::AMQFrame& f) {
}
else { // Shadow or updated catch-up connection.
if (f.getMethod() && f.getMethod()->isA<ConnectionCloseBody>()) {
- if (isShadow())
+ if (isShadow())
cluster.addShadowConnection(this);
AMQFrame ok((ConnectionCloseOkBody()));
connection->getOutput().send(ok);
@@ -213,16 +218,9 @@ void Connection::received(framing::AMQFrame& f) {
}
}
-bool Connection::checkUnsupported(const AMQBody& body) {
- std::string message;
- if (body.getMethod()) {
- switch (body.getMethod()->amqpClassId()) {
- case DTX_CLASS_ID: message = "DTX transactions are not currently supported by cluster."; break;
- }
- }
- if (!message.empty())
- connection->close(connection::CLOSE_CODE_FRAMING_ERROR, message);
- return !message.empty();
+bool Connection::checkUnsupported(const AMQBody&) {
+ // Throw an exception for unsupported commands. Currently all are supported.
+ return false;
}
struct GiveReadCreditOnExit {
@@ -241,7 +239,7 @@ void Connection::deliverDoOutput(uint32_t limit) {
void Connection::deliveredFrame(const EventFrame& f) {
GiveReadCreditOnExit gc(*this, f.readCredit);
assert(!catchUp);
- currentChannel = f.frame.getChannel();
+ currentChannel = f.frame.getChannel();
if (f.frame.getBody() // frame can be emtpy with just readCredit
&& !framing::invoke(*this, *f.frame.getBody()).wasHandled() // Connection contol.
&& !checkUnsupported(*f.frame.getBody())) // Unsupported operation.
@@ -255,7 +253,7 @@ void Connection::deliveredFrame(const EventFrame& f) {
}
}
-// A local connection is closed by the network layer.
+// A local connection is closed by the network layer. Called in the connection thread.
void Connection::closed() {
try {
if (isUpdated()) {
@@ -272,8 +270,9 @@ void Connection::closed() {
// closed and process any outstanding frames from the cluster
// until self-delivery of deliver-close.
output.closeOutput();
- cluster.getMulticast().mcastControl(
- ClusterConnectionDeliverCloseBody(), self);
+ if (announced)
+ cluster.getMulticast().mcastControl(
+ ClusterConnectionDeliverCloseBody(), self);
}
}
catch (const std::exception& e) {
@@ -287,7 +286,7 @@ void Connection::deliverClose () {
cluster.erase(self);
}
-// Close the connection
+// Close the connection
void Connection::close() {
if (connection.get()) {
QPID_LOG(debug, cluster << " closed connection " << *this);
@@ -320,10 +319,10 @@ size_t Connection::decode(const char* data, size_t size) {
while (localDecoder.decode(buf))
received(localDecoder.getFrame());
if (!wasOpen && connection->isOpen()) {
- // Connections marked as federation links are allowed to proxy
+ // Connections marked with setUserProxyAuth are allowed to proxy
// messages with user-ID that doesn't match the connection's
// authenticated ID. This is important for updates.
- connection->setFederationLink(isCatchUp());
+ connection->setUserProxyAuth(isCatchUp());
}
}
else { // Multicast local connections.
@@ -332,9 +331,9 @@ size_t Connection::decode(const char* data, size_t size) {
if (!checkProtocolHeader(ptr, size)) // Updates ptr
return 0; // Incomplete header
- if (!connection->isOpen())
+ if (!connection->isOpen())
processInitialFrames(ptr, end-ptr); // Updates ptr
-
+
if (connection->isOpen() && end - ptr > 0) {
// We're multi-casting, we will give read credit on delivery.
grc.credit = 0;
@@ -384,6 +383,7 @@ void Connection::processInitialFrames(const char*& ptr, size_t size) {
connection->getUserId(),
initialFrames),
getId());
+ announced = true;
initialFrames.clear();
}
}
@@ -406,11 +406,11 @@ void Connection::shadowSetUser(const std::string& userId) {
void Connection::consumerState(const string& name, bool blocked, bool notifyEnabled, const SequenceNumber& position)
{
- broker::SemanticState::ConsumerImpl& c = semanticState().find(name);
- c.position = position;
- c.setBlocked(blocked);
- if (notifyEnabled) c.enableNotify(); else c.disableNotify();
- updateIn.consumerNumbering.add(c.shared_from_this());
+ broker::SemanticState::ConsumerImpl::shared_ptr c = semanticState().find(name);
+ c->position = position;
+ c->setBlocked(blocked);
+ if (notifyEnabled) c->enableNotify(); else c->disableNotify();
+ updateIn.consumerNumbering.add(c);
}
@@ -421,7 +421,8 @@ void Connection::sessionState(
const SequenceNumber& expected,
const SequenceNumber& received,
const SequenceSet& unknownCompleted,
- const SequenceSet& receivedIncomplete)
+ const SequenceSet& receivedIncomplete,
+ bool dtxSelected)
{
sessionState().setState(
replayStart,
@@ -431,8 +432,10 @@ void Connection::sessionState(
received,
unknownCompleted,
receivedIncomplete);
- QPID_LOG(debug, cluster << " received session state update for " << sessionState().getId());
- // The output tasks will be added later in the update process.
+ if (dtxSelected) semanticState().selectDtx();
+ QPID_LOG(debug, cluster << " received session state update for "
+ << sessionState().getId());
+ // The output tasks will be added later in the update process.
connection->getOutputTasks().removeAll();
}
@@ -441,7 +444,7 @@ void Connection::outputTask(uint16_t channel, const std::string& name) {
if (!session)
throw Exception(QPID_MSG(cluster << " channel not attached " << *this
<< "[" << channel << "] "));
- OutputTask* task = &session->getSemanticState().find(name);
+ OutputTask* task = session->getSemanticState().find(name).get();
connection->getOutputTasks().addOutputTask(task);
}
@@ -461,11 +464,24 @@ void Connection::shadowReady(
output.setSendMax(sendMax);
}
+void Connection::setDtxBuffer(const UpdateReceiver::DtxBufferRef& bufRef) {
+ broker::DtxManager& mgr = cluster.getBroker().getDtxManager();
+ broker::DtxWorkRecord* record = mgr.getWork(bufRef.xid);
+ broker::DtxBuffer::shared_ptr buffer = (*record)[bufRef.index];
+ if (bufRef.suspended)
+ bufRef.semanticState->getSuspendedXids()[bufRef.xid] = buffer;
+ else
+ bufRef.semanticState->setDtxBuffer(buffer);
+}
+
+// Marks the end of the update.
void Connection::membership(const FieldTable& joiners, const FieldTable& members,
const framing::SequenceNumber& frameSeq)
{
QPID_LOG(debug, cluster << " incoming update complete on connection " << *this);
updateIn.consumerNumbering.clear();
+ for_each(updateIn.dtxBuffers.begin(), updateIn.dtxBuffers.end(),
+ boost::bind(&Connection::setDtxBuffer, this, _1));
closeUpdated();
cluster.updateInDone(ClusterMap(joiners, members, frameSeq));
}
@@ -478,7 +494,7 @@ void Connection::retractOffer() {
void Connection::closeUpdated() {
self.second = 0; // Mark this as completed update connection.
- if (connection.get())
+ if (connection.get())
connection->close(connection::CLOSE_CODE_NORMAL, "OK");
}
@@ -529,12 +545,20 @@ void Connection::deliveryRecord(const string& qname,
m = getUpdateMessage();
m.queue = queue.get();
m.position = position;
- if (enqueued) queue->updateEnqueued(m); //inform queue of the message
+ if (enqueued) queue->updateEnqueued(m); //inform queue of the message
} else { // Message at original position in original queue
- m = queue->find(position);
+ queue->find(position, m);
}
- if (!m.payload)
- throw Exception(QPID_MSG("deliveryRecord no update message"));
+ // FIXME aconway 2011-08-19: removed:
+ // if (!m.payload)
+ // throw Exception(QPID_MSG("deliveryRecord no update message"));
+ //
+ // It seems this could happen legitimately in the case one
+ // session browses message M, then another session acquires
+ // it. In that case the browsers delivery record is !acquired
+ // but the message is not on its original Queue. In that case
+ // we'll get a deliveryRecord with no payload for the browser.
+ //
}
broker::DeliveryRecord dr(m, queue, tag, acquired, accepted, windowing, credit);
@@ -542,7 +566,11 @@ void Connection::deliveryRecord(const string& qname,
if (cancelled) dr.cancel(dr.getTag());
if (completed) dr.complete();
if (ended) dr.setEnded(); // Exsitance of message
- semanticState().record(dr); // Part of the session's unacked list.
+
+ if (dtxBuffer) // Record for next dtx-ack
+ dtxAckRecords.push_back(dr);
+ else
+ semanticState().record(dr); // Record on session's unacked list.
}
void Connection::queuePosition(const string& qname, const SequenceNumber& position) {
@@ -556,8 +584,46 @@ void Connection::queueFairshareState(const std::string& qname, const uint8_t pri
}
}
-void Connection::expiryId(uint64_t id) {
- cluster.getExpiryPolicy().setId(id);
+
+namespace {
+// find a StatefulQueueObserver that matches a given identifier
+class ObserverFinder {
+ const std::string id;
+ boost::shared_ptr<broker::QueueObserver> target;
+ ObserverFinder(const ObserverFinder&) {}
+ public:
+ ObserverFinder(const std::string& _id) : id(_id) {}
+ broker::StatefulQueueObserver *getObserver()
+ {
+ if (target)
+ return dynamic_cast<broker::StatefulQueueObserver *>(target.get());
+ return 0;
+ }
+ void operator() (boost::shared_ptr<broker::QueueObserver> o)
+ {
+ if (!target) {
+ broker::StatefulQueueObserver *p = dynamic_cast<broker::StatefulQueueObserver *>(o.get());
+ if (p && p->getId() == id) {
+ target = o;
+ }
+ }
+ }
+};
+}
+
+
+void Connection::queueObserverState(const std::string& qname, const std::string& observerId, const FieldTable& state)
+{
+ boost::shared_ptr<broker::Queue> queue(findQueue(qname));
+ ObserverFinder finder(observerId); // find this observer
+ queue->eachObserver<ObserverFinder &>(finder);
+ broker::StatefulQueueObserver *so = finder.getObserver();
+ if (so) {
+ so->setState( state );
+ QPID_LOG(debug, "updated queue observer " << observerId << "'s state on queue " << qname << "; ...");
+ return;
+ }
+ QPID_LOG(error, "Failed to find observer " << observerId << " state on queue " << qname << "; this will result in inconsistencies.");
}
std::ostream& operator<<(std::ostream& o, const Connection& c) {
@@ -574,6 +640,7 @@ std::ostream& operator<<(std::ostream& o, const Connection& c) {
void Connection::txStart() {
txBuffer.reset(new broker::TxBuffer());
}
+
void Connection::txAccept(const framing::SequenceSet& acked) {
txBuffer->enlist(boost::shared_ptr<broker::TxAccept>(
new broker::TxAccept(acked, semanticState().getUnacked())));
@@ -589,9 +656,11 @@ void Connection::txEnqueue(const std::string& queue) {
new broker::RecoveredEnqueue(findQueue(queue), getUpdateMessage().payload)));
}
-void Connection::txPublish(const framing::Array& queues, bool delivered) {
- boost::shared_ptr<broker::TxPublish> txPub(new broker::TxPublish(getUpdateMessage().payload));
- for (framing::Array::const_iterator i = queues.begin(); i != queues.end(); ++i)
+void Connection::txPublish(const framing::Array& queues, bool delivered)
+{
+ boost::shared_ptr<broker::TxPublish> txPub(
+ new broker::TxPublish(getUpdateMessage().payload));
+ for (framing::Array::const_iterator i = queues.begin(); i != queues.end(); ++i)
txPub->deliverTo(findQueue((*i)->get<std::string>()));
txPub->delivered = delivered;
txBuffer->enlist(txPub);
@@ -605,6 +674,51 @@ void Connection::accumulatedAck(const qpid::framing::SequenceSet& s) {
semanticState().setAccumulatedAck(s);
}
+void Connection::dtxStart(const std::string& xid,
+ bool ended,
+ bool suspended,
+ bool failed,
+ bool expired)
+{
+ dtxBuffer.reset(new broker::DtxBuffer(xid, ended, suspended, failed, expired));
+ txBuffer = dtxBuffer;
+}
+
+void Connection::dtxEnd() {
+ broker::DtxManager& mgr = cluster.getBroker().getDtxManager();
+ std::string xid = dtxBuffer->getXid();
+ if (mgr.exists(xid))
+ mgr.join(xid, dtxBuffer);
+ else
+ mgr.start(xid, dtxBuffer);
+ dtxBuffer.reset();
+ txBuffer.reset();
+}
+
+// Sent after all DeliveryRecords for a dtx-ack have been collected in dtxAckRecords
+void Connection::dtxAck() {
+ dtxBuffer->enlist(
+ boost::shared_ptr<broker::DtxAck>(new broker::DtxAck(dtxAckRecords)));
+ dtxAckRecords.clear();
+}
+
+void Connection::dtxBufferRef(const std::string& xid, uint32_t index, bool suspended) {
+ // Save the association between DtxBuffers and the session so we
+ // can set the DtxBuffers at the end of the update when the
+ // DtxManager has been replicated.
+ updateIn.dtxBuffers.push_back(
+ UpdateReceiver::DtxBufferRef(xid, index, suspended, &semanticState()));
+}
+
+// Sent at end of work record.
+void Connection::dtxWorkRecord(const std::string& xid, bool prepared, uint32_t timeout)
+{
+ broker::DtxManager& mgr = cluster.getBroker().getDtxManager();
+ if (timeout) mgr.setTimeout(xid, timeout);
+ if (prepared) mgr.prepare(xid);
+}
+
+
void Connection::exchange(const std::string& encoded) {
Buffer buf(const_cast<char*>(encoded.data()), encoded.size());
broker::Exchange::shared_ptr ex = broker::Exchange::decode(cluster.getBroker().getExchanges(), buf);
@@ -614,12 +728,6 @@ void Connection::exchange(const std::string& encoded) {
QPID_LOG(debug, cluster << " updated exchange " << ex->getName());
}
-void Connection::queue(const std::string& encoded) {
- Buffer buf(const_cast<char*>(encoded.data()), encoded.size());
- broker::Queue::shared_ptr q = broker::Queue::decode(cluster.getBroker().getQueues(), buf);
- QPID_LOG(debug, cluster << " updated queue " << q->getName());
-}
-
void Connection::sessionError(uint16_t , const std::string& msg) {
// Ignore errors before isOpen(), we're not multicasting yet.
if (connection->isOpen())
@@ -678,6 +786,23 @@ void Connection::config(const std::string& encoded) {
else throw Exception(QPID_MSG("Update failed, invalid kind of config: " << kind));
}
+void Connection::doCatchupIoCallbacks() {
+ // We need to process IO callbacks during the catch-up phase in
+ // order to service asynchronous completions for messages
+ // transferred during catch-up.
+
+ if (catchUp) getBrokerConnection()->doIoCallbacks();
+}
+
+void Connection::clock(uint64_t time) {
+ QPID_LOG(debug, "Cluster connection received time update");
+ cluster.clock(time);
+}
+
+void Connection::queueDequeueSincePurgeState(const std::string& qname, uint32_t dequeueSincePurge) {
+ boost::shared_ptr<broker::Queue> queue(findQueue(qname));
+ queue->setDequeueSincePurge(dequeueSincePurge);
+}
}} // Namespace qpid::cluster
diff --git a/cpp/src/qpid/cluster/Connection.h b/cpp/src/qpid/cluster/Connection.h
index 7ee85bf1aa..fe66b77238 100644
--- a/cpp/src/qpid/cluster/Connection.h
+++ b/cpp/src/qpid/cluster/Connection.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -24,11 +24,12 @@
#include "types.h"
#include "OutputInterceptor.h"
-#include "EventFrame.h"
#include "McastFrameHandler.h"
#include "UpdateReceiver.h"
+#include "qpid/RefCounted.h"
#include "qpid/broker/Connection.h"
+#include "qpid/broker/DeliveryRecord.h"
#include "qpid/broker/SecureConnection.h"
#include "qpid/broker/SemanticState.h"
#include "qpid/amqp_0_10/Connection.h"
@@ -47,7 +48,7 @@ namespace framing { class AMQFrame; }
namespace broker {
class SemanticState;
-class QueuedMessage;
+struct QueuedMessage;
class TxBuffer;
class TxAccept;
}
@@ -55,6 +56,7 @@ class TxAccept;
namespace cluster {
class Cluster;
class Event;
+struct EventFrame;
/** Intercept broker::Connection calls for shadow and local cluster connections. */
class Connection :
@@ -62,7 +64,7 @@ class Connection :
public sys::ConnectionInputHandler,
public framing::AMQP_AllOperations::ClusterConnectionHandler,
private broker::Connection::ErrorListener
-
+
{
public:
@@ -73,7 +75,7 @@ class Connection :
Connection(Cluster&, sys::ConnectionOutputHandler& out, const std::string& mgmtId, const ConnectionId& id,
const qpid::sys::SecuritySettings& external);
~Connection();
-
+
ConnectionId getId() const { return self; }
broker::Connection* getBrokerConnection() { return connection.get(); }
const broker::Connection* getBrokerConnection() const { return connection.get(); }
@@ -108,9 +110,9 @@ class Connection :
void deliveredFrame(const EventFrame&);
void consumerState(const std::string& name, bool blocked, bool notifyEnabled, const qpid::framing::SequenceNumber& position);
-
+
// ==== Used in catch-up mode to build initial state.
- //
+ //
// State update methods.
void shadowPrepare(const std::string&);
@@ -122,10 +124,11 @@ class Connection :
const framing::SequenceNumber& expected,
const framing::SequenceNumber& received,
const framing::SequenceSet& unknownCompleted,
- const SequenceSet& receivedIncomplete);
-
+ const SequenceSet& receivedIncomplete,
+ bool dtxSelected);
+
void outputTask(uint16_t channel, const std::string& name);
-
+
void shadowReady(uint64_t memberId,
uint64_t connectionId,
const std::string& managementId,
@@ -153,7 +156,7 @@ class Connection :
void queuePosition(const std::string&, const framing::SequenceNumber&);
void queueFairshareState(const std::string&, const uint8_t priority, const uint8_t count);
- void expiryId(uint64_t);
+ void queueObserverState(const std::string&, const std::string&, const framing::FieldTable&);
void txStart();
void txAccept(const framing::SequenceSet&);
@@ -163,8 +166,18 @@ class Connection :
void txEnd();
void accumulatedAck(const framing::SequenceSet&);
- // Encoded queue/exchange replication.
- void queue(const std::string& encoded);
+ // Dtx state
+ void dtxStart(const std::string& xid,
+ bool ended,
+ bool suspended,
+ bool failed,
+ bool expired);
+ void dtxEnd();
+ void dtxAck();
+ void dtxBufferRef(const std::string& xid, uint32_t index, bool suspended);
+ void dtxWorkRecord(const std::string& xid, bool prepared, uint32_t timeout);
+
+ // Encoded exchange replication.
void exchange(const std::string& encoded);
void giveReadCredit(int credit);
@@ -189,6 +202,12 @@ class Connection :
void setSecureConnection ( broker::SecureConnection * sc );
+ void doCatchupIoCallbacks();
+
+ void clock(uint64_t time);
+
+ void queueDequeueSincePurgeState(const std::string&, uint32_t);
+
private:
struct NullFrameHandler : public framing::FrameHandler {
void handle(framing::AMQFrame&) {}
@@ -233,7 +252,7 @@ class Connection :
// Error listener functions
void connectionError(const std::string&);
void sessionError(uint16_t channel, const std::string&);
-
+
void init();
bool checkUnsupported(const framing::AMQBody& body);
void deliverDoOutput(uint32_t limit);
@@ -245,10 +264,11 @@ class Connection :
broker::SemanticState& semanticState();
broker::QueuedMessage getUpdateMessage();
void closeUpdated();
-
+ void setDtxBuffer(const UpdateReceiver::DtxBuffers::value_type &);
Cluster& cluster;
ConnectionId self;
bool catchUp;
+ bool announced;
OutputInterceptor output;
framing::FrameDecoder localDecoder;
ConnectionCtor connectionCtor;
@@ -256,6 +276,9 @@ class Connection :
framing::SequenceNumber deliverSeq;
framing::ChannelId currentChannel;
boost::shared_ptr<broker::TxBuffer> txBuffer;
+ boost::shared_ptr<broker::DtxBuffer> dtxBuffer;
+ broker::DeliveryRecords dtxAckRecords;
+ broker::DtxWorkRecord* dtxCurrent;
bool expectProtocolHeader;
McastFrameHandler mcastFrameHandler;
UpdateReceiver& updateIn;
diff --git a/cpp/src/qpid/cluster/Decoder.h b/cpp/src/qpid/cluster/Decoder.h
index 2e2af2868f..3b5ada4a81 100644
--- a/cpp/src/qpid/cluster/Decoder.h
+++ b/cpp/src/qpid/cluster/Decoder.h
@@ -31,7 +31,7 @@
namespace qpid {
namespace cluster {
-class EventFrame;
+struct EventFrame;
class EventHeader;
/**
diff --git a/cpp/src/qpid/cluster/ErrorCheck.h b/cpp/src/qpid/cluster/ErrorCheck.h
index de8cedafb3..a417b2ec25 100644
--- a/cpp/src/qpid/cluster/ErrorCheck.h
+++ b/cpp/src/qpid/cluster/ErrorCheck.h
@@ -33,7 +33,7 @@
namespace qpid {
namespace cluster {
-class EventFrame;
+struct EventFrame;
class Cluster;
class Multicaster;
class Connection;
diff --git a/cpp/src/qpid/cluster/Event.cpp b/cpp/src/qpid/cluster/Event.cpp
index cd775ce2f1..da2bc89d8c 100644
--- a/cpp/src/qpid/cluster/Event.cpp
+++ b/cpp/src/qpid/cluster/Event.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 "qpid/cluster/Cpg.h"
#include "qpid/framing/Buffer.h"
#include "qpid/framing/AMQFrame.h"
+#include "qpid/RefCountedBuffer.h"
#include "qpid/assert.h"
#include <ostream>
#include <iterator>
diff --git a/cpp/src/qpid/cluster/Event.h b/cpp/src/qpid/cluster/Event.h
index 07f74d3ba5..13283edff7 100644
--- a/cpp/src/qpid/cluster/Event.h
+++ b/cpp/src/qpid/cluster/Event.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -23,7 +23,7 @@
*/
#include "qpid/cluster/types.h"
-#include "qpid/RefCountedBuffer.h"
+#include "qpid/BufferRef.h"
#include "qpid/framing/AMQFrame.h"
#include <sys/uio.h> // For iovec
#include <iosfwd>
@@ -53,7 +53,7 @@ class EventHeader {
/** Size of payload data, excluding header. */
size_t getSize() const { return size; }
- /** Size of header + payload. */
+ /** Size of header + payload. */
size_t getStoreSize() const { return size + HEADER_SIZE; }
bool isCluster() const { return connectionId.getNumber() == 0; }
@@ -62,7 +62,7 @@ class EventHeader {
protected:
static const size_t HEADER_SIZE;
-
+
EventType type;
ConnectionId connectionId;
size_t size;
@@ -86,25 +86,25 @@ class Event : public EventHeader {
/** Create a control event. */
static Event control(const framing::AMQFrame&, const ConnectionId&);
-
+
// Data excluding header.
- char* getData() { return store + HEADER_SIZE; }
- const char* getData() const { return store + HEADER_SIZE; }
+ char* getData() { return store.begin() + HEADER_SIZE; }
+ const char* getData() const { return store.begin() + HEADER_SIZE; }
// Store including header
- char* getStore() { return store; }
- const char* getStore() const { return store; }
+ char* getStore() { return store.begin(); }
+ const char* getStore() const { return store.begin(); }
+
+ const framing::AMQFrame& getFrame() const;
- const framing::AMQFrame& getFrame() const;
-
operator framing::Buffer() const;
iovec toIovec() const;
-
+
private:
void encodeHeader() const;
- RefCountedBuffer::pointer store;
+ BufferRef store;
mutable framing::AMQFrame frame;
};
diff --git a/cpp/src/qpid/cluster/EventFrame.h b/cpp/src/qpid/cluster/EventFrame.h
index 61447c5525..6b702a9bf8 100644
--- a/cpp/src/qpid/cluster/EventFrame.h
+++ b/cpp/src/qpid/cluster/EventFrame.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -48,7 +48,7 @@ struct EventFrame
ConnectionId connectionId;
- framing::AMQFrame frame;
+ framing::AMQFrame frame;
int readCredit; ///< last frame in an event, give credit when processed.
EventType type;
};
diff --git a/cpp/src/qpid/cluster/ExpiryPolicy.cpp b/cpp/src/qpid/cluster/ExpiryPolicy.cpp
index d9a7b0122a..0ef5c2a35d 100644
--- a/cpp/src/qpid/cluster/ExpiryPolicy.cpp
+++ b/cpp/src/qpid/cluster/ExpiryPolicy.cpp
@@ -21,106 +21,21 @@
#include "qpid/broker/Message.h"
#include "qpid/cluster/ExpiryPolicy.h"
-#include "qpid/cluster/Multicaster.h"
-#include "qpid/framing/ClusterMessageExpiredBody.h"
+#include "qpid/cluster/Cluster.h"
#include "qpid/sys/Time.h"
-#include "qpid/sys/Timer.h"
#include "qpid/log/Statement.h"
namespace qpid {
namespace cluster {
-ExpiryPolicy::ExpiryPolicy(Multicaster& m, const MemberId& id, sys::Timer& t)
- : expiryId(1), expiredPolicy(new Expired), mcast(m), memberId(id), timer(t) {}
+ExpiryPolicy::ExpiryPolicy(Cluster& cluster) : cluster(cluster) {}
-struct ExpiryTask : public sys::TimerTask {
- ExpiryTask(const boost::intrusive_ptr<ExpiryPolicy>& policy, uint64_t id, sys::AbsTime when)
- : TimerTask(when,"ExpiryPolicy"), expiryPolicy(policy), expiryId(id) {}
- void fire() { expiryPolicy->sendExpire(expiryId); }
- boost::intrusive_ptr<ExpiryPolicy> expiryPolicy;
- const uint64_t expiryId;
-};
-
-// Called while receiving an update
-void ExpiryPolicy::setId(uint64_t id) {
- sys::Mutex::ScopedLock l(lock);
- expiryId = id;
-}
-
-// Called while giving an update
-uint64_t ExpiryPolicy::getId() const {
- sys::Mutex::ScopedLock l(lock);
- return expiryId;
-}
-
-// Called in enqueuing connection thread
-void ExpiryPolicy::willExpire(broker::Message& m) {
- uint64_t id;
- {
- // When messages are fanned out to multiple queues, update sends
- // them as independenty messages so we can have multiple messages
- // with the same expiry ID.
- //
- sys::Mutex::ScopedLock l(lock);
- id = expiryId++;
- if (!id) { // This is an update of an already-expired message.
- m.setExpiryPolicy(expiredPolicy);
- }
- else {
- assert(unexpiredByMessage.find(&m) == unexpiredByMessage.end());
- // If this is an update, the id may already exist
- unexpiredById.insert(IdMessageMap::value_type(id, &m));
- unexpiredByMessage[&m] = id;
- }
- }
- timer.add(new ExpiryTask(this, id, m.getExpiration()));
-}
-
-// Called in dequeueing connection thread
-void ExpiryPolicy::forget(broker::Message& m) {
- sys::Mutex::ScopedLock l(lock);
- MessageIdMap::iterator i = unexpiredByMessage.find(&m);
- assert(i != unexpiredByMessage.end());
- unexpiredById.erase(i->second);
- unexpiredByMessage.erase(i);
-}
-
-// Called in dequeueing connection or cleanup thread.
bool ExpiryPolicy::hasExpired(broker::Message& m) {
- sys::Mutex::ScopedLock l(lock);
- return unexpiredByMessage.find(&m) == unexpiredByMessage.end();
-}
-
-// Called in timer thread
-void ExpiryPolicy::sendExpire(uint64_t id) {
- {
- sys::Mutex::ScopedLock l(lock);
- // Don't multicast an expiry notice if message is already forgotten.
- if (unexpiredById.find(id) == unexpiredById.end()) return;
- }
- mcast.mcastControl(framing::ClusterMessageExpiredBody(framing::ProtocolVersion(), id), memberId);
+ return m.getExpiration() < cluster.getClusterTime();
}
-// Called in CPG deliver thread.
-void ExpiryPolicy::deliverExpire(uint64_t id) {
- sys::Mutex::ScopedLock l(lock);
- std::pair<IdMessageMap::iterator, IdMessageMap::iterator> expired = unexpiredById.equal_range(id);
- IdMessageMap::iterator i = expired.first;
- while (i != expired.second) {
- i->second->setExpiryPolicy(expiredPolicy); // hasExpired() == true;
- unexpiredByMessage.erase(i->second);
- unexpiredById.erase(i++);
- }
+sys::AbsTime ExpiryPolicy::getCurrentTime() {
+ return cluster.getClusterTime();
}
-// Called in update thread on the updater.
-boost::optional<uint64_t> ExpiryPolicy::getId(broker::Message& m) {
- sys::Mutex::ScopedLock l(lock);
- MessageIdMap::iterator i = unexpiredByMessage.find(&m);
- return i == unexpiredByMessage.end() ? boost::optional<uint64_t>() : i->second;
-}
-
-bool ExpiryPolicy::Expired::hasExpired(broker::Message&) { return true; }
-void ExpiryPolicy::Expired::willExpire(broker::Message&) { }
-
}} // namespace qpid::cluster
diff --git a/cpp/src/qpid/cluster/ExpiryPolicy.h b/cpp/src/qpid/cluster/ExpiryPolicy.h
index 77a656aa68..d8ddbca8b3 100644
--- a/cpp/src/qpid/cluster/ExpiryPolicy.h
+++ b/cpp/src/qpid/cluster/ExpiryPolicy.h
@@ -36,12 +36,8 @@ namespace broker {
class Message;
}
-namespace sys {
-class Timer;
-}
-
namespace cluster {
-class Multicaster;
+class Cluster;
/**
* Cluster expiry policy
@@ -49,43 +45,13 @@ class Multicaster;
class ExpiryPolicy : public broker::ExpiryPolicy
{
public:
- ExpiryPolicy(Multicaster&, const MemberId&, sys::Timer&);
+ ExpiryPolicy(Cluster& cluster);
- void willExpire(broker::Message&);
bool hasExpired(broker::Message&);
- void forget(broker::Message&);
-
- // Send expiration notice to cluster.
- void sendExpire(uint64_t);
+ qpid::sys::AbsTime getCurrentTime();
- // Cluster delivers expiry notice.
- void deliverExpire(uint64_t);
-
- void setId(uint64_t id);
- uint64_t getId() const;
-
- boost::optional<uint64_t> getId(broker::Message&);
-
private:
- typedef std::map<broker::Message*, uint64_t> MessageIdMap;
- // When messages are fanned out to multiple queues, update sends
- // them as independenty messages so we can have multiple messages
- // with the same expiry ID.
- typedef std::multimap<uint64_t, broker::Message*> IdMessageMap;
-
- struct Expired : public broker::ExpiryPolicy {
- bool hasExpired(broker::Message&);
- void willExpire(broker::Message&);
- };
-
- mutable sys::Mutex lock;
- MessageIdMap unexpiredByMessage;
- IdMessageMap unexpiredById;
- uint64_t expiryId;
- boost::intrusive_ptr<Expired> expiredPolicy;
- Multicaster& mcast;
- MemberId memberId;
- sys::Timer& timer;
+ Cluster& cluster;
};
}} // namespace qpid::cluster
diff --git a/cpp/src/qpid/cluster/FailoverExchange.cpp b/cpp/src/qpid/cluster/FailoverExchange.cpp
index 84232dac1b..cfbe34a460 100644
--- a/cpp/src/qpid/cluster/FailoverExchange.cpp
+++ b/cpp/src/qpid/cluster/FailoverExchange.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
@@ -39,8 +39,10 @@ using namespace broker;
using namespace framing;
const string FailoverExchange::typeName("amq.failover");
-
-FailoverExchange::FailoverExchange(management::Manageable* parent, Broker* b) : Exchange(typeName, parent, b ) {
+
+FailoverExchange::FailoverExchange(management::Manageable* parent, Broker* b)
+ : Exchange(typeName, parent, b ), ready(false)
+{
if (mgmtExchange != 0)
mgmtExchange->set_type(typeName);
}
@@ -53,16 +55,17 @@ void FailoverExchange::setUrls(const vector<Url>& u) {
void FailoverExchange::updateUrls(const vector<Url>& u) {
Lock l(lock);
urls=u;
- if (urls.empty()) return;
- std::for_each(queues.begin(), queues.end(),
- boost::bind(&FailoverExchange::sendUpdate, this, _1));
+ if (ready && !urls.empty()) {
+ std::for_each(queues.begin(), queues.end(),
+ boost::bind(&FailoverExchange::sendUpdate, this, _1));
+ }
}
string FailoverExchange::getType() const { return typeName; }
bool FailoverExchange::bind(Queue::shared_ptr queue, const string&, const framing::FieldTable*) {
Lock l(lock);
- sendUpdate(queue);
+ if (ready) sendUpdate(queue);
return queues.insert(queue).second;
}
@@ -84,7 +87,7 @@ void FailoverExchange::sendUpdate(const Queue::shared_ptr& queue) {
// Called with lock held.
if (urls.empty()) return;
framing::Array array(0x95);
- for (Urls::const_iterator i = urls.begin(); i != urls.end(); ++i)
+ for (Urls::const_iterator i = urls.begin(); i != urls.end(); ++i)
array.add(boost::shared_ptr<Str16Value>(new Str16Value(i->str())));
const ProtocolVersion v;
boost::intrusive_ptr<Message> msg(new Message);
@@ -96,9 +99,12 @@ void FailoverExchange::sendUpdate(const Queue::shared_ptr& queue) {
header.get<MessageProperties>(true)->getApplicationHeaders().setArray(typeName, array);
AMQFrame headerFrame(header);
headerFrame.setFirstSegment(false);
- msg->getFrames().append(headerFrame);
+ msg->getFrames().append(headerFrame);
DeliverableMessage(msg).deliverTo(queue);
}
+void FailoverExchange::setReady() {
+ ready = true;
+}
}} // namespace cluster
diff --git a/cpp/src/qpid/cluster/FailoverExchange.h b/cpp/src/qpid/cluster/FailoverExchange.h
index 2e1edfc0ae..c3e50c6929 100644
--- a/cpp/src/qpid/cluster/FailoverExchange.h
+++ b/cpp/src/qpid/cluster/FailoverExchange.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -46,6 +46,8 @@ class FailoverExchange : public broker::Exchange
void setUrls(const std::vector<Url>&);
/** Set the URLs and send an update.*/
void updateUrls(const std::vector<Url>&);
+ /** Flag the failover exchange as ready to generate updates (caught up) */
+ void setReady();
// Exchange overrides
std::string getType() const;
@@ -56,7 +58,7 @@ class FailoverExchange : public broker::Exchange
private:
void sendUpdate(const boost::shared_ptr<broker::Queue>&);
-
+
typedef sys::Mutex::ScopedLock Lock;
typedef std::vector<Url> Urls;
typedef std::set<boost::shared_ptr<broker::Queue> > Queues;
@@ -64,7 +66,7 @@ class FailoverExchange : public broker::Exchange
sys::Mutex lock;
Urls urls;
Queues queues;
-
+ bool ready;
};
}} // namespace qpid::cluster
diff --git a/cpp/src/qpid/cluster/Multicaster.cpp b/cpp/src/qpid/cluster/Multicaster.cpp
index 8916de9628..217641841c 100644
--- a/cpp/src/qpid/cluster/Multicaster.cpp
+++ b/cpp/src/qpid/cluster/Multicaster.cpp
@@ -21,6 +21,7 @@
#include "qpid/cluster/Multicaster.h"
#include "qpid/cluster/Cpg.h"
+#include "qpid/cluster/Cluster.h"
#include "qpid/log/Statement.h"
#include "qpid/framing/AMQBody.h"
#include "qpid/framing/AMQFrame.h"
@@ -58,7 +59,7 @@ void Multicaster::mcast(const Event& e) {
return;
}
}
- QPID_LOG(trace, "MCAST " << e);
+ QPID_LOG_IF(trace, e.isControl() && Cluster::loggable(e.getFrame()), "MCAST " << e);
if (bypass) { // direct, don't queue
iovec iov = e.toIovec();
while (!cpg.mcast(&iov, 1))
diff --git a/cpp/src/qpid/cluster/OutputInterceptor.cpp b/cpp/src/qpid/cluster/OutputInterceptor.cpp
index 1354dab17b..4bf03eefa2 100644
--- a/cpp/src/qpid/cluster/OutputInterceptor.cpp
+++ b/cpp/src/qpid/cluster/OutputInterceptor.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
@@ -45,12 +45,11 @@ void OutputInterceptor::send(framing::AMQFrame& f) {
}
void OutputInterceptor::activateOutput() {
- if (parent.isCatchUp()) {
- sys::Mutex::ScopedLock l(lock);
+ sys::Mutex::ScopedLock l(lock);
+ if (parent.isCatchUp())
next->activateOutput();
- }
else
- sendDoOutput(sendMax);
+ sendDoOutput(sendMax, l);
}
void OutputInterceptor::abort() {
@@ -66,29 +65,38 @@ void OutputInterceptor::giveReadCredit(int32_t credit) {
}
// Called in write thread when the IO layer has no more data to write.
-// We do nothing in the write thread, we run doOutput only on delivery
-// of doOutput requests.
-bool OutputInterceptor::doOutput() { return false; }
+// We only process IO callbacks in the write thread during catch-up.
+// Normally we run doOutput only on delivery of doOutput requests.
+bool OutputInterceptor::doOutput() {
+ parent.doCatchupIoCallbacks();
+ return false;
+}
-// Send output up to limit, calculate new limit.
+// Send output up to limit, calculate new limit.
void OutputInterceptor::deliverDoOutput(uint32_t limit) {
+ sys::Mutex::ScopedLock l(lock);
sentDoOutput = false;
sendMax = limit;
size_t newLimit = limit;
if (parent.isLocal()) {
- size_t buffered = getBuffered();
+ size_t buffered = next->getBuffered();
if (buffered == 0 && sent == sendMax) // Could have sent more, increase the limit.
- newLimit = sendMax*2;
+ newLimit = sendMax*2;
else if (buffered > 0 && sent > 1) // Data left unsent, reduce the limit.
newLimit = (sendMax + sent) / 2;
}
sent = 0;
- while (sent < limit && parent.getBrokerConnection()->doOutput())
+ while (sent < limit) {
+ {
+ sys::Mutex::ScopedUnlock u(lock);
+ if (!parent.getBrokerConnection()->doOutput()) break;
+ }
++sent;
- if (sent == limit) sendDoOutput(newLimit);
+ }
+ if (sent == limit) sendDoOutput(newLimit, l);
}
-void OutputInterceptor::sendDoOutput(size_t newLimit) {
+void OutputInterceptor::sendDoOutput(size_t newLimit, const sys::Mutex::ScopedLock&) {
if (parent.isLocal() && !sentDoOutput && !closing) {
sentDoOutput = true;
parent.getCluster().getMulticast().mcastControl(
@@ -97,6 +105,7 @@ void OutputInterceptor::sendDoOutput(size_t newLimit) {
}
}
+// Called in connection thread when local connection closes.
void OutputInterceptor::closeOutput() {
sys::Mutex::ScopedLock l(lock);
closing = true;
diff --git a/cpp/src/qpid/cluster/OutputInterceptor.h b/cpp/src/qpid/cluster/OutputInterceptor.h
index 65bd82a4fc..3abf5273a0 100644
--- a/cpp/src/qpid/cluster/OutputInterceptor.h
+++ b/cpp/src/qpid/cluster/OutputInterceptor.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -58,13 +58,13 @@ class OutputInterceptor : public sys::ConnectionOutputHandler {
uint32_t getSendMax() const { return sendMax; }
void setSendMax(uint32_t sendMax_) { sendMax=sendMax_; }
-
+
cluster::Connection& parent;
-
+
private:
typedef sys::Mutex::ScopedLock Locker;
- void sendDoOutput(size_t newLimit);
+ void sendDoOutput(size_t newLimit, const sys::Mutex::ScopedLock&);
mutable sys::Mutex lock;
bool closing;
diff --git a/cpp/src/qpid/cluster/SecureConnectionFactory.cpp b/cpp/src/qpid/cluster/SecureConnectionFactory.cpp
index 6ddef66226..2672d8360c 100644
--- a/cpp/src/qpid/cluster/SecureConnectionFactory.cpp
+++ b/cpp/src/qpid/cluster/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
@@ -48,7 +48,7 @@ SecureConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, cons
if (clusterCodec) {
SecureConnectionPtr sc(new SecureConnection());
clusterCodec->setSecureConnection(sc.get());
- sc->setCodec(codec);
+ sc->setCodec(codec);
return sc.release();
}
return 0;
@@ -63,7 +63,7 @@ SecureConnectionFactory::create(sys::OutputControl& out, const std::string& id,
if (clusterCodec) {
SecureConnectionPtr sc(new SecureConnection());
clusterCodec->setSecureConnection(sc.get());
- sc->setCodec(codec);
+ sc->setCodec(codec);
return sc.release();
}
return 0;
diff --git a/cpp/src/qpid/cluster/UpdateClient.cpp b/cpp/src/qpid/cluster/UpdateClient.cpp
index 8f751add9b..2446c12f2b 100644
--- a/cpp/src/qpid/cluster/UpdateClient.cpp
+++ b/cpp/src/qpid/cluster/UpdateClient.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
@@ -26,9 +26,9 @@
#include "qpid/cluster/Decoder.h"
#include "qpid/cluster/ExpiryPolicy.h"
#include "qpid/cluster/UpdateDataExchange.h"
-#include "qpid/client/SessionBase_0_10Access.h"
-#include "qpid/client/ConnectionAccess.h"
-#include "qpid/client/SessionImpl.h"
+#include "qpid/client/SessionBase_0_10Access.h"
+#include "qpid/client/ConnectionAccess.h"
+#include "qpid/client/SessionImpl.h"
#include "qpid/client/ConnectionImpl.h"
#include "qpid/client/Future.h"
#include "qpid/broker/Broker.h"
@@ -45,10 +45,13 @@
#include "qpid/broker/SessionState.h"
#include "qpid/broker/TxOpVisitor.h"
#include "qpid/broker/DtxAck.h"
+#include "qpid/broker/DtxBuffer.h"
+#include "qpid/broker/DtxWorkRecord.h"
#include "qpid/broker/TxAccept.h"
#include "qpid/broker/TxPublish.h"
#include "qpid/broker/RecoveredDequeue.h"
#include "qpid/broker/RecoveredEnqueue.h"
+#include "qpid/broker/StatefulQueueObserver.h"
#include "qpid/framing/MessageTransferBody.h"
#include "qpid/framing/ClusterConnectionMembershipBody.h"
#include "qpid/framing/ClusterConnectionShadowReadyBody.h"
@@ -64,6 +67,7 @@
#include <boost/bind.hpp>
#include <boost/cast.hpp>
#include <algorithm>
+#include <iterator>
#include <sstream>
namespace qpid {
@@ -82,11 +86,20 @@ using namespace framing;
namespace arg=client::arg;
using client::SessionBase_0_10Access;
+// 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.
+const std::string UpdateClient::X_QPID_EXPIRATION = "x-qpid.expiration";
+// Headers used to flag headers/properties added by the UpdateClient so they can be
+// removed on the other side.
+const std::string UpdateClient::X_QPID_NO_MESSAGE_PROPS = "x-qpid.no-message-props";
+const std::string UpdateClient::X_QPID_NO_HEADERS = "x-qpid.no-headers";
+
std::ostream& operator<<(std::ostream& o, const UpdateClient& c) {
return o << "cluster(" << c.updaterId << " UPDATER)";
}
-struct ClusterConnectionProxy : public AMQP_AllProxy::ClusterConnection, public framing::FrameHandler
+struct ClusterConnectionProxy : public AMQP_AllProxy::ClusterConnection, public framing::FrameHandler
{
boost::shared_ptr<qpid::client::ConnectionImpl> connection;
@@ -120,7 +133,7 @@ void send(client::AsyncSession& s, const AMQBody& body) {
// TODO aconway 2008-09-24: optimization: update connections/sessions in parallel.
UpdateClient::UpdateClient(const MemberId& updater, const MemberId& updatee, const Url& url,
- broker::Broker& broker, const ClusterMap& m, ExpiryPolicy& expiry_,
+ broker::Broker& broker, const ClusterMap& m, ExpiryPolicy& expiry_,
const Cluster::ConnectionVector& cons, Decoder& decoder_,
const boost::function<void()>& ok,
const boost::function<void(const std::exception&)>& fail,
@@ -134,13 +147,11 @@ UpdateClient::UpdateClient(const MemberId& updater, const MemberId& updatee, con
UpdateClient::~UpdateClient() {}
-// Reserved exchange/queue name for catch-up, avoid clashes with user queues/exchanges.
-const std::string UpdateClient::UPDATE("qpid.cluster-update");
-
void UpdateClient::run() {
try {
connection.open(updateeUrl, connectionSettings);
session = connection.newSession(UPDATE);
+ session.sync();
update();
done();
} catch (const std::exception& e) {
@@ -154,6 +165,13 @@ void UpdateClient::update() {
<< " at " << updateeUrl);
Broker& b = updaterBroker;
+ if(b.getExpiryPolicy()) {
+ QPID_LOG(debug, *this << "Updating updatee with cluster time");
+ qpid::sys::AbsTime clusterTime = b.getExpiryPolicy()->getCurrentTime();
+ int64_t time = qpid::sys::Duration(qpid::sys::EPOCH, clusterTime);
+ ClusterConnectionProxy(session).clock(time);
+ }
+
updateManagementSetupState();
b.getExchanges().eachExchange(boost::bind(&UpdateClient::updateExchange, this, _1));
@@ -163,16 +181,20 @@ void UpdateClient::update() {
// longer on their original queue.
session.queueDeclare(arg::queue=UPDATE, arg::autoDelete=true);
session.sync();
+
std::for_each(connections.begin(), connections.end(),
boost::bind(&UpdateClient::updateConnection, this, _1));
- session.queueDelete(arg::queue=UPDATE);
+
+ // some Queue Observers need session state & msgs synced first, so sync observers now
+ b.getQueues().eachQueue(boost::bind(&UpdateClient::updateQueueObservers, this, _1));
// Update queue listeners: must come after sessions so consumerNumbering is populated
b.getQueues().eachQueue(boost::bind(&UpdateClient::updateQueueListeners, this, _1));
- ClusterConnectionProxy(session).expiryId(expiry.getId());
updateLinks();
updateManagementAgent();
+ updateDtxManager();
+ session.queueDelete(arg::queue=UPDATE);
session.close();
@@ -184,7 +206,7 @@ void UpdateClient::update() {
// NOTE: connection will be closed from the other end, don't close
// it here as that causes a race.
-
+
// TODO aconway 2010-03-15: This sleep avoids the race condition
// described in // https://bugzilla.redhat.com/show_bug.cgi?id=568831.
// It allows the connection to fully close before destroying the
@@ -276,7 +298,7 @@ class MessageUpdater {
framing::SequenceNumber lastPos;
client::AsyncSession session;
ExpiryPolicy& expiry;
-
+
public:
MessageUpdater(const string& q, const client::AsyncSession s, ExpiryPolicy& expiry_) : queue(q), haveLastPos(false), session(s), expiry(expiry_) {
@@ -293,7 +315,6 @@ class MessageUpdater {
}
}
-
void updateQueuedMessage(const broker::QueuedMessage& message) {
// Send the queue position if necessary.
if (!haveLastPos || message.position - lastPos != 1) {
@@ -302,10 +323,23 @@ class MessageUpdater {
}
lastPos = message.position;
- // Send the expiry ID if necessary.
- if (message.payload->getProperties<DeliveryProperties>()->getTtl()) {
- boost::optional<uint64_t> expiryId = expiry.getId(*message.payload);
- ClusterConnectionProxy(session).expiryId(expiryId?*expiryId:0);
+ // if the ttl > 0, we need to send the calculated expiration time to the updatee
+ const DeliveryProperties* dprops =
+ message.payload->getProperties<DeliveryProperties>();
+ if (dprops && dprops->getTtl() > 0) {
+ bool hadMessageProps =
+ message.payload->hasProperties<framing::MessageProperties>();
+ const framing::MessageProperties* mprops =
+ message.payload->getProperties<framing::MessageProperties>();
+ bool hadApplicationHeaders = mprops->hasApplicationHeaders();
+ message.payload->insertCustomProperty(UpdateClient::X_QPID_EXPIRATION,
+ sys::Duration(sys::EPOCH, message.payload->getExpiration()));
+ // If message properties or application headers didn't exist
+ // prior to us adding data, we want to remove them on the other side.
+ if (!hadMessageProps)
+ message.payload->insertCustomProperty(UpdateClient::X_QPID_NO_MESSAGE_PROPS, 0);
+ else if (!hadApplicationHeaders)
+ message.payload->insertCustomProperty(UpdateClient::X_QPID_NO_HEADERS, 0);
}
// We can't send a broker::Message via the normal client API,
@@ -318,7 +352,7 @@ class MessageUpdater {
framing::MessageTransferBody transfer(
*message.payload->getFrames().as<framing::MessageTransferBody>());
transfer.setDestination(UpdateClient::UPDATE);
-
+
sb.get()->send(transfer, message.payload->getFrames(),
!message.payload->isContentReleased());
if (message.payload->isContentReleased()){
@@ -326,9 +360,10 @@ class MessageUpdater {
uint16_t maxContentSize = maxFrameSize - AMQFrame::frameOverhead();
bool morecontent = true;
for (uint64_t offset = 0; morecontent; offset += maxContentSize)
- {
+ {
AMQFrame frame((AMQContentBody()));
- morecontent = message.payload->getContentFrame(*(message.queue), frame, maxContentSize, offset);
+ morecontent = message.payload->getContentFrame(
+ *(message.queue), frame, maxContentSize, offset);
sb.get()->sendRawFrame(frame);
}
}
@@ -357,6 +392,8 @@ void UpdateClient::updateQueue(client::AsyncSession& s, const boost::shared_ptr<
if (qpid::broker::Fairshare::getState(q->getMessages(), priority, count)) {
ClusterConnectionProxy(s).queueFairshareState(q->getName(), priority, count);
}
+
+ ClusterConnectionProxy(s).queueDequeueSincePurgeState(q->getName(), q->getDequeueSincePurge());
}
void UpdateClient::updateExclusiveQueue(const boost::shared_ptr<broker::Queue>& q) {
@@ -372,7 +409,11 @@ void UpdateClient::updateNonExclusiveQueue(const boost::shared_ptr<broker::Queue
}
void UpdateClient::updateBinding(client::AsyncSession& s, const std::string& queue, const QueueBinding& binding) {
- s.exchangeBind(queue, binding.exchange, binding.key, binding.args);
+ if (binding.exchange.size())
+ s.exchangeBind(queue, binding.exchange, binding.key, binding.args);
+ //else its the default exchange and there is no need to replicate
+ //the binding, the creation of the queue will have done so
+ //automatically
}
void UpdateClient::updateOutputTask(const sys::OutputTask* task) {
@@ -380,8 +421,8 @@ void UpdateClient::updateOutputTask(const sys::OutputTask* task) {
boost::polymorphic_downcast<const SemanticState::ConsumerImpl*> (task);
SemanticState::ConsumerImpl* ci = const_cast<SemanticState::ConsumerImpl*>(cci);
uint16_t channel = ci->getParent().getSession().getChannel();
- ClusterConnectionProxy(shadowConnection).outputTask(channel, ci->getName());
- QPID_LOG(debug, *this << " updating output task " << ci->getName()
+ ClusterConnectionProxy(shadowConnection).outputTask(channel, ci->getTag());
+ QPID_LOG(debug, *this << " updating output task " << ci->getTag()
<< " channel=" << channel);
}
@@ -389,7 +430,7 @@ void UpdateClient::updateConnection(const boost::intrusive_ptr<Connection>& upda
QPID_LOG(debug, *this << " updating connection " << *updateConnection);
assert(updateConnection->getBrokerConnection());
broker::Connection& bc = *updateConnection->getBrokerConnection();
-
+
// Send the management ID first on the main connection.
std::string mgmtId = updateConnection->getBrokerConnection()->getMgmtId();
ClusterConnectionProxy(session).shadowPrepare(mgmtId);
@@ -426,7 +467,7 @@ void UpdateClient::updateSession(broker::SessionHandler& sh) {
QPID_LOG(debug, *this << " updating session " << ss->getId());
- // Create a client session to update session state.
+ // Create a client session to update session state.
boost::shared_ptr<client::ConnectionImpl> cimpl = client::ConnectionAccess::getImpl(shadowConnection);
boost::shared_ptr<client::SessionImpl> simpl = cimpl->newSession(ss->getId().getName(), ss->getTimeout(), sh.getChannel());
simpl->disableAutoDetach();
@@ -445,19 +486,19 @@ void UpdateClient::updateSession(broker::SessionHandler& sh) {
QPID_LOG(debug, *this << " updating unacknowledged messages.");
broker::DeliveryRecords& drs = ss->getSemanticState().getUnacked();
std::for_each(drs.begin(), drs.end(),
- boost::bind(&UpdateClient::updateUnacked, this, _1));
+ boost::bind(&UpdateClient::updateUnacked, this, _1, shadowSession));
- updateTxState(ss->getSemanticState()); // Tx transaction state.
+ updateTransactionState(ss->getSemanticState());
// Adjust command counter for message in progress, will be sent after state update.
boost::intrusive_ptr<Message> inProgress = ss->getMessageInProgress();
SequenceNumber received = ss->receiverGetReceived().command;
- if (inProgress)
+ if (inProgress)
--received;
// Sync the session to ensure all responses from broker have been processed.
shadowSession.sync();
-
+
// Reset command-sequence state.
proxy.sessionState(
ss->senderGetReplayPoint().command,
@@ -466,7 +507,8 @@ void UpdateClient::updateSession(broker::SessionHandler& sh) {
std::max(received, ss->receiverGetExpected().command),
received,
ss->receiverGetUnknownComplete(),
- ss->receiverGetIncomplete()
+ ss->receiverGetIncomplete(),
+ ss->getSemanticState().getDtxSelected()
);
// Send frames for partial message in progress.
@@ -479,13 +521,13 @@ void UpdateClient::updateSession(broker::SessionHandler& sh) {
void UpdateClient::updateConsumer(
const broker::SemanticState::ConsumerImpl::shared_ptr& ci)
{
- QPID_LOG(debug, *this << " updating consumer " << ci->getName() << " on "
+ QPID_LOG(debug, *this << " updating consumer " << ci->getTag() << " on "
<< shadowSession.getId());
using namespace message;
shadowSession.messageSubscribe(
arg::queue = ci->getQueue()->getName(),
- arg::destination = ci->getName(),
+ arg::destination = ci->getTag(),
arg::acceptMode = ci->isAckExpected() ? ACCEPT_MODE_EXPLICIT : ACCEPT_MODE_NONE,
arg::acquireMode = ci->isAcquire() ? ACQUIRE_MODE_PRE_ACQUIRED : ACQUIRE_MODE_NOT_ACQUIRED,
arg::exclusive = ci->isExclusive(),
@@ -493,29 +535,32 @@ void UpdateClient::updateConsumer(
arg::resumeTtl = ci->getResumeTtl(),
arg::arguments = ci->getArguments()
);
- shadowSession.messageSetFlowMode(ci->getName(), ci->isWindowing() ? FLOW_MODE_WINDOW : FLOW_MODE_CREDIT);
- shadowSession.messageFlow(ci->getName(), CREDIT_UNIT_MESSAGE, ci->getMsgCredit());
- shadowSession.messageFlow(ci->getName(), CREDIT_UNIT_BYTE, ci->getByteCredit());
+ shadowSession.messageSetFlowMode(ci->getTag(), ci->isWindowing() ? FLOW_MODE_WINDOW : FLOW_MODE_CREDIT);
+ shadowSession.messageFlow(ci->getTag(), CREDIT_UNIT_MESSAGE, ci->getMsgCredit());
+ shadowSession.messageFlow(ci->getTag(), CREDIT_UNIT_BYTE, ci->getByteCredit());
ClusterConnectionProxy(shadowSession).consumerState(
- ci->getName(),
+ ci->getTag(),
ci->isBlocked(),
ci->isNotifyEnabled(),
ci->position
);
consumerNumbering.add(ci.get());
- QPID_LOG(debug, *this << " updated consumer " << ci->getName()
+ QPID_LOG(debug, *this << " updated consumer " << ci->getTag()
<< " on " << shadowSession.getId());
}
-
-void UpdateClient::updateUnacked(const broker::DeliveryRecord& dr) {
- if (!dr.isEnded() && dr.isAcquired() && dr.getMessage().payload) {
+
+void UpdateClient::updateUnacked(const broker::DeliveryRecord& dr,
+ client::AsyncSession& updateSession)
+{
+ if (!dr.isEnded() && dr.isAcquired()) {
+ assert(dr.getMessage().payload);
// If the message is acquired then it is no longer on the
// updatees queue, put it on the update queue for updatee to pick up.
//
- MessageUpdater(UPDATE, shadowSession, expiry).updateQueuedMessage(dr.getMessage());
+ MessageUpdater(UPDATE, updateSession, expiry).updateQueuedMessage(dr.getMessage());
}
- ClusterConnectionProxy(shadowSession).deliveryRecord(
+ ClusterConnectionProxy(updateSession).deliveryRecord(
dr.getQueue()->getName(),
dr.getMessage().position,
dr.getTag(),
@@ -536,10 +581,12 @@ class TxOpUpdater : public broker::TxOpConstVisitor, public MessageUpdater {
TxOpUpdater(UpdateClient& dc, client::AsyncSession s, ExpiryPolicy& expiry)
: MessageUpdater(UpdateClient::UPDATE, s, expiry), parent(dc), session(s), proxy(s) {}
- void operator()(const broker::DtxAck& ) {
- throw InternalErrorException("DTX transactions not currently supported by cluster.");
+ void operator()(const broker::DtxAck& ack) {
+ std::for_each(ack.getPending().begin(), ack.getPending().end(),
+ boost::bind(&UpdateClient::updateUnacked, &parent, _1, session));
+ proxy.dtxAck();
}
-
+
void operator()(const broker::RecoveredDequeue& rdeq) {
updateMessage(rdeq.getMessage());
proxy.txEnqueue(rdeq.getQueue()->getName());
@@ -554,13 +601,18 @@ class TxOpUpdater : public broker::TxOpConstVisitor, public MessageUpdater {
proxy.txAccept(txAccept.getAcked());
}
+ typedef std::list<Queue::shared_ptr> QueueList;
+
+ void copy(const QueueList& l, Array& a) {
+ for (QueueList::const_iterator i = l.begin(); i!=l.end(); ++i)
+ a.push_back(Array::ValuePtr(new Str8Value((*i)->getName())));
+ }
+
void operator()(const broker::TxPublish& txPub) {
updateMessage(txPub.getMessage());
- typedef std::list<Queue::shared_ptr> QueueList;
- const QueueList& qlist = txPub.getQueues();
+ assert(txPub.getQueues().empty() || txPub.getPrepared().empty());
Array qarray(TYPE_CODE_STR8);
- for (QueueList::const_iterator i = qlist.begin(); i != qlist.end(); ++i)
- qarray.push_back(Array::ValuePtr(new Str8Value((*i)->getName())));
+ copy(txPub.getQueues().empty() ? txPub.getPrepared() : txPub.getQueues(), qarray);
proxy.txPublish(qarray, txPub.delivered);
}
@@ -569,18 +621,44 @@ class TxOpUpdater : public broker::TxOpConstVisitor, public MessageUpdater {
client::AsyncSession session;
ClusterConnectionProxy proxy;
};
-
-void UpdateClient::updateTxState(broker::SemanticState& s) {
- QPID_LOG(debug, *this << " updating TX transaction state.");
+
+void UpdateClient::updateBufferRef(const broker::DtxBuffer::shared_ptr& dtx,bool suspended)
+{
+ ClusterConnectionProxy proxy(shadowSession);
+ broker::DtxWorkRecord* record =
+ updaterBroker.getDtxManager().getWork(dtx->getXid());
+ proxy.dtxBufferRef(dtx->getXid(), record->indexOf(dtx), suspended);
+
+}
+
+void UpdateClient::updateTransactionState(broker::SemanticState& s) {
ClusterConnectionProxy proxy(shadowSession);
proxy.accumulatedAck(s.getAccumulatedAck());
- broker::TxBuffer::shared_ptr txBuffer = s.getTxBuffer();
- if (txBuffer) {
+ broker::TxBuffer::shared_ptr tx = s.getTxBuffer();
+ broker::DtxBuffer::shared_ptr dtx = s.getDtxBuffer();
+ if (dtx) {
+ updateBufferRef(dtx, false); // Current transaction.
+ } else if (tx) {
proxy.txStart();
TxOpUpdater updater(*this, shadowSession, expiry);
- txBuffer->accept(updater);
+ tx->accept(updater);
proxy.txEnd();
}
+ for (SemanticState::DtxBufferMap::iterator i = s.getSuspendedXids().begin();
+ i != s.getSuspendedXids().end();
+ ++i)
+ {
+ updateBufferRef(i->second, true);
+ }
+}
+
+void UpdateClient::updateDtxBuffer(const broker::DtxBuffer::shared_ptr& dtx) {
+ ClusterConnectionProxy proxy(session);
+ proxy.dtxStart(
+ dtx->getXid(), dtx->isEnded(), dtx->isSuspended(), dtx->isFailed(), dtx->isExpired());
+ TxOpUpdater updater(*this, session, expiry);
+ dtx->accept(updater);
+ proxy.dtxEnd();
}
void UpdateClient::updateQueueListeners(const boost::shared_ptr<broker::Queue>& queue) {
@@ -615,4 +693,35 @@ void UpdateClient::updateBridge(const boost::shared_ptr<broker::Bridge>& bridge)
ClusterConnectionProxy(session).config(encode(*bridge));
}
+void UpdateClient::updateQueueObservers(const boost::shared_ptr<broker::Queue>& q)
+{
+ q->eachObserver(boost::bind(&UpdateClient::updateObserver, this, q, _1));
+}
+
+void UpdateClient::updateObserver(const boost::shared_ptr<broker::Queue>& q,
+ boost::shared_ptr<broker::QueueObserver> o)
+{
+ qpid::framing::FieldTable state;
+ broker::StatefulQueueObserver *so = dynamic_cast<broker::StatefulQueueObserver *>(o.get());
+ if (so) {
+ so->getState( state );
+ std::string id(so->getId());
+ QPID_LOG(debug, *this << " updating queue " << q->getName() << "'s observer " << id);
+ ClusterConnectionProxy(session).queueObserverState( q->getName(), id, state );
+ }
+}
+
+void UpdateClient::updateDtxManager() {
+ broker::DtxManager& dtm = updaterBroker.getDtxManager();
+ dtm.each(boost::bind(&UpdateClient::updateDtxWorkRecord, this, _1));
+}
+
+void UpdateClient::updateDtxWorkRecord(const broker::DtxWorkRecord& r) {
+ QPID_LOG(debug, *this << " updating DTX transaction: " << r.getXid());
+ for (size_t i = 0; i < r.size(); ++i)
+ updateDtxBuffer(r[i]);
+ ClusterConnectionProxy(session).dtxWorkRecord(
+ r.getXid(), r.isPrepared(), r.getTimeout());
+}
+
}} // namespace qpid::cluster
diff --git a/cpp/src/qpid/cluster/UpdateClient.h b/cpp/src/qpid/cluster/UpdateClient.h
index 7520bb82cb..481ee357c7 100644
--- a/cpp/src/qpid/cluster/UpdateClient.h
+++ b/cpp/src/qpid/cluster/UpdateClient.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -34,7 +34,7 @@
namespace qpid {
-class Url;
+struct Url;
namespace broker {
@@ -42,8 +42,8 @@ class Broker;
class Queue;
class Exchange;
class QueueBindings;
-class QueueBinding;
-class QueuedMessage;
+struct QueueBinding;
+struct QueuedMessage;
class SessionHandler;
class DeliveryRecord;
class SessionState;
@@ -51,7 +51,8 @@ class SemanticState;
class Decoder;
class Link;
class Bridge;
-
+class QueueObserver;
+class DtxBuffer;
} // namespace broker
namespace cluster {
@@ -68,21 +69,26 @@ class ExpiryPolicy;
class UpdateClient : public sys::Runnable {
public:
static const std::string UPDATE; // Name for special update queue and exchange.
+ static const std::string X_QPID_EXPIRATION; // Update message expiration
+ // Flag to remove props/headers that were added by the UpdateClient
+ static const std::string X_QPID_NO_MESSAGE_PROPS;
+ static const std::string X_QPID_NO_HEADERS;
+
static client::Connection catchUpConnection();
-
+
UpdateClient(const MemberId& updater, const MemberId& updatee, const Url&,
broker::Broker& donor, const ClusterMap& map, ExpiryPolicy& expiry,
const std::vector<boost::intrusive_ptr<Connection> >&, Decoder&,
const boost::function<void()>& done,
const boost::function<void(const std::exception&)>& fail,
- const client::ConnectionSettings&
+ const client::ConnectionSettings&
);
~UpdateClient();
void update();
void run(); // Will delete this when finished.
- void updateUnacked(const broker::DeliveryRecord&);
+ void updateUnacked(const broker::DeliveryRecord&, client::AsyncSession&);
private:
void updateQueue(client::AsyncSession&, const boost::shared_ptr<broker::Queue>&);
@@ -94,7 +100,8 @@ class UpdateClient : public sys::Runnable {
void updateBinding(client::AsyncSession&, const std::string& queue, const broker::QueueBinding& binding);
void updateConnection(const boost::intrusive_ptr<Connection>& connection);
void updateSession(broker::SessionHandler& s);
- void updateTxState(broker::SemanticState& s);
+ void updateBufferRef(const broker::DtxBuffer::shared_ptr& dtx, bool suspended);
+ void updateTransactionState(broker::SemanticState& s);
void updateOutputTask(const sys::OutputTask* task);
void updateConsumer(const broker::SemanticState::ConsumerImpl::shared_ptr&);
void updateQueueListeners(const boost::shared_ptr<broker::Queue>&);
@@ -104,6 +111,11 @@ class UpdateClient : public sys::Runnable {
void updateLinks();
void updateLink(const boost::shared_ptr<broker::Link>&);
void updateBridge(const boost::shared_ptr<broker::Bridge>&);
+ void updateQueueObservers(const boost::shared_ptr<broker::Queue>&);
+ void updateObserver(const boost::shared_ptr<broker::Queue>&, boost::shared_ptr<broker::QueueObserver>);
+ void updateDtxManager();
+ void updateDtxBuffer(const boost::shared_ptr<broker::DtxBuffer>& );
+ void updateDtxWorkRecord(const broker::DtxWorkRecord&);
Numbering<broker::SemanticState::ConsumerImpl*> consumerNumbering;
diff --git a/cpp/src/qpid/cluster/UpdateDataExchange.cpp b/cpp/src/qpid/cluster/UpdateDataExchange.cpp
index 2a079b8881..e5cd82e3d3 100644
--- a/cpp/src/qpid/cluster/UpdateDataExchange.cpp
+++ b/cpp/src/qpid/cluster/UpdateDataExchange.cpp
@@ -36,13 +36,8 @@ const std::string UpdateDataExchange::MANAGEMENT_AGENTS_KEY("management-agents")
const std::string UpdateDataExchange::MANAGEMENT_SCHEMAS_KEY("management-schemas");
const std::string UpdateDataExchange::MANAGEMENT_DELETED_OBJECTS_KEY("management-deleted-objects");
-std::ostream& operator<<(std::ostream& o, const UpdateDataExchange& c) {
- return o << "cluster(" << c.clusterId << " UPDATER)";
-}
-
UpdateDataExchange::UpdateDataExchange(Cluster& cluster) :
- Exchange(EXCHANGE_NAME, &cluster),
- clusterId(cluster.getId())
+ Exchange(EXCHANGE_NAME, &cluster)
{}
void UpdateDataExchange::route(broker::Deliverable& msg, const std::string& routingKey,
@@ -62,11 +57,9 @@ void UpdateDataExchange::updateManagementAgent(management::ManagementAgent* agen
framing::Buffer buf1(const_cast<char*>(managementAgents.data()), managementAgents.size());
agent->importAgents(buf1);
- QPID_LOG(debug, *this << " updated management agents.");
framing::Buffer buf2(const_cast<char*>(managementSchemas.data()), managementSchemas.size());
agent->importSchemas(buf2);
- QPID_LOG(debug, *this << " updated management schemas.");
using amqp_0_10::ListCodec;
using types::Variant;
@@ -78,7 +71,6 @@ void UpdateDataExchange::updateManagementAgent(management::ManagementAgent* agen
new management::ManagementAgent::DeletedObject(*i)));
}
agent->importDeletedObjects(objects);
- QPID_LOG(debug, *this << " updated management deleted objects.");
}
diff --git a/cpp/src/qpid/cluster/UpdateDataExchange.h b/cpp/src/qpid/cluster/UpdateDataExchange.h
index 8c493e400a..d2f6c35ad0 100644
--- a/cpp/src/qpid/cluster/UpdateDataExchange.h
+++ b/cpp/src/qpid/cluster/UpdateDataExchange.h
@@ -74,11 +74,9 @@ class UpdateDataExchange : public broker::Exchange
void updateManagementAgent(management::ManagementAgent* agent);
private:
- MemberId clusterId;
std::string managementAgents;
std::string managementSchemas;
std::string managementDeletedObjects;
- friend std::ostream& operator<<(std::ostream&, const UpdateDataExchange&);
};
}} // namespace qpid::cluster
diff --git a/cpp/src/qpid/cluster/UpdateExchange.cpp b/cpp/src/qpid/cluster/UpdateExchange.cpp
index 11937f296f..cb1376004e 100644
--- a/cpp/src/qpid/cluster/UpdateExchange.cpp
+++ b/cpp/src/qpid/cluster/UpdateExchange.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
@@ -19,6 +19,7 @@
*
*/
#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/FieldTable.h"
#include "qpid/broker/Message.h"
#include "UpdateExchange.h"
@@ -27,6 +28,8 @@ namespace cluster {
using framing::MessageTransferBody;
using framing::DeliveryProperties;
+using framing::MessageProperties;
+using framing::FieldTable;
UpdateExchange::UpdateExchange(management::Manageable* parent)
: broker::Exchange(UpdateClient::UPDATE, parent),
@@ -34,6 +37,7 @@ UpdateExchange::UpdateExchange(management::Manageable* parent)
void UpdateExchange::setProperties(const boost::intrusive_ptr<broker::Message>& msg) {
+ // Copy exchange name to destination property.
MessageTransferBody* transfer = msg->getMethod<MessageTransferBody>();
assert(transfer);
const DeliveryProperties* props = msg->getProperties<DeliveryProperties>();
@@ -42,6 +46,23 @@ void UpdateExchange::setProperties(const boost::intrusive_ptr<broker::Message>&
transfer->setDestination(props->getExchange());
else
transfer->clearDestinationFlag();
-}
+ // Copy expiration from x-property if present.
+ if (msg->hasProperties<MessageProperties>()) {
+ const MessageProperties* mprops = msg->getProperties<MessageProperties>();
+ if (mprops->hasApplicationHeaders()) {
+ const FieldTable& headers = mprops->getApplicationHeaders();
+ if (headers.isSet(UpdateClient::X_QPID_EXPIRATION)) {
+ msg->setExpiration(
+ sys::AbsTime(sys::EPOCH, headers.getAsInt64(UpdateClient::X_QPID_EXPIRATION)));
+ msg->removeCustomProperty(UpdateClient::X_QPID_EXPIRATION);
+ // Erase props/headers that were added by the UpdateClient
+ if (headers.isSet(UpdateClient::X_QPID_NO_MESSAGE_PROPS))
+ msg->eraseProperties<MessageProperties>();
+ else if (headers.isSet(UpdateClient::X_QPID_NO_HEADERS))
+ msg->clearApplicationHeadersFlag();
+ }
+ }
+ }
+}
}} // namespace qpid::cluster
diff --git a/cpp/src/qpid/cluster/UpdateReceiver.h b/cpp/src/qpid/cluster/UpdateReceiver.h
index 7e8ce47662..81ee3a5ffe 100644
--- a/cpp/src/qpid/cluster/UpdateReceiver.h
+++ b/cpp/src/qpid/cluster/UpdateReceiver.h
@@ -39,6 +39,20 @@ class UpdateReceiver {
/** Management-id for the next shadow connection */
std::string nextShadowMgmtId;
+
+ /** Record the position of a DtxBuffer in the DtxManager (xid + index)
+ * and the association with a session, either suspended or current.
+ */
+ struct DtxBufferRef {
+ std::string xid;
+ uint32_t index; // Index in WorkRecord in DtxManager
+ bool suspended; // Is this a suspended or current transaction?
+ broker::SemanticState* semanticState; // Associated session
+ DtxBufferRef(const std::string& x, uint32_t i, bool s, broker::SemanticState* ss)
+ : xid(x), index(i), suspended(s), semanticState(ss) {}
+ };
+ typedef std::vector<DtxBufferRef> DtxBuffers;
+ DtxBuffers dtxBuffers;
};
}} // namespace qpid::cluster
diff --git a/cpp/src/qpid/cluster/types.h b/cpp/src/qpid/cluster/types.h
index 0795e5e77a..bfb4fd5b9e 100644
--- a/cpp/src/qpid/cluster/types.h
+++ b/cpp/src/qpid/cluster/types.h
@@ -24,6 +24,7 @@
#include "config.h"
#include "qpid/Url.h"
+#include "qpid/RefCounted.h"
#include "qpid/sys/IntegerTypes.h"
#include <boost/intrusive_ptr.hpp>
#include <utility>
diff --git a/cpp/src/qpid/console/SessionManager.cpp b/cpp/src/qpid/console/SessionManager.cpp
index 80c5959417..910ae22be8 100644
--- a/cpp/src/qpid/console/SessionManager.cpp
+++ b/cpp/src/qpid/console/SessionManager.cpp
@@ -362,12 +362,11 @@ void SessionManager::handleCommandComplete(Broker* broker, Buffer& inBuffer, uin
void SessionManager::handleClassInd(Broker* broker, Buffer& inBuffer, uint32_t)
{
- uint8_t kind;
string packageName;
string className;
uint8_t hash[16];
- kind = inBuffer.getOctet();
+ /*kind*/ (void) inBuffer.getOctet();
inBuffer.getShortString(packageName);
inBuffer.getShortString(className);
inBuffer.getBin128(hash);
diff --git a/cpp/src/qpid/framing/AMQBody.h b/cpp/src/qpid/framing/AMQBody.h
index 60ac2d3b7e..56d1d250c1 100644
--- a/cpp/src/qpid/framing/AMQBody.h
+++ b/cpp/src/qpid/framing/AMQBody.h
@@ -46,7 +46,7 @@ struct AMQBodyConstVisitor {
virtual void visit(const AMQMethodBody&) = 0;
};
-class AMQBody : public RefCounted {
+class QPID_COMMON_CLASS_EXTERN AMQBody : public RefCounted {
public:
AMQBody() {}
QPID_COMMON_EXTERN virtual ~AMQBody();
diff --git a/cpp/src/qpid/framing/AMQContentBody.h b/cpp/src/qpid/framing/AMQContentBody.h
index 69813b221c..e25451e354 100644
--- a/cpp/src/qpid/framing/AMQContentBody.h
+++ b/cpp/src/qpid/framing/AMQContentBody.h
@@ -29,7 +29,7 @@
namespace qpid {
namespace framing {
-class AMQContentBody : public AMQBody
+class QPID_COMMON_CLASS_EXTERN AMQContentBody : public AMQBody
{
string data;
@@ -37,15 +37,15 @@ public:
QPID_COMMON_EXTERN AMQContentBody();
QPID_COMMON_EXTERN AMQContentBody(const string& data);
inline virtual ~AMQContentBody(){}
- QPID_COMMON_EXTERN inline uint8_t type() const { return CONTENT_BODY; };
- QPID_COMMON_EXTERN inline const string& getData() const { return data; }
- QPID_COMMON_EXTERN inline string& getData() { return data; }
+ inline uint8_t type() const { return CONTENT_BODY; };
+ inline const string& getData() const { return data; }
+ inline 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);
QPID_COMMON_EXTERN void print(std::ostream& out) const;
- QPID_COMMON_EXTERN void accept(AMQBodyConstVisitor& v) const { v.visit(*this); }
- QPID_COMMON_EXTERN boost::intrusive_ptr<AMQBody> clone() const { return BodyFactory::copy(*this); }
+ void accept(AMQBodyConstVisitor& v) const { v.visit(*this); }
+ boost::intrusive_ptr<AMQBody> clone() const { return BodyFactory::copy(*this); }
};
}
diff --git a/cpp/src/qpid/framing/AMQFrame.cpp b/cpp/src/qpid/framing/AMQFrame.cpp
index cd60cd971f..5b9673f0d0 100644
--- a/cpp/src/qpid/framing/AMQFrame.cpp
+++ b/cpp/src/qpid/framing/AMQFrame.cpp
@@ -139,6 +139,11 @@ bool AMQFrame::decode(Buffer& buffer)
return true;
}
+void AMQFrame::cloneBody()
+{
+ body = body->clone();
+}
+
std::ostream& operator<<(std::ostream& out, const AMQFrame& f)
{
return
diff --git a/cpp/src/qpid/framing/AMQFrame.h b/cpp/src/qpid/framing/AMQFrame.h
index d7b04f0f65..4f6faf4199 100644
--- a/cpp/src/qpid/framing/AMQFrame.h
+++ b/cpp/src/qpid/framing/AMQFrame.h
@@ -33,7 +33,7 @@
namespace qpid {
namespace framing {
-class AMQFrame : public AMQDataBlock
+class QPID_COMMON_CLASS_EXTERN AMQFrame : public AMQDataBlock
{
public:
QPID_COMMON_EXTERN AMQFrame(const boost::intrusive_ptr<AMQBody>& b=0);
@@ -59,6 +59,11 @@ class AMQFrame : public AMQDataBlock
return boost::polymorphic_downcast<const T*>(getBody());
}
+ /**
+ * Take a deep copy of the body currently referenced
+ */
+ QPID_COMMON_EXTERN void cloneBody();
+
QPID_COMMON_EXTERN void encode(Buffer& buffer) const;
QPID_COMMON_EXTERN bool decode(Buffer& buffer);
QPID_COMMON_EXTERN uint32_t encodedSize() const;
diff --git a/cpp/src/qpid/framing/AMQHeaderBody.h b/cpp/src/qpid/framing/AMQHeaderBody.h
index 8d96e35720..452154eb5c 100644
--- a/cpp/src/qpid/framing/AMQHeaderBody.h
+++ b/cpp/src/qpid/framing/AMQHeaderBody.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -35,7 +35,7 @@
namespace qpid {
namespace framing {
-class AMQHeaderBody : public AMQBody
+class QPID_COMMON_CLASS_EXTERN AMQHeaderBody : public AMQBody
{
template <class T> struct OptProps { boost::optional<T> props; };
template <class Base, class T>
@@ -58,7 +58,7 @@ class AMQHeaderBody : public AMQBody
}
else
return Base::decode(buffer, size, type);
- }
+ }
void print(std::ostream& out) const {
const boost::optional<T>& p=this->OptProps<T>::props;
if (p) out << *p;
@@ -77,7 +77,7 @@ class AMQHeaderBody : public AMQBody
typedef PropSet<PropSet<Empty, DeliveryProperties>, MessageProperties> Properties;
Properties properties;
-
+
public:
inline uint8_t type() const { return HEADER_BODY; }
@@ -99,6 +99,10 @@ public:
return properties.OptProps<T>::props.get_ptr();
}
+ template <class T> void erase() {
+ properties.OptProps<T>::props.reset();
+ }
+
boost::intrusive_ptr<AMQBody> clone() const { return BodyFactory::copy(*this); }
};
diff --git a/cpp/src/qpid/framing/AMQHeartbeatBody.h b/cpp/src/qpid/framing/AMQHeartbeatBody.h
index 9b1fe8a4c1..19ac2be013 100644
--- a/cpp/src/qpid/framing/AMQHeartbeatBody.h
+++ b/cpp/src/qpid/framing/AMQHeartbeatBody.h
@@ -29,7 +29,7 @@
namespace qpid {
namespace framing {
-class AMQHeartbeatBody : public AMQBody
+class QPID_COMMON_CLASS_EXTERN AMQHeartbeatBody : public AMQBody
{
public:
QPID_COMMON_EXTERN virtual ~AMQHeartbeatBody();
diff --git a/cpp/src/qpid/framing/FieldTable.cpp b/cpp/src/qpid/framing/FieldTable.cpp
index 023e4af819..f80d2f9fb1 100644
--- a/cpp/src/qpid/framing/FieldTable.cpp
+++ b/cpp/src/qpid/framing/FieldTable.cpp
@@ -129,7 +129,7 @@ FieldTable::ValuePtr FieldTable::get(const std::string& name) const
namespace {
template <class T> T default_value() { return T(); }
template <> int default_value<int>() { return 0; }
- template <> uint64_t default_value<uint64_t>() { return 0; }
+ //template <> uint64_t default_value<uint64_t>() { return 0; }
}
template <class T>
@@ -198,10 +198,12 @@ void FieldTable::encode(Buffer& buffer) const {
void FieldTable::decode(Buffer& buffer){
clear();
+ if (buffer.available() < 4)
+ throw IllegalArgumentException(QPID_MSG("Not enough data for field table."));
uint32_t len = buffer.getLong();
if (len) {
uint32_t available = buffer.available();
- if (available < len)
+ if ((available < len) || (available < 4))
throw IllegalArgumentException(QPID_MSG("Not enough data for field table."));
uint32_t count = buffer.getLong();
uint32_t leftover = available - len;
diff --git a/cpp/src/qpid/framing/List.cpp b/cpp/src/qpid/framing/List.cpp
index 963ebc206b..d7ea172bac 100644
--- a/cpp/src/qpid/framing/List.cpp
+++ b/cpp/src/qpid/framing/List.cpp
@@ -49,6 +49,9 @@ void List::encode(Buffer& buffer) const
void List::decode(Buffer& buffer)
{
values.clear();
+ if (buffer.available() < 4)
+ throw IllegalArgumentException(QPID_MSG("Not enough data for list, expected at least "
+ " 4 bytes but only " << buffer.available() << " available"));
uint32_t size = buffer.getLong();
uint32_t available = buffer.available();
if (available < size) {
@@ -56,6 +59,9 @@ void List::decode(Buffer& buffer)
<< size << " bytes but only " << available << " available"));
}
if (size) {
+ if (buffer.available() < 4)
+ throw IllegalArgumentException(QPID_MSG("Not enough data for list, expected at least "
+ " 4 bytes but only " << buffer.available() << " available"));
uint32_t count = buffer.getLong();
for (uint32_t i = 0; i < count; i++) {
ValuePtr value(new FieldValue);
diff --git a/cpp/src/qpid/framing/MethodBodyFactory.h b/cpp/src/qpid/framing/MethodBodyFactory.h
index 607ec9d959..88bc444795 100644
--- a/cpp/src/qpid/framing/MethodBodyFactory.h
+++ b/cpp/src/qpid/framing/MethodBodyFactory.h
@@ -22,6 +22,7 @@
*
*/
#include "qpid/framing/amqp_types.h"
+#include "qpid/framing/AMQBody.h"
#include <boost/intrusive_ptr.hpp>
namespace qpid {
diff --git a/cpp/src/qpid/framing/SendContent.h b/cpp/src/qpid/framing/SendContent.h
index 745c948c9e..1c464b9c8b 100644
--- a/cpp/src/qpid/framing/SendContent.h
+++ b/cpp/src/qpid/framing/SendContent.h
@@ -37,7 +37,7 @@ namespace framing {
*/
class SendContent
{
- mutable FrameHandler& handler;
+ FrameHandler& handler;
const uint16_t maxFrameSize;
uint expectedFrameCount;
uint frameCount;
diff --git a/cpp/src/qpid/framing/TransferContent.h b/cpp/src/qpid/framing/TransferContent.h
index 5fe1a513a9..9a698a1823 100644
--- a/cpp/src/qpid/framing/TransferContent.h
+++ b/cpp/src/qpid/framing/TransferContent.h
@@ -32,7 +32,7 @@ namespace qpid {
namespace framing {
/** Message content */
-class TransferContent : public MethodContent
+class QPID_COMMON_CLASS_EXTERN TransferContent : public MethodContent
{
AMQHeaderBody header;
std::string data;
diff --git a/cpp/src/qpid/framing/Uuid.cpp b/cpp/src/qpid/framing/Uuid.cpp
index 945c0a4d24..b3d1e2e1e4 100644
--- a/cpp/src/qpid/framing/Uuid.cpp
+++ b/cpp/src/qpid/framing/Uuid.cpp
@@ -59,7 +59,9 @@ void Uuid::clear() {
// Force int 0/!0 to false/true; avoids compile warnings.
bool Uuid::isNull() const {
- return !!uuid_is_null(data());
+ // This const cast is for Solaris which has a
+ // uuid_is_null that takes a non const argument
+ return !!uuid_is_null(const_cast<uint8_t*>(data()));
}
void Uuid::encode(Buffer& buf) const {
diff --git a/cpp/src/qpid/log/Logger.cpp b/cpp/src/qpid/log/Logger.cpp
index 2217cdddbd..1600822142 100644
--- a/cpp/src/qpid/log/Logger.cpp
+++ b/cpp/src/qpid/log/Logger.cpp
@@ -22,6 +22,7 @@
#include "qpid/memory.h"
#include "qpid/sys/Thread.h"
#include "qpid/sys/Time.h"
+#include "qpid/DisableExceptionLogging.h"
#include <boost/pool/detail/singleton.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>
@@ -48,11 +49,16 @@ Logger& Logger::instance() {
}
Logger::Logger() : flags(0) {
+ // Disable automatic logging in Exception constructors to avoid
+ // re-entrant use of logger singleton if there is an error in
+ // option parsing.
+ DisableExceptionLogging del;
+
// Initialize myself from env variables so all programs
// (e.g. tests) can use logging even if they don't parse
// command line args.
Options opts("");
- opts.parse(0, 0);
+ opts.parse(0, 0);
configure(opts);
}
@@ -73,8 +79,12 @@ void Logger::log(const Statement& s, const std::string& msg) {
std::ostringstream os;
if (!prefix.empty())
os << prefix << ": ";
- if (flags&TIME)
- qpid::sys::outputFormattedNow(os);
+ if (flags&TIME) {
+ if (flags&HIRES)
+ qpid::sys::outputHiresNow(os);
+ else
+ qpid::sys::outputFormattedNow(os);
+ }
if (flags&LEVEL)
os << LevelTraits::name(s.level) << " ";
if (flags&THREAD)
@@ -123,7 +133,8 @@ int Logger::format(const Options& opts) {
bitIf(opts.time, TIME) |
bitIf(opts.source, (FILE|LINE)) |
bitIf(opts.function, FUNCTION) |
- bitIf(opts.thread, THREAD);
+ bitIf(opts.thread, THREAD) |
+ bitIf(opts.hiresTs, HIRES);
format(flags);
return flags;
}
@@ -140,7 +151,7 @@ void Logger::configure(const Options& opts) {
Options o(opts);
if (o.trace)
o.selectors.push_back("trace+");
- format(o);
+ format(o);
select(Selector(o));
setPrefix(opts.prefix);
options.sinkOptions->setup(this);
diff --git a/cpp/src/qpid/log/Options.cpp b/cpp/src/qpid/log/Options.cpp
index 24ef413cbc..0001d00bdf 100644
--- a/cpp/src/qpid/log/Options.cpp
+++ b/cpp/src/qpid/log/Options.cpp
@@ -38,6 +38,7 @@ Options::Options(const std::string& argv0_, const std::string& name_) :
thread(false),
source(false),
function(false),
+ hiresTs(false),
trace(false),
sinkOptions (SinkOptions::create(argv0_))
{
@@ -65,6 +66,7 @@ Options::Options(const std::string& argv0_, const std::string& name_) :
("log-source", optValue(source,"yes|no"), "Include source file:line in log messages")
("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 unformatted hi-res timestamp in log messages")
("log-prefix", optValue(prefix,"STRING"), "Prefix to append to all log messages")
;
add(*sinkOptions);
@@ -80,6 +82,7 @@ Options::Options(const Options &o) :
thread(o.thread),
source(o.source),
function(o.function),
+ hiresTs(o.hiresTs),
trace(o.trace),
prefix(o.prefix),
sinkOptions (SinkOptions::create(o.argv0))
@@ -97,6 +100,7 @@ Options& Options::operator=(const Options& x) {
thread = x.thread;
source = x.source;
function = x.function;
+ hiresTs = x.hiresTs;
trace = x.trace;
prefix = x.prefix;
*sinkOptions = *x.sinkOptions;
diff --git a/cpp/src/qpid/log/Statement.cpp b/cpp/src/qpid/log/Statement.cpp
index 6a32b50096..7dfdf08703 100644
--- a/cpp/src/qpid/log/Statement.cpp
+++ b/cpp/src/qpid/log/Statement.cpp
@@ -27,8 +27,6 @@ namespace qpid {
namespace log {
namespace {
-using namespace std;
-
struct NonPrint { bool operator()(unsigned char c) { return !isprint(c) && !isspace(c); } };
const char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
@@ -39,7 +37,7 @@ std::string quote(const std::string& str) {
if (n==0) return str;
std::string ret;
ret.reserve(str.size()+2*n); // Avoid extra allocations.
- for (string::const_iterator i = str.begin(); i != str.end(); ++i) {
+ for (std::string::const_iterator i = str.begin(); i != str.end(); ++i) {
if (nonPrint(*i)) {
ret.push_back('\\');
ret.push_back('x');
@@ -50,7 +48,6 @@ std::string quote(const std::string& str) {
}
return ret;
}
-
}
void Statement::log(const std::string& message) {
diff --git a/cpp/src/qpid/log/posix/SinkOptions.cpp b/cpp/src/qpid/log/posix/SinkOptions.cpp
index 292e9147f6..ffa7633e3b 100644
--- a/cpp/src/qpid/log/posix/SinkOptions.cpp
+++ b/cpp/src/qpid/log/posix/SinkOptions.cpp
@@ -180,7 +180,7 @@ qpid::log::SinkOptions& SinkOptions::operator=(const qpid::log::SinkOptions& rhs
}
void SinkOptions::detached(void) {
- if (logToStderr && !logToStdout && !logToSyslog) {
+ if (logToStderr && !logToStdout && !logToSyslog && logFile.empty()) {
logToStderr = false;
logToSyslog = true;
}
diff --git a/cpp/src/qpid/log/windows/SinkOptions.cpp b/cpp/src/qpid/log/windows/SinkOptions.cpp
index 28f4b267e0..0c74bea64e 100644
--- a/cpp/src/qpid/log/windows/SinkOptions.cpp
+++ b/cpp/src/qpid/log/windows/SinkOptions.cpp
@@ -53,7 +53,7 @@ static int eventTypes[qpid::log::LevelTraits::COUNT] = {
class EventLogOutput : public qpid::log::Logger::Output {
public:
- EventLogOutput(const std::string& sourceName) : logHandle(0)
+ EventLogOutput(const std::string& /*sourceName*/) : logHandle(0)
{
logHandle = OpenEventLog(0, "Application");
}
@@ -83,7 +83,7 @@ private:
HANDLE logHandle;
};
-SinkOptions::SinkOptions(const std::string& argv0)
+SinkOptions::SinkOptions(const std::string& /*argv0*/)
: qpid::log::SinkOptions(),
logToStderr(true),
logToStdout(false),
diff --git a/cpp/src/qpid/log/windows/SinkOptions.h b/cpp/src/qpid/log/windows/SinkOptions.h
index 605822fd46..f270c504a2 100644
--- a/cpp/src/qpid/log/windows/SinkOptions.h
+++ b/cpp/src/qpid/log/windows/SinkOptions.h
@@ -26,7 +26,7 @@ namespace qpid {
namespace log {
namespace windows {
-struct SinkOptions : public qpid::log::SinkOptions {
+struct QPID_COMMON_CLASS_EXTERN SinkOptions : public qpid::log::SinkOptions {
QPID_COMMON_EXTERN SinkOptions(const std::string& argv0);
virtual ~SinkOptions() {}
diff --git a/cpp/src/qpid/management/ManagementAgent.cpp b/cpp/src/qpid/management/ManagementAgent.cpp
index 23c999a98a..5799a1adca 100644
--- a/cpp/src/qpid/management/ManagementAgent.cpp
+++ b/cpp/src/qpid/management/ManagementAgent.cpp
@@ -31,6 +31,7 @@
#include <qpid/broker/Message.h>
#include "qpid/framing/MessageTransferBody.h"
#include "qpid/sys/Time.h"
+#include "qpid/sys/Thread.h"
#include "qpid/broker/ConnectionState.h"
#include "qpid/broker/AclModule.h"
#include "qpid/types/Variant.h"
@@ -74,6 +75,18 @@ namespace {
}
return n2;
}
+
+struct ScopedManagementContext
+{
+ ScopedManagementContext(const qpid::broker::ConnectionState* context)
+ {
+ setManagementExecutionContext(context);
+ }
+ ~ScopedManagementContext()
+ {
+ setManagementExecutionContext(0);
+ }
+};
}
@@ -535,6 +548,7 @@ void ManagementAgent::sendBufferLH(Buffer& buf,
dp->setRoutingKey(routingKey);
msg->getFrames().append(content);
+ msg->setIsManagementMessage(true);
{
sys::Mutex::ScopedUnlock u(userLock);
@@ -600,7 +614,7 @@ void ManagementAgent::sendBufferLH(const string& data,
props->setAppId("qmf2");
for (i = headers.begin(); i != headers.end(); ++i) {
- msg->getOrInsertHeaders().setString(i->first, i->second.asString());
+ msg->insertCustomProperty(i->first, i->second.asString());
}
DeliveryProperties* dp =
@@ -608,9 +622,10 @@ void ManagementAgent::sendBufferLH(const string& data,
dp->setRoutingKey(routingKey);
if (ttl_msec) {
dp->setTtl(ttl_msec);
- msg->setTimestamp(broker->getExpiryPolicy());
+ msg->computeExpiration(broker->getExpiryPolicy());
}
msg->getFrames().append(content);
+ msg->setIsManagementMessage(true);
{
sys::Mutex::ScopedUnlock u(userLock);
@@ -2237,6 +2252,7 @@ void ManagementAgent::dispatchAgentCommandLH(Message& msg, bool viaLocal)
uint32_t bufferLen = inBuffer.getPosition();
inBuffer.reset();
+ ScopedManagementContext context((const qpid::broker::ConnectionState*) msg.getPublisher());
const framing::FieldTable *headers = msg.getApplicationHeaders();
if (headers && msg.getAppId() == "qmf2")
{
@@ -2740,200 +2756,14 @@ void ManagementAgent::debugSnapshot(const char* title) {
title << ": new objects" << dumpVector(newManagementObjects));
}
+
Variant::Map ManagementAgent::toMap(const FieldTable& from)
{
Variant::Map map;
-
- for (FieldTable::const_iterator iter = from.begin(); iter != from.end(); iter++) {
- const string& key(iter->first);
- const FieldTable::ValuePtr& val(iter->second);
-
- map[key] = toVariant(val);
- }
-
+ qpid::amqp_0_10::translate(from, map);
return map;
}
-Variant::List ManagementAgent::toList(const List& from)
-{
- Variant::List _list;
-
- for (List::const_iterator iter = from.begin(); iter != from.end(); iter++) {
- const List::ValuePtr& val(*iter);
-
- _list.push_back(toVariant(val));
- }
-
- return _list;
-}
-
-qpid::framing::FieldTable ManagementAgent::fromMap(const Variant::Map& from)
-{
- qpid::framing::FieldTable ft;
-
- for (Variant::Map::const_iterator iter = from.begin();
- iter != from.end();
- iter++) {
- const string& key(iter->first);
- const Variant& val(iter->second);
-
- ft.set(key, toFieldValue(val));
- }
-
- return ft;
-}
-
-
-List ManagementAgent::fromList(const Variant::List& from)
-{
- List fa;
-
- for (Variant::List::const_iterator iter = from.begin();
- iter != from.end();
- iter++) {
- const Variant& val(*iter);
-
- fa.push_back(toFieldValue(val));
- }
-
- return fa;
-}
-
-
-boost::shared_ptr<FieldValue> ManagementAgent::toFieldValue(const Variant& in)
-{
-
- switch(in.getType()) {
-
- case types::VAR_VOID: return boost::shared_ptr<FieldValue>(new VoidValue());
- case types::VAR_BOOL: return boost::shared_ptr<FieldValue>(new BoolValue(in.asBool()));
- case types::VAR_UINT8: return boost::shared_ptr<FieldValue>(new Unsigned8Value(in.asUint8()));
- case types::VAR_UINT16: return boost::shared_ptr<FieldValue>(new Unsigned16Value(in.asUint16()));
- case types::VAR_UINT32: return boost::shared_ptr<FieldValue>(new Unsigned32Value(in.asUint32()));
- case types::VAR_UINT64: return boost::shared_ptr<FieldValue>(new Unsigned64Value(in.asUint64()));
- case types::VAR_INT8: return boost::shared_ptr<FieldValue>(new Integer8Value(in.asInt8()));
- case types::VAR_INT16: return boost::shared_ptr<FieldValue>(new Integer16Value(in.asInt16()));
- case types::VAR_INT32: return boost::shared_ptr<FieldValue>(new Integer32Value(in.asInt32()));
- case types::VAR_INT64: return boost::shared_ptr<FieldValue>(new Integer64Value(in.asInt64()));
- case types::VAR_FLOAT: return boost::shared_ptr<FieldValue>(new FloatValue(in.asFloat()));
- case types::VAR_DOUBLE: return boost::shared_ptr<FieldValue>(new DoubleValue(in.asDouble()));
- case types::VAR_STRING: return boost::shared_ptr<FieldValue>(new Str16Value(in.asString()));
- case types::VAR_UUID: return boost::shared_ptr<FieldValue>(new UuidValue(in.asUuid().data()));
- case types::VAR_MAP: return boost::shared_ptr<FieldValue>(new FieldTableValue(ManagementAgent::fromMap(in.asMap())));
- case types::VAR_LIST: return boost::shared_ptr<FieldValue>(new ListValue(ManagementAgent::fromList(in.asList())));
- }
-
- QPID_LOG(error, "Unknown Variant type - not converted: [" << in.getType() << "]");
- return boost::shared_ptr<FieldValue>(new VoidValue());
-}
-
-// stolen from qpid/client/amqp0_10/Codecs.cpp - TODO: make Codecs public, and remove this dup.
-Variant ManagementAgent::toVariant(const boost::shared_ptr<FieldValue>& in)
-{
- const string iso885915("iso-8859-15");
- const string utf8("utf8");
- const string utf16("utf16");
- //const string binary("binary");
- const string amqp0_10_binary("amqp0-10:binary");
- //const string amqp0_10_bit("amqp0-10:bit");
- const string amqp0_10_datetime("amqp0-10:datetime");
- const string amqp0_10_struct("amqp0-10:struct");
- Variant out;
-
- //based on AMQP 0-10 typecode, pick most appropriate variant type
- switch (in->getType()) {
- //Fixed Width types:
- case 0x00: //bin8
- case 0x01: out.setEncoding(amqp0_10_binary); // int8
- case 0x02: out = in->getIntegerValue<int8_t, 1>(); break; //uint8
- case 0x03: out = in->getIntegerValue<uint8_t, 1>(); break; //
- // case 0x04: break; //TODO: iso-8859-15 char // char
- case 0x08: out = static_cast<bool>(in->getIntegerValue<uint8_t, 1>()); break; // bool int8
-
- case 0x10: out.setEncoding(amqp0_10_binary); // bin16
- case 0x11: out = in->getIntegerValue<int16_t, 2>(); break; // int16
- case 0x12: out = in->getIntegerValue<uint16_t, 2>(); break; //uint16
-
- case 0x20: out.setEncoding(amqp0_10_binary); // bin32
- case 0x21: out = in->getIntegerValue<int32_t, 4>(); break; // int32
- case 0x22: out = in->getIntegerValue<uint32_t, 4>(); break; // uint32
-
- case 0x23: out = in->get<float>(); break; // float(32)
-
- // case 0x27: break; //TODO: utf-32 char
-
- case 0x30: out.setEncoding(amqp0_10_binary); // bin64
- case 0x31: out = in->getIntegerValue<int64_t, 8>(); break; //int64
-
- case 0x38: out.setEncoding(amqp0_10_datetime); //treat datetime as uint64_t, but set encoding
- case 0x32: out = in->getIntegerValue<uint64_t, 8>(); break; //uint64
- case 0x33: out = in->get<double>(); break; // double
-
- case 0x48: // uuid
- {
- unsigned char data[16];
- in->getFixedWidthValue<16>(data);
- out = qpid::types::Uuid(data);
- } break;
-
- //TODO: figure out whether and how to map values with codes 0x40-0xd8
-
- 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: // str8
- case 0x90: // str16
- case 0xa0: // str32
- out = in->get<string>();
- out.setEncoding(amqp0_10_binary);
- break;
-
- case 0x84: // str8
- case 0x94: // str16
- out = in->get<string>();
- out.setEncoding(iso885915);
- break;
-
- case 0x85: // str8
- case 0x95: // str16
- out = in->get<string>();
- out.setEncoding(utf8);
- break;
-
- case 0x86: // str8
- case 0x96: // str16
- out = in->get<string>();
- out.setEncoding(utf16);
- break;
-
- case 0xab: // str32
- out = in->get<string>();
- out.setEncoding(amqp0_10_struct);
- break;
-
- case 0xa8: // map
- out = ManagementAgent::toMap(in->get<FieldTable>());
- break;
-
- case 0xa9: // list of variant types
- out = ManagementAgent::toList(in->get<List>());
- break;
- //case 0xaa: //convert amqp0-10 array (uniform type) into variant list
- // out = Variant::List();
- // translate<Array>(in, out.asList(), &toVariant);
- // break;
-
- default:
- //error?
- QPID_LOG(error, "Unknown FieldValue type - not converted: [" << (unsigned int)(in->getType()) << "]");
- break;
- }
-
- return out;
-}
-
// Build up a list of the current set of deleted objects that are pending their
// next (last) publish-ment.
@@ -3085,3 +2915,21 @@ bool ManagementAgent::moveDeletedObjectsLH() {
}
return !deleteList.empty();
}
+
+namespace qpid {
+namespace management {
+
+namespace {
+QPID_TSS const qpid::broker::ConnectionState* executionContext = 0;
+}
+
+void setManagementExecutionContext(const qpid::broker::ConnectionState* ctxt)
+{
+ executionContext = ctxt;
+}
+const qpid::broker::ConnectionState* getManagementExecutionContext()
+{
+ return executionContext;
+}
+
+}}
diff --git a/cpp/src/qpid/management/ManagementAgent.h b/cpp/src/qpid/management/ManagementAgent.h
index 0db19594a7..c21f384433 100644
--- a/cpp/src/qpid/management/ManagementAgent.h
+++ b/cpp/src/qpid/management/ManagementAgent.h
@@ -41,6 +41,9 @@
#include <map>
namespace qpid {
+namespace broker {
+class ConnectionState;
+}
namespace management {
class ManagementAgent
@@ -142,13 +145,7 @@ public:
const framing::Uuid& getUuid() const { return uuid; }
void setUuid(const framing::Uuid& id) { uuid = id; writeData(); }
- // TODO: remove these when Variant API moved into common library.
static types::Variant::Map toMap(const framing::FieldTable& from);
- static framing::FieldTable fromMap(const types::Variant::Map& from);
- static types::Variant::List toList(const framing::List& from);
- static framing::List fromList(const types::Variant::List& from);
- static boost::shared_ptr<framing::FieldValue> toFieldValue(const types::Variant& in);
- static types::Variant toVariant(const boost::shared_ptr<framing::FieldValue>& val);
// For Clustering: management objects that have been marked as
// "deleted", but are waiting for their last published object
@@ -422,6 +419,8 @@ private:
void debugSnapshot(const char* title);
};
+void setManagementExecutionContext(const qpid::broker::ConnectionState*);
+const qpid::broker::ConnectionState* getManagementExecutionContext();
}}
-
+
#endif /*!_ManagementAgent_*/
diff --git a/cpp/src/qpid/messaging/AddressParser.cpp b/cpp/src/qpid/messaging/AddressParser.cpp
index 4c8f35fbc5..d76210ee5d 100644
--- a/cpp/src/qpid/messaging/AddressParser.cpp
+++ b/cpp/src/qpid/messaging/AddressParser.cpp
@@ -151,7 +151,7 @@ bool AddressParser::readValueIfExists(Variant& value)
bool AddressParser::readString(std::string& value, char delimiter)
{
if (readChar(delimiter)) {
- std::string::size_type start = current++;
+ std::string::size_type start = current;
while (!eos()) {
if (input.at(current) == delimiter) {
if (current > start) {
@@ -201,7 +201,8 @@ bool AddressParser::readSimpleValue(Variant& value)
{
std::string s;
if (readWord(s)) {
- value.parse(s);
+ value.parse(s);
+ if (value.getType() == VAR_STRING) value.setEncoding("utf8");
return true;
} else {
return false;
diff --git a/cpp/src/qpid/messaging/Duration.cpp b/cpp/src/qpid/messaging/Duration.cpp
index a2c443c746..a23e9f5bcb 100644
--- a/cpp/src/qpid/messaging/Duration.cpp
+++ b/cpp/src/qpid/messaging/Duration.cpp
@@ -37,6 +37,16 @@ Duration operator*(uint64_t multiplier, const Duration& duration)
return Duration(duration.getMilliseconds() * multiplier);
}
+bool operator==(const Duration& a, const Duration& b)
+{
+ return a.getMilliseconds() == b.getMilliseconds();
+}
+
+bool operator!=(const Duration& a, const Duration& b)
+{
+ return a.getMilliseconds() != b.getMilliseconds();
+}
+
const Duration Duration::FOREVER(std::numeric_limits<uint64_t>::max());
const Duration Duration::IMMEDIATE(0);
const Duration Duration::SECOND(1000);
diff --git a/cpp/src/qpid/messaging/Message.cpp b/cpp/src/qpid/messaging/Message.cpp
index 83cdfd3c55..ef70c103e9 100644
--- a/cpp/src/qpid/messaging/Message.cpp
+++ b/cpp/src/qpid/messaging/Message.cpp
@@ -21,6 +21,7 @@
#include "qpid/messaging/Message.h"
#include "qpid/messaging/MessageImpl.h"
#include "qpid/amqp_0_10/Codecs.h"
+#include <qpid/Exception.h>
#include <boost/format.hpp>
namespace qpid {
@@ -115,7 +116,11 @@ template <class C> struct MessageCodec
static void decode(const Message& message, typename C::ObjectType& object, const std::string& encoding)
{
checkEncoding(message, encoding);
- C::decode(message.getContent(), object);
+ try {
+ C::decode(message.getContent(), object);
+ } catch (const qpid::Exception &ex) {
+ throw EncodingException(ex.what());
+ }
}
static void encode(const typename C::ObjectType& map, Message& message, const std::string& encoding)
diff --git a/cpp/src/qpid/messaging/Session.cpp b/cpp/src/qpid/messaging/Session.cpp
index 496953a8e5..cccfd9a873 100644
--- a/cpp/src/qpid/messaging/Session.cpp
+++ b/cpp/src/qpid/messaging/Session.cpp
@@ -39,7 +39,8 @@ Session& Session::operator=(const Session& s) { return PI::assign(*this, s); }
void Session::commit() { impl->commit(); }
void Session::rollback() { impl->rollback(); }
void Session::acknowledge(bool sync) { impl->acknowledge(sync); }
-void Session::acknowledge(Message& m, bool s) { impl->acknowledge(m); sync(s); }
+void Session::acknowledge(Message& m, bool s) { impl->acknowledge(m, false); sync(s); }
+void Session::acknowledgeUpTo(Message& m, bool s) { impl->acknowledge(m, true); sync(s); }
void Session::reject(Message& m) { impl->reject(m); }
void Session::release(Message& m) { impl->release(m); }
void Session::close() { impl->close(); }
diff --git a/cpp/src/qpid/messaging/SessionImpl.h b/cpp/src/qpid/messaging/SessionImpl.h
index 02a254e4f2..60ae615253 100644
--- a/cpp/src/qpid/messaging/SessionImpl.h
+++ b/cpp/src/qpid/messaging/SessionImpl.h
@@ -41,7 +41,7 @@ class SessionImpl : public virtual qpid::RefCounted
virtual void commit() = 0;
virtual void rollback() = 0;
virtual void acknowledge(bool sync) = 0;
- virtual void acknowledge(Message&) = 0;
+ virtual void acknowledge(Message&, bool cumulative) = 0;
virtual void reject(Message&) = 0;
virtual void release(Message&) = 0;
virtual void close() = 0;
diff --git a/cpp/src/qpid/replication/ReplicatingEventListener.cpp b/cpp/src/qpid/replication/ReplicatingEventListener.cpp
index b7d52372f4..0ced4d9161 100644
--- a/cpp/src/qpid/replication/ReplicatingEventListener.cpp
+++ b/cpp/src/qpid/replication/ReplicatingEventListener.cpp
@@ -69,10 +69,9 @@ void ReplicatingEventListener::deliverDequeueMessage(const QueuedMessage& dequeu
void ReplicatingEventListener::deliverEnqueueMessage(const QueuedMessage& enqueued)
{
boost::intrusive_ptr<Message> msg(cloneMessage(*(enqueued.queue), enqueued.payload));
- FieldTable& headers = msg->getProperties<MessageProperties>()->getApplicationHeaders();
- headers.setString(REPLICATION_TARGET_QUEUE, enqueued.queue->getName());
- headers.setInt(REPLICATION_EVENT_TYPE, ENQUEUE);
- headers.setInt(QUEUE_MESSAGE_POSITION,enqueued.position);
+ msg->insertCustomProperty(REPLICATION_TARGET_QUEUE, enqueued.queue->getName());
+ msg->insertCustomProperty(REPLICATION_EVENT_TYPE, ENQUEUE);
+ msg->insertCustomProperty(QUEUE_MESSAGE_POSITION,enqueued.position);
route(msg);
}
diff --git a/cpp/src/qpid/replication/ReplicationExchange.cpp b/cpp/src/qpid/replication/ReplicationExchange.cpp
index 4b6d25ac7d..89a2bf516d 100644
--- a/cpp/src/qpid/replication/ReplicationExchange.cpp
+++ b/cpp/src/qpid/replication/ReplicationExchange.cpp
@@ -97,11 +97,10 @@ void ReplicationExchange::handleEnqueueEvent(const FieldTable* args, Deliverable
} else {
queue->setPosition(seqno1);
- FieldTable& headers = msg.getMessage().getProperties<MessageProperties>()->getApplicationHeaders();
- headers.erase(REPLICATION_TARGET_QUEUE);
- headers.erase(REPLICATION_EVENT_SEQNO);
- headers.erase(REPLICATION_EVENT_TYPE);
- headers.erase(QUEUE_MESSAGE_POSITION);
+ msg.getMessage().removeCustomProperty(REPLICATION_TARGET_QUEUE);
+ msg.getMessage().removeCustomProperty(REPLICATION_EVENT_SEQNO);
+ msg.getMessage().removeCustomProperty(REPLICATION_EVENT_TYPE);
+ msg.getMessage().removeCustomProperty(QUEUE_MESSAGE_POSITION);
msg.deliverTo(queue);
QPID_LOG(debug, "Enqueued replicated message onto " << queueName);
if (mgmtExchange != 0) {
diff --git a/cpp/src/qpid/store/StorageProvider.h b/cpp/src/qpid/store/StorageProvider.h
index bc8d187517..d162cc58ec 100644
--- a/cpp/src/qpid/store/StorageProvider.h
+++ b/cpp/src/qpid/store/StorageProvider.h
@@ -54,7 +54,7 @@ struct QueueEntry {
QueueEntry(uint64_t id, TplStatus tpl = NONE, const std::string& x = "")
: queueId(id), tplStatus(tpl), xid(x) {}
- bool operator==(const QueueEntry& rhs) {
+ bool operator==(const QueueEntry& rhs) const {
if (queueId != rhs.queueId) return false;
if (tplStatus == NONE && rhs.tplStatus == NONE) return true;
return xid == rhs.xid;
diff --git a/cpp/src/qpid/sys/AggregateOutput.h b/cpp/src/qpid/sys/AggregateOutput.h
index 6dad998bb0..d7c0ff29e3 100644
--- a/cpp/src/qpid/sys/AggregateOutput.h
+++ b/cpp/src/qpid/sys/AggregateOutput.h
@@ -41,7 +41,7 @@ namespace sys {
* doOutput is called in another.
*/
-class AggregateOutput : public OutputTask, public OutputControl
+class QPID_COMMON_CLASS_EXTERN AggregateOutput : public OutputTask, public OutputControl
{
typedef std::deque<OutputTask*> TaskList;
diff --git a/cpp/src/qpid/sys/AsynchIO.h b/cpp/src/qpid/sys/AsynchIO.h
index 50da8fa4fc..41f74f7ed0 100644
--- a/cpp/src/qpid/sys/AsynchIO.h
+++ b/cpp/src/qpid/sys/AsynchIO.h
@@ -64,8 +64,8 @@ public:
// deletes. To correctly manage heaps when needed, the allocate and
// delete should both be done from the same class/library.
QPID_COMMON_EXTERN static AsynchConnector* create(const Socket& s,
- std::string hostname,
- uint16_t port,
+ const std::string& hostname,
+ const std::string& port,
ConnectedCallback connCb,
FailedCallback failCb);
virtual void start(boost::shared_ptr<Poller> poller) = 0;
diff --git a/cpp/src/qpid/sys/AsynchIOHandler.h b/cpp/src/qpid/sys/AsynchIOHandler.h
index e1885bac79..b9867606c4 100644
--- a/cpp/src/qpid/sys/AsynchIOHandler.h
+++ b/cpp/src/qpid/sys/AsynchIOHandler.h
@@ -57,7 +57,7 @@ class AsynchIOHandler : public OutputControl {
QPID_COMMON_EXTERN ~AsynchIOHandler();
QPID_COMMON_EXTERN void init(AsynchIO* a, int numBuffs);
- QPID_COMMON_EXTERN void setClient() { isClient = true; }
+ QPID_COMMON_INLINE_EXTERN void setClient() { isClient = true; }
// Output side
QPID_COMMON_EXTERN void abort();
diff --git a/cpp/src/qpid/sys/AtomicValue.h b/cpp/src/qpid/sys/AtomicValue.h
index 6e90eafead..bf995f991e 100644
--- a/cpp/src/qpid/sys/AtomicValue.h
+++ b/cpp/src/qpid/sys/AtomicValue.h
@@ -22,7 +22,12 @@
*
*/
-#if defined( __GNUC__ ) && __GNUC__ >= 4 && ( defined( __i686__ ) || defined( __x86_64__ ) )
+// Have to check for clang before gcc as clang pretends to be gcc too
+#if defined( __clang__ )
+// Use the clang doesn't support atomic builtins for 64 bit values, so use the slow versions
+#include "qpid/sys/AtomicValue_mutex.h"
+
+#elif defined( __GNUC__ ) && __GNUC__ >= 4 && ( defined( __i686__ ) || defined( __x86_64__ ) )
// Use the Gnu C built-in atomic operations if compiling with gcc on a suitable platform.
#include "qpid/sys/AtomicValue_gcc.h"
diff --git a/cpp/src/qpid/sys/AtomicValue_gcc.h b/cpp/src/qpid/sys/AtomicValue_gcc.h
index d022b07c1d..724bae422e 100644
--- a/cpp/src/qpid/sys/AtomicValue_gcc.h
+++ b/cpp/src/qpid/sys/AtomicValue_gcc.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -39,6 +39,9 @@ class AtomicValue
public:
AtomicValue(T init=0) : value(init) {}
+ // Not atomic. Don't call concurrently with atomic ops.
+ AtomicValue<T>& operator=(T newValue) { value = newValue; return *this; }
+
// Update and return new value.
inline T operator+=(T n) { return __sync_add_and_fetch(&value, n); }
inline T operator-=(T n) { return __sync_sub_and_fetch(&value, n); }
@@ -54,11 +57,11 @@ class AtomicValue
/** If current value == testval then set to newval. Returns the old value. */
T valueCompareAndSwap(T testval, T newval) { return __sync_val_compare_and_swap(&value, testval, newval); }
- /** If current value == testval then set to newval. Returns true if the swap was performed. */
+ /** If current value == testval then set to newval. Returns true if the swap was performed. */
bool boolCompareAndSwap(T testval, T newval) { return __sync_bool_compare_and_swap(&value, testval, newval); }
T get() const { return const_cast<AtomicValue<T>*>(this)->fetchAndAdd(static_cast<T>(0)); }
-
+
private:
T value;
};
diff --git a/cpp/src/qpid/sys/ClusterSafe.cpp b/cpp/src/qpid/sys/ClusterSafe.cpp
index c6b527dfdf..dd37615145 100644
--- a/cpp/src/qpid/sys/ClusterSafe.cpp
+++ b/cpp/src/qpid/sys/ClusterSafe.cpp
@@ -34,8 +34,6 @@ QPID_TSS bool inContext = false;
bool isClusterSafe() { return !inCluster || inContext; }
-bool isCluster() { return inCluster; }
-
void assertClusterSafe() {
if (!isClusterSafe()) {
QPID_LOG(critical, "Modified cluster state outside of cluster context");
@@ -53,6 +51,16 @@ ClusterSafeScope::~ClusterSafeScope() {
inContext = save;
}
+ClusterUnsafeScope::ClusterUnsafeScope() {
+ save = inContext;
+ inContext = false;
+}
+
+ClusterUnsafeScope::~ClusterUnsafeScope() {
+ assert(!inContext);
+ inContext = save;
+}
+
void enableClusterSafe() { inCluster = true; }
}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/ClusterSafe.h b/cpp/src/qpid/sys/ClusterSafe.h
index 15675e8cc5..27e4eb46a5 100644
--- a/cpp/src/qpid/sys/ClusterSafe.h
+++ b/cpp/src/qpid/sys/ClusterSafe.h
@@ -52,14 +52,9 @@ QPID_COMMON_EXTERN void assertClusterSafe();
*/
QPID_COMMON_EXTERN bool isClusterSafe();
-/** Return true in a clustered broker */
-QPID_COMMON_EXTERN bool isCluster();
-
/**
- * Base class for classes that encapsulate state which is replicated
- * to all members of a cluster. Acts as a marker for clustered state
- * and provides functions to assist detecting bugs in cluster
- * behavior.
+ * Mark a scope as cluster safe. Sets isClusterSafe in constructor and resets
+ * to previous value in destructor.
*/
class ClusterSafeScope {
public:
@@ -70,6 +65,18 @@ class ClusterSafeScope {
};
/**
+ * Mark a scope as cluster unsafe. Clears isClusterSafe in constructor and resets
+ * to previous value in destructor.
+ */
+class ClusterUnsafeScope {
+ public:
+ QPID_COMMON_EXTERN ClusterUnsafeScope();
+ QPID_COMMON_EXTERN ~ClusterUnsafeScope();
+ private:
+ bool save;
+};
+
+/**
* Enable cluster-safe assertions. By default they are no-ops.
* Called by cluster code.
*/
diff --git a/cpp/src/qpid/sys/CopyOnWriteArray.h b/cpp/src/qpid/sys/CopyOnWriteArray.h
index 45a231dfd8..41384fc5a4 100644
--- a/cpp/src/qpid/sys/CopyOnWriteArray.h
+++ b/cpp/src/qpid/sys/CopyOnWriteArray.h
@@ -43,6 +43,12 @@ public:
CopyOnWriteArray() {}
CopyOnWriteArray(const CopyOnWriteArray& c) : array(c.array) {}
+ bool empty()
+ {
+ Mutex::ScopedLock l(lock);
+ return array ? array->empty() : true;
+ }
+
void add(T& t)
{
Mutex::ScopedLock l(lock);
diff --git a/cpp/src/qpid/sys/PollableQueue.h b/cpp/src/qpid/sys/PollableQueue.h
index 81c2301c1e..03b9d0084d 100644
--- a/cpp/src/qpid/sys/PollableQueue.h
+++ b/cpp/src/qpid/sys/PollableQueue.h
@@ -10,9 +10,9 @@
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -28,7 +28,8 @@
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <algorithm>
-#include <vector>
+#include <deque>
+#include "qpid/log/Statement.h" // FIXME aconway 2011-08-05:
namespace qpid {
namespace sys {
@@ -44,7 +45,7 @@ class Poller;
template <class T>
class PollableQueue {
public:
- typedef std::vector<T> Batch;
+ typedef std::deque<T> Batch;
typedef T value_type;
/**
@@ -68,11 +69,11 @@ class PollableQueue {
const boost::shared_ptr<sys::Poller>& poller);
~PollableQueue();
-
+
/** Push a value onto the queue. Thread safe */
void push(const T& t);
- /** Start polling. */
+ /** Start polling. */
void start();
/** Stop polling and wait for the current callback, if any, to complete. */
@@ -90,14 +91,14 @@ class PollableQueue {
* ensure clean shutdown with no events left on the queue.
*/
void shutdown();
-
+
private:
typedef sys::Monitor::ScopedLock ScopedLock;
typedef sys::Monitor::ScopedUnlock ScopedUnlock;
void dispatch(PollableCondition& cond);
void process();
-
+
mutable sys::Monitor lock;
Callback callback;
PollableCondition condition;
@@ -107,7 +108,7 @@ class PollableQueue {
};
template <class T> PollableQueue<T>::PollableQueue(
- const Callback& cb, const boost::shared_ptr<sys::Poller>& p)
+ const Callback& cb, const boost::shared_ptr<sys::Poller>& p)
: callback(cb),
condition(boost::bind(&PollableQueue<T>::dispatch, this, _1), p),
stopped(true)
@@ -151,7 +152,7 @@ template <class T> void PollableQueue<T>::process() {
putBack = callback(batch);
}
// put back unprocessed items.
- queue.insert(queue.begin(), putBack, typename Batch::const_iterator(batch.end()));
+ queue.insert(queue.begin(), putBack, typename Batch::const_iterator(batch.end()));
batch.clear();
}
}
diff --git a/cpp/src/qpid/sys/Poller.h b/cpp/src/qpid/sys/Poller.h
index ec53b79bad..01ee139ee6 100644
--- a/cpp/src/qpid/sys/Poller.h
+++ b/cpp/src/qpid/sys/Poller.h
@@ -120,7 +120,7 @@ class PollerHandle {
friend struct Poller::Event;
PollerHandlePrivate* const impl;
- QPID_COMMON_EXTERN virtual void processEvent(Poller::EventType) {};
+ QPID_COMMON_INLINE_EXTERN virtual void processEvent(Poller::EventType) {};
public:
QPID_COMMON_EXTERN PollerHandle(const IOHandle& h);
diff --git a/cpp/src/qpid/sys/ProtocolFactory.h b/cpp/src/qpid/sys/ProtocolFactory.h
index b233b2da1a..4d198a92da 100644
--- a/cpp/src/qpid/sys/ProtocolFactory.h
+++ b/cpp/src/qpid/sys/ProtocolFactory.h
@@ -39,11 +39,10 @@ class ProtocolFactory : public qpid::SharedObject<ProtocolFactory>
virtual ~ProtocolFactory() = 0;
virtual uint16_t getPort() const = 0;
- virtual std::string getHost() const = 0;
virtual void accept(boost::shared_ptr<Poller>, ConnectionCodec::Factory*) = 0;
virtual void connect(
boost::shared_ptr<Poller>,
- const std::string& host, int16_t port,
+ const std::string& host, const std::string& port,
ConnectionCodec::Factory* codec,
ConnectFailedCallback failed) = 0;
virtual bool supports(const std::string& /*capability*/) { return false; }
diff --git a/cpp/src/qpid/sys/RdmaIOPlugin.cpp b/cpp/src/qpid/sys/RdmaIOPlugin.cpp
index d53db20598..6769e5383c 100644
--- a/cpp/src/qpid/sys/RdmaIOPlugin.cpp
+++ b/cpp/src/qpid/sys/RdmaIOPlugin.cpp
@@ -31,7 +31,6 @@
#include "qpid/sys/SecuritySettings.h"
#include <boost/bind.hpp>
-#include <boost/lexical_cast.hpp>
#include <memory>
#include <netdb.h>
@@ -212,10 +211,9 @@ void RdmaIOHandler::readbuff(Rdma::AsynchIO&, Rdma::Buffer* buff) {
if (readError) {
return;
}
- size_t decoded = 0;
try {
if (codec) {
- decoded = codec->decode(buff->bytes(), buff->dataCount());
+ (void) codec->decode(buff->bytes(), buff->dataCount());
}else{
// Need to start protocol processing
initProtocolIn(buff);
@@ -230,9 +228,7 @@ void RdmaIOHandler::readbuff(Rdma::AsynchIO&, Rdma::Buffer* buff) {
void RdmaIOHandler::initProtocolIn(Rdma::Buffer* buff) {
framing::Buffer in(buff->bytes(), buff->dataCount());
framing::ProtocolInitiation protocolInit;
- size_t decoded = 0;
if (protocolInit.decode(in)) {
- decoded = in.getPosition();
QPID_LOG(debug, "Rdma: RECV [" << identifier << "] INIT(" << protocolInit << ")");
codec = factory->create(protocolInit.getVersion(), *this, identifier, SecuritySettings());
@@ -254,10 +250,9 @@ class RdmaIOProtocolFactory : public ProtocolFactory {
public:
RdmaIOProtocolFactory(int16_t port, int backlog);
void accept(Poller::shared_ptr, ConnectionCodec::Factory*);
- void connect(Poller::shared_ptr, const string& host, int16_t port, ConnectionCodec::Factory*, ConnectFailedCallback);
+ void connect(Poller::shared_ptr, const string& host, const std::string& port, ConnectionCodec::Factory*, ConnectFailedCallback);
uint16_t getPort() const;
- string getHost() const;
private:
bool request(Rdma::Connection::intrusive_ptr, const Rdma::ConnectionParams&, ConnectionCodec::Factory*);
@@ -347,18 +342,7 @@ uint16_t RdmaIOProtocolFactory::getPort() const {
return listeningPort; // Immutable no need for lock.
}
-string RdmaIOProtocolFactory::getHost() const {
- //return listener.getSockname();
- return "";
-}
-
void RdmaIOProtocolFactory::accept(Poller::shared_ptr poller, ConnectionCodec::Factory* fact) {
- ::sockaddr_in sin;
-
- sin.sin_family = AF_INET;
- sin.sin_port = htons(listeningPort);
- sin.sin_addr.s_addr = INADDR_ANY;
-
listener.reset(
new Rdma::Listener(
Rdma::ConnectionParams(65536, Rdma::DEFAULT_WR_ENTRIES),
@@ -387,7 +371,7 @@ void RdmaIOProtocolFactory::connected(Poller::shared_ptr poller, Rdma::Connectio
void RdmaIOProtocolFactory::connect(
Poller::shared_ptr poller,
- const std::string& host, int16_t port,
+ const std::string& host, const std::string& port,
ConnectionCodec::Factory* f,
ConnectFailedCallback failed)
{
@@ -399,7 +383,7 @@ void RdmaIOProtocolFactory::connect(
boost::bind(&RdmaIOProtocolFactory::disconnected, this, _1),
boost::bind(&RdmaIOProtocolFactory::rejected, this, _1, _2, failed));
- SocketAddress sa(host, boost::lexical_cast<std::string>(port));
+ SocketAddress sa(host, port);
c->start(poller, sa);
}
diff --git a/cpp/src/qpid/sys/Socket.h b/cpp/src/qpid/sys/Socket.h
index 7d50afc59f..defec4879c 100644
--- a/cpp/src/qpid/sys/Socket.h
+++ b/cpp/src/qpid/sys/Socket.h
@@ -33,21 +33,21 @@ namespace sys {
class Duration;
class SocketAddress;
-class Socket : public IOHandle
+class QPID_COMMON_CLASS_EXTERN Socket : public IOHandle
{
public:
/** Create a socket wrapper for descriptor. */
QPID_COMMON_EXTERN Socket();
- /** Set timeout for read and write */
- void setTimeout(const Duration& interval) const;
+ /** Create a new Socket which is the same address family as this one */
+ QPID_COMMON_EXTERN Socket* createSameTypeSocket() const;
/** Set socket non blocking */
void setNonblocking() const;
QPID_COMMON_EXTERN void setTcpNoDelay() const;
- QPID_COMMON_EXTERN void connect(const std::string& host, uint16_t port) const;
+ QPID_COMMON_EXTERN void connect(const std::string& host, const std::string& port) const;
QPID_COMMON_EXTERN void connect(const SocketAddress&) const;
QPID_COMMON_EXTERN void close() const;
@@ -57,19 +57,9 @@ public:
*@param backlog maximum number of pending connections.
*@return The bound port.
*/
- QPID_COMMON_EXTERN int listen(uint16_t port = 0, int backlog = 10) const;
+ QPID_COMMON_EXTERN int listen(const std::string& host = "", const std::string& port = "0", int backlog = 10) const;
QPID_COMMON_EXTERN int listen(const SocketAddress&, int backlog = 10) const;
- /** Returns the "socket name" ie the address bound to
- * the near end of the socket
- */
- QPID_COMMON_EXTERN std::string getSockname() const;
-
- /** Returns the "peer name" ie the address bound to
- * the remote end of the socket
- */
- std::string getPeername() const;
-
/**
* Returns an address (host and port) for the remote end of the
* socket
@@ -84,16 +74,13 @@ public:
/**
* Returns the full address of the connection: local and remote host and port.
*/
- QPID_COMMON_EXTERN std::string getFullAddress() const { return getLocalAddress()+"-"+getPeerAddress(); }
-
- QPID_COMMON_EXTERN uint16_t getLocalPort() const;
- uint16_t getRemotePort() const;
+ QPID_COMMON_INLINE_EXTERN std::string getFullAddress() const { return getLocalAddress()+"-"+getPeerAddress(); }
/**
* Returns the error code stored in the socket. This may be used
* to determine the result of a non-blocking connect.
*/
- int getError() const;
+ QPID_COMMON_EXTERN int getError() const;
/** Accept a connection from a socket that is already listening
* and has an incoming connection
@@ -108,8 +95,13 @@ private:
/** Create socket */
void createSocket(const SocketAddress&) const;
+public:
+ /** Construct socket with existing handle */
Socket(IOHandlePrivate*);
- mutable std::string connectname;
+
+protected:
+ mutable std::string localname;
+ mutable std::string peername;
mutable bool nonblocking;
mutable bool nodelay;
};
diff --git a/cpp/src/qpid/sys/SocketAddress.h b/cpp/src/qpid/sys/SocketAddress.h
index 27b9642f2c..dcca109d94 100644
--- a/cpp/src/qpid/sys/SocketAddress.h
+++ b/cpp/src/qpid/sys/SocketAddress.h
@@ -27,6 +27,7 @@
#include <string>
struct addrinfo;
+struct sockaddr;
namespace qpid {
namespace sys {
@@ -41,12 +42,19 @@ public:
QPID_COMMON_EXTERN SocketAddress& operator=(const SocketAddress&);
QPID_COMMON_EXTERN ~SocketAddress();
- std::string asString() const;
+ QPID_COMMON_EXTERN bool nextAddress();
+ QPID_COMMON_EXTERN std::string asString(bool numeric=true) const;
+ QPID_COMMON_EXTERN void setAddrInfoPort(uint16_t port);
+
+ QPID_COMMON_EXTERN static std::string asString(::sockaddr const * const addr, size_t addrlen);
+ QPID_COMMON_EXTERN static uint16_t getPort(::sockaddr const * const addr);
+
private:
std::string host;
std::string port;
mutable ::addrinfo* addrInfo;
+ mutable ::addrinfo* currentAddrInfo;
};
}}
diff --git a/cpp/src/qpid/sys/SslPlugin.cpp b/cpp/src/qpid/sys/SslPlugin.cpp
index b0e791d60b..ab15785492 100644
--- a/cpp/src/qpid/sys/SslPlugin.cpp
+++ b/cpp/src/qpid/sys/SslPlugin.cpp
@@ -25,6 +25,8 @@
#include "qpid/sys/ssl/check.h"
#include "qpid/sys/ssl/util.h"
#include "qpid/sys/ssl/SslHandler.h"
+#include "qpid/sys/AsynchIOHandler.h"
+#include "qpid/sys/AsynchIO.h"
#include "qpid/sys/ssl/SslIo.h"
#include "qpid/sys/ssl/SslSocket.h"
#include "qpid/broker/Broker.h"
@@ -37,15 +39,19 @@
namespace qpid {
namespace sys {
+using namespace qpid::sys::ssl;
+
struct SslServerOptions : ssl::SslOptions
{
uint16_t port;
bool clientAuth;
bool nodict;
+ bool multiplex;
SslServerOptions() : port(5671),
clientAuth(false),
- nodict(false)
+ nodict(false),
+ multiplex(false)
{
addOptions()
("ssl-port", optValue(port, "PORT"), "Port on which to listen for SSL connections")
@@ -56,29 +62,37 @@ struct SslServerOptions : ssl::SslOptions
}
};
-class SslProtocolFactory : public ProtocolFactory {
+template <class T>
+class SslProtocolFactoryTmpl : public ProtocolFactory {
+ private:
+
+ typedef SslAcceptorTmpl<T> SslAcceptor;
+
const bool tcpNoDelay;
- qpid::sys::ssl::SslSocket listener;
+ T listener;
const uint16_t listeningPort;
- std::auto_ptr<qpid::sys::ssl::SslAcceptor> acceptor;
+ std::auto_ptr<SslAcceptor> acceptor;
bool nodict;
public:
- SslProtocolFactory(const SslServerOptions&, int backlog, bool nodelay);
+ SslProtocolFactoryTmpl(const SslServerOptions&, int backlog, bool nodelay);
void accept(Poller::shared_ptr, ConnectionCodec::Factory*);
- void connect(Poller::shared_ptr, const std::string& host, int16_t port,
+ void connect(Poller::shared_ptr, const std::string& host, const std::string& port,
ConnectionCodec::Factory*,
boost::function2<void, int, std::string> failed);
uint16_t getPort() const;
- std::string getHost() const;
bool supports(const std::string& capability);
private:
- void established(Poller::shared_ptr, const qpid::sys::ssl::SslSocket&, ConnectionCodec::Factory*,
+ void established(Poller::shared_ptr, const Socket&, ConnectionCodec::Factory*,
bool isClient);
};
+typedef SslProtocolFactoryTmpl<SslSocket> SslProtocolFactory;
+typedef SslProtocolFactoryTmpl<SslMuxSocket> SslMuxProtocolFactory;
+
+
// Static instance to initialise plugin
static struct SslPlugin : public Plugin {
SslServerOptions options;
@@ -87,24 +101,48 @@ static struct SslPlugin : public Plugin {
~SslPlugin() { ssl::shutdownNSS(); }
- void earlyInitialize(Target&) {
+ void earlyInitialize(Target& target) {
+ broker::Broker* broker = dynamic_cast<broker::Broker*>(&target);
+ if (broker && !options.certDbPath.empty()) {
+ const broker::Broker::Options& opts = broker->getOptions();
+
+ if (opts.port == options.port && // AMQP & AMQPS ports are the same
+ opts.port != 0) {
+ // The presence of this option is used to signal to the TCP
+ // plugin not to start listening on the shared port. The actual
+ // value cannot be configured through the command line or config
+ // file (other than by setting the ports to the same value)
+ // because we are only adding it after option parsing.
+ options.multiplex = true;
+ options.addOptions()("ssl-multiplex", optValue(options.multiplex), "Allow SSL and non-SSL connections on the same port");
+ }
+ }
}
void initialize(Target& target) {
+ QPID_LOG(trace, "Initialising SSL plugin");
broker::Broker* broker = dynamic_cast<broker::Broker*>(&target);
// Only provide to a Broker
if (broker) {
if (options.certDbPath.empty()) {
- QPID_LOG(info, "SSL plugin not enabled, you must set --ssl-cert-db to enable it.");
+ QPID_LOG(notice, "SSL plugin not enabled, you must set --ssl-cert-db to enable it.");
} else {
try {
ssl::initNSS(options, true);
const broker::Broker::Options& opts = broker->getOptions();
- ProtocolFactory::shared_ptr protocol(new SslProtocolFactory(options,
- opts.connectionBacklog,
- opts.tcpNoDelay));
- QPID_LOG(notice, "Listening for SSL connections on TCP port " << protocol->getPort());
+
+ ProtocolFactory::shared_ptr protocol(options.multiplex ?
+ static_cast<ProtocolFactory*>(new SslMuxProtocolFactory(options,
+ opts.connectionBacklog,
+ opts.tcpNoDelay)) :
+ static_cast<ProtocolFactory*>(new SslProtocolFactory(options,
+ opts.connectionBacklog,
+ opts.tcpNoDelay)));
+ QPID_LOG(notice, "Listening for " <<
+ (options.multiplex ? "SSL or TCP" : "SSL") <<
+ " connections on TCP port " <<
+ protocol->getPort());
broker->registerProtocolFactory("ssl", protocol);
} catch (const std::exception& e) {
QPID_LOG(error, "Failed to initialise SSL plugin: " << e.what());
@@ -114,13 +152,15 @@ static struct SslPlugin : public Plugin {
}
} sslPlugin;
-SslProtocolFactory::SslProtocolFactory(const SslServerOptions& options, int backlog, bool nodelay) :
+template <class T>
+SslProtocolFactoryTmpl<T>::SslProtocolFactoryTmpl(const SslServerOptions& options, int backlog, bool nodelay) :
tcpNoDelay(nodelay), listeningPort(listener.listen(options.port, backlog, options.certName, options.clientAuth)),
nodict(options.nodict)
{}
-void SslProtocolFactory::established(Poller::shared_ptr poller, const qpid::sys::ssl::SslSocket& s,
- ConnectionCodec::Factory* f, bool isClient) {
+void SslEstablished(Poller::shared_ptr poller, const qpid::sys::SslSocket& s,
+ ConnectionCodec::Factory* f, bool isClient,
+ bool tcpNoDelay, bool nodict) {
qpid::sys::ssl::SslHandler* async = new qpid::sys::ssl::SslHandler(s.getFullAddress(), f, nodict);
if (tcpNoDelay) {
@@ -128,8 +168,10 @@ void SslProtocolFactory::established(Poller::shared_ptr poller, const qpid::sys:
QPID_LOG(info, "Set TCP_NODELAY on connection to " << s.getPeerAddress());
}
- if (isClient)
+ if (isClient) {
async->setClient();
+ }
+
qpid::sys::ssl::SslIO* aio = new qpid::sys::ssl::SslIO(s,
boost::bind(&qpid::sys::ssl::SslHandler::readbuff, async, _1, _2),
boost::bind(&qpid::sys::ssl::SslHandler::eof, async, _1),
@@ -142,25 +184,66 @@ void SslProtocolFactory::established(Poller::shared_ptr poller, const qpid::sys:
aio->start(poller);
}
-uint16_t SslProtocolFactory::getPort() const {
- return listeningPort; // Immutable no need for lock.
+template <>
+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);
}
-std::string SslProtocolFactory::getHost() const {
- return listener.getSockname();
+template <class T>
+uint16_t SslProtocolFactoryTmpl<T>::getPort() const {
+ return listeningPort; // Immutable no need for lock.
}
-void SslProtocolFactory::accept(Poller::shared_ptr poller,
- ConnectionCodec::Factory* fact) {
+template <class T>
+void SslProtocolFactoryTmpl<T>::accept(Poller::shared_ptr poller,
+ ConnectionCodec::Factory* fact) {
acceptor.reset(
- new qpid::sys::ssl::SslAcceptor(listener,
- boost::bind(&SslProtocolFactory::established, this, poller, _1, fact, false)));
+ new SslAcceptor(listener,
+ boost::bind(&SslProtocolFactoryTmpl<T>::established,
+ this, poller, _1, fact, false)));
acceptor->start(poller);
}
-void SslProtocolFactory::connect(
+template <>
+void SslMuxProtocolFactory::established(Poller::shared_ptr poller, const Socket& s,
+ ConnectionCodec::Factory* f, bool isClient) {
+ const SslSocket *sslSock = dynamic_cast<const SslSocket*>(&s);
+
+ if (sslSock) {
+ SslEstablished(poller, *sslSock, f, isClient, tcpNoDelay, nodict);
+ return;
+ }
+
+ AsynchIOHandler* async = new AsynchIOHandler(s.getFullAddress(), f);
+
+ if (tcpNoDelay) {
+ s.setTcpNoDelay();
+ QPID_LOG(info, "Set TCP_NODELAY on connection to " << s.getPeerAddress());
+ }
+
+ if (isClient) {
+ async->setClient();
+ }
+ AsynchIO* aio = AsynchIO::create
+ (s,
+ boost::bind(&AsynchIOHandler::readbuff, async, _1, _2),
+ boost::bind(&AsynchIOHandler::eof, async, _1),
+ boost::bind(&AsynchIOHandler::disconnect, async, _1),
+ boost::bind(&AsynchIOHandler::closedSocket, async, _1, _2),
+ boost::bind(&AsynchIOHandler::nobuffs, async, _1),
+ boost::bind(&AsynchIOHandler::idle, async, _1));
+
+ async->init(aio, 4);
+ aio->start(poller);
+}
+
+template <class T>
+void SslProtocolFactoryTmpl<T>::connect(
Poller::shared_ptr poller,
- const std::string& host, int16_t port,
+ const std::string& host, const std::string& port,
ConnectionCodec::Factory* fact,
ConnectFailedCallback failed)
{
@@ -171,9 +254,9 @@ void SslProtocolFactory::connect(
// is no longer needed.
qpid::sys::ssl::SslSocket* socket = new qpid::sys::ssl::SslSocket();
- new qpid::sys::ssl::SslConnector (*socket, poller, host, port,
- boost::bind(&SslProtocolFactory::established, this, poller, _1, fact, true),
- failed);
+ new SslConnector(*socket, poller, host, port,
+ boost::bind(&SslProtocolFactoryTmpl<T>::established, this, poller, _1, fact, true),
+ failed);
}
namespace
@@ -181,6 +264,7 @@ namespace
const std::string SSL = "ssl";
}
+template <>
bool SslProtocolFactory::supports(const std::string& capability)
{
std::string s = capability;
@@ -188,4 +272,12 @@ bool SslProtocolFactory::supports(const std::string& capability)
return s == SSL;
}
+template <>
+bool SslMuxProtocolFactory::supports(const std::string& capability)
+{
+ std::string s = capability;
+ transform(s.begin(), s.end(), s.begin(), tolower);
+ return s == SSL || s == "tcp";
+}
+
}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/StateMonitor.h b/cpp/src/qpid/sys/StateMonitor.h
index 5a92756f3a..eac37a8543 100644
--- a/cpp/src/qpid/sys/StateMonitor.h
+++ b/cpp/src/qpid/sys/StateMonitor.h
@@ -41,9 +41,9 @@ class StateMonitor : public Waitable
struct Set : public std::bitset<MaxEnum + 1> {
Set() {}
Set(Enum s) { set(s); }
- Set(Enum s, Enum t) { set(s).set(t); }
- Set(Enum s, Enum t, Enum u) { set(s).set(t).set(u); }
- Set(Enum s, Enum t, Enum u, Enum v) { set(s).set(t).set(u).set(v); }
+ Set(Enum s, Enum t) { std::bitset<MaxEnum + 1>::set(s).set(t); }
+ Set(Enum s, Enum t, Enum u) { std::bitset<MaxEnum + 1>::set(s).set(t).set(u); }
+ Set(Enum s, Enum t, Enum u, Enum v) { std::bitset<MaxEnum + 1>::set(s).set(t).set(u).set(v); }
};
@@ -60,13 +60,13 @@ class StateMonitor : public Waitable
operator Enum() const { return state; }
/** @pre Caller holds a ScopedLock */
- void waitFor(Enum s) { ScopedWait(*this); while (s != state) wait(); }
+ void waitFor(Enum s) { ScopedWait w(*this); while (s != state) wait(); }
/** @pre Caller holds a ScopedLock */
- void waitFor(Set s) { ScopedWait(*this); while (!s.test(state)) wait(); }
+ void waitFor(Set s) { ScopedWait w(*this); while (!s.test(state)) wait(); }
/** @pre Caller holds a ScopedLock */
- void waitNot(Enum s) { ScopedWait(*this); while (s == state) wait(); }
+ void waitNot(Enum s) { ScopedWait w(*this); while (s == state) wait(); }
/** @pre Caller holds a ScopedLock */
- void waitNot(Set s) { ScopedWait(*this); while (s.test(state)) wait(); }
+ void waitNot(Set s) { ScopedWait w(*this); while (s.test(state)) wait(); }
private:
Enum state;
diff --git a/cpp/src/qpid/sys/TCPIOPlugin.cpp b/cpp/src/qpid/sys/TCPIOPlugin.cpp
index a6528f9ad9..8a99d8db71 100644
--- a/cpp/src/qpid/sys/TCPIOPlugin.cpp
+++ b/cpp/src/qpid/sys/TCPIOPlugin.cpp
@@ -25,31 +25,31 @@
#include "qpid/Plugin.h"
#include "qpid/sys/Socket.h"
+#include "qpid/sys/SocketAddress.h"
#include "qpid/sys/Poller.h"
#include "qpid/broker/Broker.h"
#include "qpid/log/Statement.h"
#include <boost/bind.hpp>
-#include <memory>
+#include <boost/ptr_container/ptr_vector.hpp>
namespace qpid {
namespace sys {
class AsynchIOProtocolFactory : public ProtocolFactory {
const bool tcpNoDelay;
- Socket listener;
- const uint16_t listeningPort;
- std::auto_ptr<AsynchAcceptor> acceptor;
+ boost::ptr_vector<Socket> listeners;
+ boost::ptr_vector<AsynchAcceptor> acceptors;
+ uint16_t listeningPort;
public:
- AsynchIOProtocolFactory(int16_t port, int backlog, bool nodelay);
+ AsynchIOProtocolFactory(const std::string& host, const std::string& port, int backlog, bool nodelay, bool shouldListen);
void accept(Poller::shared_ptr, ConnectionCodec::Factory*);
- void connect(Poller::shared_ptr, const std::string& host, int16_t port,
+ void connect(Poller::shared_ptr, const std::string& host, const std::string& port,
ConnectionCodec::Factory*,
ConnectFailedCallback);
uint16_t getPort() const;
- std::string getHost() const;
private:
void established(Poller::shared_ptr, const Socket&, ConnectionCodec::Factory*,
@@ -57,27 +57,78 @@ class AsynchIOProtocolFactory : public ProtocolFactory {
void connectFailed(const Socket&, int, const std::string&, ConnectFailedCallback);
};
+static bool sslMultiplexEnabled(void)
+{
+ Options o;
+ Plugin::addOptions(o);
+
+ if (o.find_nothrow("ssl-multiplex", false)) {
+ // This option is added by the SSL plugin when the SSL port
+ // is configured to be the same as the main port.
+ QPID_LOG(notice, "SSL multiplexing enabled");
+ return true;
+ }
+ return false;
+}
+
// Static instance to initialise plugin
static class TCPIOPlugin : public Plugin {
void earlyInitialize(Target&) {
}
-
+
void initialize(Target& target) {
broker::Broker* broker = dynamic_cast<broker::Broker*>(&target);
// Only provide to a Broker
if (broker) {
const broker::Broker::Options& opts = broker->getOptions();
- ProtocolFactory::shared_ptr protocol(new AsynchIOProtocolFactory(opts.port, opts.connectionBacklog,
- opts.tcpNoDelay));
- QPID_LOG(notice, "Listening on TCP port " << protocol->getPort());
- broker->registerProtocolFactory("tcp", protocol);
+
+ // Check for SSL on the same port
+ bool shouldListen = !sslMultiplexEnabled();
+
+ ProtocolFactory::shared_ptr protocolt(
+ new AsynchIOProtocolFactory(
+ "", boost::lexical_cast<std::string>(opts.port),
+ opts.connectionBacklog,
+ opts.tcpNoDelay,
+ shouldListen));
+ if (shouldListen) {
+ QPID_LOG(notice, "Listening on TCP/TCP6 port " << protocolt->getPort());
+ }
+ broker->registerProtocolFactory("tcp", protocolt);
}
}
} tcpPlugin;
-AsynchIOProtocolFactory::AsynchIOProtocolFactory(int16_t port, int backlog, bool nodelay) :
- tcpNoDelay(nodelay), listeningPort(listener.listen(port, backlog))
-{}
+AsynchIOProtocolFactory::AsynchIOProtocolFactory(const std::string& host, const std::string& port, int backlog, bool nodelay, bool shouldListen) :
+ tcpNoDelay(nodelay)
+{
+ if (!shouldListen) {
+ return;
+ }
+
+ SocketAddress sa(host, port);
+
+ // We must have at least one resolved address
+ QPID_LOG(info, "Listening to: " << sa.asString())
+ Socket* s = new Socket;
+ uint16_t lport = s->listen(sa, backlog);
+ QPID_LOG(debug, "Listened to: " << lport);
+ listeners.push_back(s);
+
+ listeningPort = lport;
+
+ // Try any other resolved addresses
+ while (sa.nextAddress()) {
+ // Hack to ensure that all listening connections are on the same port
+ sa.setAddrInfoPort(listeningPort);
+ QPID_LOG(info, "Listening to: " << sa.asString())
+ Socket* s = new Socket;
+ uint16_t lport = s->listen(sa, backlog);
+ QPID_LOG(debug, "Listened to: " << lport);
+ listeners.push_back(s);
+ }
+
+}
void AsynchIOProtocolFactory::established(Poller::shared_ptr poller, const Socket& s,
ConnectionCodec::Factory* f, bool isClient) {
@@ -107,16 +158,14 @@ uint16_t AsynchIOProtocolFactory::getPort() const {
return listeningPort; // Immutable no need for lock.
}
-std::string AsynchIOProtocolFactory::getHost() const {
- return listener.getSockname();
-}
-
void AsynchIOProtocolFactory::accept(Poller::shared_ptr poller,
ConnectionCodec::Factory* fact) {
- acceptor.reset(
- AsynchAcceptor::create(listener,
- boost::bind(&AsynchIOProtocolFactory::established, this, poller, _1, fact, false)));
- acceptor->start(poller);
+ for (unsigned i = 0; i<listeners.size(); ++i) {
+ acceptors.push_back(
+ AsynchAcceptor::create(listeners[i],
+ boost::bind(&AsynchIOProtocolFactory::established, this, poller, _1, fact, false)));
+ acceptors[i].start(poller);
+ }
}
void AsynchIOProtocolFactory::connectFailed(
@@ -130,7 +179,7 @@ void AsynchIOProtocolFactory::connectFailed(
void AsynchIOProtocolFactory::connect(
Poller::shared_ptr poller,
- const std::string& host, int16_t port,
+ const std::string& host, const std::string& port,
ConnectionCodec::Factory* fact,
ConnectFailedCallback failed)
{
@@ -139,8 +188,8 @@ void AsynchIOProtocolFactory::connect(
// upon connection failure or by the AsynchIO upon connection
// shutdown. The allocated AsynchConnector frees itself when it
// is no longer needed.
-
Socket* socket = new Socket();
+ try {
AsynchConnector* c = AsynchConnector::create(
*socket,
host,
@@ -150,6 +199,12 @@ void AsynchIOProtocolFactory::connect(
boost::bind(&AsynchIOProtocolFactory::connectFailed,
this, _1, _2, _3, failed));
c->start(poller);
+ } catch (std::exception&) {
+ // TODO: Design question - should we do the error callback and also throw?
+ int errCode = socket->getError();
+ connectFailed(*socket, errCode, strError(errCode), failed);
+ throw;
+ }
}
}} // namespace qpid::sys
diff --git a/cpp/src/qpid/sys/Timer.cpp b/cpp/src/qpid/sys/Timer.cpp
index a97ccd1bd1..47752e4584 100644
--- a/cpp/src/qpid/sys/Timer.cpp
+++ b/cpp/src/qpid/sys/Timer.cpp
@@ -75,6 +75,12 @@ void TimerTask::cancel() {
cancelled = true;
}
+void TimerTask::setFired() {
+ // Set nextFireTime to just before now, making readyToFire() true.
+ nextFireTime = AbsTime(sys::now(), Duration(-1));
+}
+
+
Timer::Timer() :
active(false),
late(50 * TIME_MSEC),
@@ -131,12 +137,14 @@ void Timer::run()
bool warningsEnabled;
QPID_LOG_TEST(warning, warningsEnabled);
if (warningsEnabled) {
- if (delay > late && overrun > overran)
- warn.lateAndOverran(t->name, delay, overrun, Duration(start, end));
+ if (overrun > overran) {
+ if (delay > overran) // if delay is significant to an overrun.
+ warn.lateAndOverran(t->name, delay, overrun, Duration(start, end));
+ else
+ warn.overran(t->name, overrun, Duration(start, end));
+ }
else if (delay > late)
warn.late(t->name, delay);
- else if (overrun > overran)
- warn.overran(t->name, overrun, Duration(start, end));
}
continue;
} else {
@@ -183,7 +191,11 @@ void Timer::stop()
// Allow subclasses to override behavior when firing a task.
void Timer::fire(boost::intrusive_ptr<TimerTask> t) {
- t->fireTask();
+ try {
+ t->fireTask();
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Exception thrown by timer task " << t->getName() << ": " << e.what());
+ }
}
// Provided for subclasses: called when a task is droped.
diff --git a/cpp/src/qpid/sys/Timer.h b/cpp/src/qpid/sys/Timer.h
index 98ba39ce38..fccb17dbc2 100644
--- a/cpp/src/qpid/sys/Timer.h
+++ b/cpp/src/qpid/sys/Timer.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
@@ -64,6 +64,10 @@ class TimerTask : public RefCounted {
std::string getName() const { return name; }
+ // Move the nextFireTime so readyToFire is true.
+ // Used by the cluster, where tasks are fired on cluster events, not on local time.
+ QPID_COMMON_EXTERN void setFired();
+
protected:
// Must be overridden with callback
virtual void fire() = 0;
diff --git a/cpp/src/qpid/sys/TimerWarnings.cpp b/cpp/src/qpid/sys/TimerWarnings.cpp
index 48a56eb472..85e26da54a 100644
--- a/cpp/src/qpid/sys/TimerWarnings.cpp
+++ b/cpp/src/qpid/sys/TimerWarnings.cpp
@@ -56,20 +56,22 @@ void TimerWarnings::log() {
std::string task = i->first;
TaskStats& stats = i->second;
if (stats.lateDelay.count)
- QPID_LOG(warning, task << " task late "
+ QPID_LOG(info, task << " task late "
<< stats.lateDelay.count << " times by "
<< stats.lateDelay.average()/TIME_MSEC << "ms on average.");
+
if (stats.overranOverrun.count)
- QPID_LOG(warning, task << " task overran "
+ QPID_LOG(info, task << " task overran "
<< stats.overranOverrun.count << " times by "
<< stats.overranOverrun.average()/TIME_MSEC << "ms (taking "
<< stats.overranTime.average() << "ns) on average.");
- if (stats.lateAndOverranDelay.count)
- QPID_LOG(warning, task << " task overran "
- << stats.overranOverrun.count << " times by "
- << stats.overranOverrun.average()/TIME_MSEC << "ms (taking "
- << stats.overranTime.average() << "ns) on average.");
+ if (stats.lateAndOverranOverrun.count)
+ QPID_LOG(info, task << " task late and overran "
+ << stats.lateAndOverranOverrun.count << " times: late "
+ << stats.lateAndOverranDelay.average()/TIME_MSEC << "ms, overran "
+ << stats.lateAndOverranOverrun.average()/TIME_MSEC << "ms (taking "
+ << stats.lateAndOverranTime.average() << "ns) on average.");
}
nextReport = AbsTime(now(), interval);
diff --git a/cpp/src/qpid/sys/alloca.h b/cpp/src/qpid/sys/alloca.h
index e989670e4f..b3f59b7c3f 100644
--- a/cpp/src/qpid/sys/alloca.h
+++ b/cpp/src/qpid/sys/alloca.h
@@ -21,19 +21,22 @@
*
*/
-#if (defined(_WINDOWS) || defined (WIN32)) && defined(_MSC_VER)
-#include <malloc.h>
-#ifdef alloc
-# undef alloc
-#endif
-#define alloc _alloc
-#ifdef alloca
-# undef alloca
-#endif
-#define alloca _alloca
+#if (defined(_WINDOWS) || defined (WIN32))
+# include <malloc.h>
+
+# if defined(_MSC_VER)
+# ifdef alloc
+# undef alloc
+# endif
+# define alloc _alloc
+# ifdef alloca
+# undef alloca
+# endif
+# define alloca _alloca
+# endif
#endif
#if !defined _WINDOWS && !defined WIN32
-#include <alloca.h>
+# include <alloca.h>
#endif
#endif /*!QPID_SYS_ALLOCA_H*/
diff --git a/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp b/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp
index 454ce62495..249b769051 100644
--- a/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp
+++ b/cpp/src/qpid/sys/cyrus/CyrusSecurityLayer.cpp
@@ -57,6 +57,7 @@ size_t CyrusSecurityLayer::decode(const char* input, size_t size)
copied += count;
decodeBuffer.position += count;
size_t decodedSize = codec->decode(decodeBuffer.data, decodeBuffer.position);
+ if (decodedSize == 0) break;
if (decodedSize < decodeBuffer.position) {
::memmove(decodeBuffer.data, decodeBuffer.data + decodedSize, decodeBuffer.position - decodedSize);
}
@@ -106,7 +107,7 @@ size_t CyrusSecurityLayer::encode(const char* buffer, size_t size)
bool CyrusSecurityLayer::canEncode()
{
- return encrypted || codec->canEncode();
+ return codec && (encrypted || codec->canEncode());
}
void CyrusSecurityLayer::init(qpid::sys::Codec* c)
diff --git a/cpp/src/qpid/sys/epoll/EpollPoller.cpp b/cpp/src/qpid/sys/epoll/EpollPoller.cpp
index 9ad05c71a3..dcc9d9181c 100644
--- a/cpp/src/qpid/sys/epoll/EpollPoller.cpp
+++ b/cpp/src/qpid/sys/epoll/EpollPoller.cpp
@@ -384,7 +384,12 @@ void PollerPrivate::resetMode(PollerHandlePrivate& eh) {
epe.data.u64 = 0; // Keep valgrind happy
epe.data.ptr = &eh;
- QPID_POSIX_CHECK(::epoll_ctl(epollFd, EPOLL_CTL_MOD, eh.fd(), &epe));
+ int rc = ::epoll_ctl(epollFd, EPOLL_CTL_MOD, eh.fd(), &epe);
+ // If something has closed the fd in the meantime try adding it back
+ if (rc ==-1 && errno == ENOENT) {
+ rc = ::epoll_ctl(epollFd, EPOLL_CTL_ADD, eh.fd(), &epe);
+ }
+ QPID_POSIX_CHECK(rc);
eh.setActive();
return;
diff --git a/cpp/src/qpid/sys/posix/AsynchIO.cpp b/cpp/src/qpid/sys/posix/AsynchIO.cpp
index 119a6aa8a4..dab8bd09c6 100644
--- a/cpp/src/qpid/sys/posix/AsynchIO.cpp
+++ b/cpp/src/qpid/sys/posix/AsynchIO.cpp
@@ -149,11 +149,12 @@ private:
ConnectedCallback connCallback;
FailedCallback failCallback;
const Socket& socket;
+ SocketAddress sa;
public:
AsynchConnector(const Socket& socket,
- std::string hostname,
- uint16_t port,
+ const std::string& hostname,
+ const std::string& port,
ConnectedCallback connCb,
FailedCallback failCb);
void start(Poller::shared_ptr poller);
@@ -161,8 +162,8 @@ public:
};
AsynchConnector::AsynchConnector(const Socket& s,
- std::string hostname,
- uint16_t port,
+ const std::string& hostname,
+ const std::string& port,
ConnectedCallback connCb,
FailedCallback failCb) :
DispatchHandle(s,
@@ -171,11 +172,13 @@ AsynchConnector::AsynchConnector(const Socket& s,
boost::bind(&AsynchConnector::connComplete, this, _1)),
connCallback(connCb),
failCallback(failCb),
- socket(s)
+ socket(s),
+ sa(hostname, port)
{
socket.setNonblocking();
- SocketAddress sa(hostname, boost::lexical_cast<std::string>(port));
+
// Note, not catching any exceptions here, also has effect of destructing
+ QPID_LOG(info, "Connecting: " << sa.asString());
socket.connect(sa);
}
@@ -191,11 +194,26 @@ void AsynchConnector::stop()
void AsynchConnector::connComplete(DispatchHandle& h)
{
- h.stopWatch();
int errCode = socket.getError();
if (errCode == 0) {
+ h.stopWatch();
connCallback(socket);
} else {
+ // Retry while we cause an immediate exception
+ // (asynch failure will be handled by re-entering here at the top)
+ while (sa.nextAddress()) {
+ try {
+ // Try next address without deleting ourselves
+ QPID_LOG(debug, "Ignored socket connect error: " << strError(errCode));
+ QPID_LOG(info, "Retrying connect: " << sa.asString());
+ socket.connect(sa);
+ return;
+ } catch (const std::exception& e) {
+ QPID_LOG(debug, "Ignored socket connect exception: " << e.what());
+ }
+ errCode = socket.getError();
+ }
+ h.stopWatch();
failCallback(socket, errCode, strError(errCode));
}
DispatchHandle::doDelete();
@@ -589,8 +607,8 @@ AsynchAcceptor* AsynchAcceptor::create(const Socket& s,
}
AsynchConnector* AsynchConnector::create(const Socket& s,
- std::string hostname,
- uint16_t port,
+ const std::string& hostname,
+ const std::string& port,
ConnectedCallback connCb,
FailedCallback failCb)
{
diff --git a/cpp/src/qpid/sys/posix/LockFile.cpp b/cpp/src/qpid/sys/posix/LockFile.cpp
index 1862ff6ac9..f5a6c292cb 100755
--- a/cpp/src/qpid/sys/posix/LockFile.cpp
+++ b/cpp/src/qpid/sys/posix/LockFile.cpp
@@ -58,8 +58,7 @@ LockFile::~LockFile() {
if (impl) {
int f = impl->fd;
if (f >= 0) {
- int unused_ret;
- unused_ret = ::lockf(f, F_ULOCK, 0); // Suppress warnings about ignoring return value.
+ (void) ::lockf(f, F_ULOCK, 0); // Suppress warnings about ignoring return value.
::close(f);
impl->fd = -1;
}
diff --git a/cpp/src/qpid/sys/posix/Socket.cpp b/cpp/src/qpid/sys/posix/Socket.cpp
index 7b906f33e8..4a6dc66f80 100644
--- a/cpp/src/qpid/sys/posix/Socket.cpp
+++ b/cpp/src/qpid/sys/posix/Socket.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
@@ -34,65 +34,35 @@
#include <netdb.h>
#include <cstdlib>
#include <string.h>
-#include <iostream>
-
-#include <boost/format.hpp>
-#include <boost/lexical_cast.hpp>
namespace qpid {
namespace sys {
namespace {
-std::string getName(int fd, bool local, bool includeService = false)
+std::string getName(int fd, bool local)
{
- ::sockaddr_storage name; // big enough for any socket address
- ::socklen_t namelen = sizeof(name);
-
- int result = -1;
+ ::sockaddr_storage name_s; // big enough for any socket address
+ ::sockaddr* name = (::sockaddr*)&name_s;
+ ::socklen_t namelen = sizeof(name_s);
+
if (local) {
- result = ::getsockname(fd, (::sockaddr*)&name, &namelen);
+ QPID_POSIX_CHECK( ::getsockname(fd, name, &namelen) );
} else {
- result = ::getpeername(fd, (::sockaddr*)&name, &namelen);
+ QPID_POSIX_CHECK( ::getpeername(fd, name, &namelen) );
}
- QPID_POSIX_CHECK(result);
-
- char servName[NI_MAXSERV];
- char dispName[NI_MAXHOST];
- if (includeService) {
- if (int rc=::getnameinfo((::sockaddr*)&name, namelen, dispName, sizeof(dispName),
- servName, sizeof(servName),
- NI_NUMERICHOST | NI_NUMERICSERV) != 0)
- throw QPID_POSIX_ERROR(rc);
- return std::string(dispName) + ":" + std::string(servName);
-
- } else {
- if (int rc=::getnameinfo((::sockaddr*)&name, namelen, dispName, sizeof(dispName), 0, 0, NI_NUMERICHOST) != 0)
- throw QPID_POSIX_ERROR(rc);
- return dispName;
- }
+ return SocketAddress::asString(name, namelen);
}
-std::string getService(int fd, bool local)
+uint16_t getLocalPort(int fd)
{
- ::sockaddr_storage name; // big enough for any socket address
- ::socklen_t namelen = sizeof(name);
-
- int result = -1;
- if (local) {
- result = ::getsockname(fd, (::sockaddr*)&name, &namelen);
- } else {
- result = ::getpeername(fd, (::sockaddr*)&name, &namelen);
- }
+ ::sockaddr_storage name_s; // big enough for any socket address
+ ::sockaddr* name = (::sockaddr*)&name_s;
+ ::socklen_t namelen = sizeof(name_s);
- QPID_POSIX_CHECK(result);
+ QPID_POSIX_CHECK( ::getsockname(fd, name, &namelen) );
- char servName[NI_MAXSERV];
- if (int rc=::getnameinfo((::sockaddr*)&name, namelen, 0, 0,
- servName, sizeof(servName),
- NI_NUMERICHOST | NI_NUMERICSERV) != 0)
- throw QPID_POSIX_ERROR(rc);
- return servName;
+ return SocketAddress::getPort(name);
}
}
@@ -119,6 +89,11 @@ void Socket::createSocket(const SocketAddress& sa) const
try {
if (nonblocking) setNonblocking();
if (nodelay) setTcpNoDelay();
+ if (getAddrInfo(sa).ai_family == AF_INET6) {
+ int flag = 1;
+ int result = ::setsockopt(socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&flag, sizeof(flag));
+ QPID_POSIX_CHECK(result);
+ }
} catch (std::exception&) {
::close(s);
socket = -1;
@@ -126,13 +101,18 @@ void Socket::createSocket(const SocketAddress& sa) const
}
}
-void Socket::setTimeout(const Duration& interval) const
-{
- const int& socket = impl->fd;
- struct timeval tv;
- toTimeval(tv, interval);
- setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
- setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+Socket* Socket::createSameTypeSocket() const {
+ int& socket = impl->fd;
+ // Socket currently has no actual socket attached
+ if (socket == -1)
+ return new Socket;
+
+ ::sockaddr_storage sa;
+ ::socklen_t salen = sizeof(sa);
+ QPID_POSIX_CHECK(::getsockname(socket, (::sockaddr*)&sa, &salen));
+ int s = ::socket(sa.ss_family, SOCK_STREAM, 0); // Currently only work with SOCK_STREAM
+ if (s < 0) throw QPID_POSIX_ERROR(errno);
+ return new Socket(new IOHandlePrivate(s));
}
void Socket::setNonblocking() const {
@@ -149,20 +129,27 @@ void Socket::setTcpNoDelay() const
nodelay = true;
if (socket != -1) {
int flag = 1;
- int result = setsockopt(impl->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag));
+ int result = ::setsockopt(impl->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag));
QPID_POSIX_CHECK(result);
}
}
-void Socket::connect(const std::string& host, uint16_t port) const
+void Socket::connect(const std::string& host, const std::string& port) const
{
- SocketAddress sa(host, boost::lexical_cast<std::string>(port));
+ SocketAddress sa(host, port);
connect(sa);
}
void Socket::connect(const SocketAddress& addr) const
{
- connectname = addr.asString();
+ // The display name for an outbound connection needs to be the name that was specified
+ // for the address rather than a resolved IP address as we don't know which of
+ // the IP addresses is actually the one that will be connected to.
+ peername = addr.asString(false);
+
+ // However the string we compare with the local port must be numeric or it might not
+ // match when it should as getLocalAddress() will always be numeric
+ std::string connectname = addr.asString();
createSocket(addr);
@@ -170,7 +157,24 @@ void Socket::connect(const SocketAddress& addr) const
// TODO the correct thing to do here is loop on failure until you've used all the returned addresses
if ((::connect(socket, getAddrInfo(addr).ai_addr, getAddrInfo(addr).ai_addrlen) < 0) &&
(errno != EINPROGRESS)) {
- throw Exception(QPID_MSG(strError(errno) << ": " << connectname));
+ throw Exception(QPID_MSG(strError(errno) << ": " << peername));
+ }
+ // When connecting to a port on the same host which no longer has
+ // a process associated with it, the OS occasionally chooses the
+ // remote port (which is unoccupied) as the port to bind the local
+ // end of the socket, resulting in a "circular" connection.
+ //
+ // This seems like something the OS should prevent but I have
+ // confirmed that sporadic hangs in
+ // cluster_tests.LongTests.test_failover on RHEL5 are caused by
+ // such a circular connection.
+ //
+ // Raise an error if we see such a connection, since we know there is
+ // no listener on the peer address.
+ //
+ if (getLocalAddress() == connectname) {
+ close();
+ throw Exception(QPID_MSG("Connection refused: " << peername));
}
}
@@ -183,9 +187,9 @@ Socket::close() const
socket = -1;
}
-int Socket::listen(uint16_t port, int backlog) const
+int Socket::listen(const std::string& host, const std::string& port, int backlog) const
{
- SocketAddress sa("", boost::lexical_cast<std::string>(port));
+ SocketAddress sa(host, port);
return listen(sa, backlog);
}
@@ -195,26 +199,24 @@ int Socket::listen(const SocketAddress& sa, int backlog) const
const int& socket = impl->fd;
int yes=1;
- QPID_POSIX_CHECK(setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)));
+ QPID_POSIX_CHECK(::setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes)));
if (::bind(socket, getAddrInfo(sa).ai_addr, getAddrInfo(sa).ai_addrlen) < 0)
throw Exception(QPID_MSG("Can't bind to port " << sa.asString() << ": " << strError(errno)));
if (::listen(socket, backlog) < 0)
throw Exception(QPID_MSG("Can't listen on port " << sa.asString() << ": " << strError(errno)));
- struct sockaddr_in name;
- socklen_t namelen = sizeof(name);
- if (::getsockname(socket, (struct sockaddr*)&name, &namelen) < 0)
- throw QPID_POSIX_ERROR(errno);
-
- return ntohs(name.sin_port);
+ return getLocalPort(socket);
}
Socket* Socket::accept() const
{
int afd = ::accept(impl->fd, 0, 0);
- if ( afd >= 0)
- return new Socket(new IOHandlePrivate(afd));
+ if ( afd >= 0) {
+ Socket* s = new Socket(new IOHandlePrivate(afd));
+ s->localname = localname;
+ return s;
+ }
else if (errno == EAGAIN)
return 0;
else throw QPID_POSIX_ERROR(errno);
@@ -230,37 +232,20 @@ int Socket::write(const void *buf, size_t count) const
return ::write(impl->fd, buf, count);
}
-std::string Socket::getSockname() const
-{
- return getName(impl->fd, true);
-}
-
-std::string Socket::getPeername() const
-{
- return getName(impl->fd, false);
-}
-
std::string Socket::getPeerAddress() const
{
- if (connectname.empty()) {
- connectname = getName(impl->fd, false, true);
+ if (peername.empty()) {
+ peername = getName(impl->fd, false);
}
- return connectname;
+ return peername;
}
std::string Socket::getLocalAddress() const
{
- return getName(impl->fd, true, true);
-}
-
-uint16_t Socket::getLocalPort() const
-{
- return std::atoi(getService(impl->fd, true).c_str());
-}
-
-uint16_t Socket::getRemotePort() const
-{
- return std::atoi(getService(impl->fd, true).c_str());
+ if (localname.empty()) {
+ localname = getName(impl->fd, true);
+ }
+ return localname;
}
int Socket::getError() const
diff --git a/cpp/src/qpid/sys/posix/SocketAddress.cpp b/cpp/src/qpid/sys/posix/SocketAddress.cpp
index 8f5f29d793..077942ef2f 100644
--- a/cpp/src/qpid/sys/posix/SocketAddress.cpp
+++ b/cpp/src/qpid/sys/posix/SocketAddress.cpp
@@ -21,11 +21,13 @@
#include "qpid/sys/SocketAddress.h"
-#include "qpid/sys/posix/check.h"
+#include "qpid/Exception.h"
+#include "qpid/Msg.h"
#include <sys/socket.h>
-#include <string.h>
+#include <netinet/in.h>
#include <netdb.h>
+#include <string.h>
namespace qpid {
namespace sys {
@@ -46,15 +48,9 @@ SocketAddress::SocketAddress(const SocketAddress& sa) :
SocketAddress& SocketAddress::operator=(const SocketAddress& sa)
{
- if (&sa != this) {
- host = sa.host;
- port = sa.port;
+ SocketAddress temp(sa);
- if (addrInfo) {
- ::freeaddrinfo(addrInfo);
- addrInfo = 0;
- }
- }
+ std::swap(temp, *this);
return *this;
}
@@ -65,9 +61,61 @@ SocketAddress::~SocketAddress()
}
}
-std::string SocketAddress::asString() const
+std::string SocketAddress::asString(::sockaddr const * const addr, size_t addrlen)
+{
+ char servName[NI_MAXSERV];
+ char dispName[NI_MAXHOST];
+ if (int rc=::getnameinfo(addr, addrlen,
+ dispName, sizeof(dispName),
+ servName, sizeof(servName),
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0)
+ throw qpid::Exception(QPID_MSG(gai_strerror(rc)));
+ std::string s;
+ switch (addr->sa_family) {
+ case AF_INET: s += dispName; break;
+ case AF_INET6: s += "["; s += dispName; s+= "]"; break;
+ default: throw Exception(QPID_MSG("Unexpected socket type"));
+ }
+ s += ":";
+ s += servName;
+ return s;
+}
+
+uint16_t SocketAddress::getPort(::sockaddr const * const addr)
{
- return host + ":" + port;
+ switch (addr->sa_family) {
+ case AF_INET: return ntohs(((::sockaddr_in*)addr)->sin_port);
+ case AF_INET6: return ntohs(((::sockaddr_in6*)addr)->sin6_port);
+ default:throw Exception(QPID_MSG("Unexpected socket type"));
+ }
+}
+
+std::string SocketAddress::asString(bool numeric) const
+{
+ if (!numeric)
+ return host + ":" + port;
+ // Canonicalise into numeric id
+ const ::addrinfo& ai = getAddrInfo(*this);
+
+ return asString(ai.ai_addr, ai.ai_addrlen);
+}
+
+bool SocketAddress::nextAddress() {
+ bool r = currentAddrInfo->ai_next != 0;
+ if (r)
+ currentAddrInfo = currentAddrInfo->ai_next;
+ return r;
+}
+
+void SocketAddress::setAddrInfoPort(uint16_t port) {
+ if (!currentAddrInfo) return;
+
+ ::addrinfo& ai = *currentAddrInfo;
+ switch (ai.ai_family) {
+ case AF_INET: ((::sockaddr_in*)ai.ai_addr)->sin_port = htons(port); return;
+ case AF_INET6:((::sockaddr_in6*)ai.ai_addr)->sin6_port = htons(port); return;
+ default: throw Exception(QPID_MSG("Unexpected socket type"));
+ }
}
const ::addrinfo& getAddrInfo(const SocketAddress& sa)
@@ -75,7 +123,8 @@ const ::addrinfo& getAddrInfo(const SocketAddress& sa)
if (!sa.addrInfo) {
::addrinfo hints;
::memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_INET; // Change this to support IPv6
+ hints.ai_flags = AI_ADDRCONFIG; // Only use protocols that we have configured interfaces for
+ hints.ai_family = AF_UNSPEC; // Allow both IPv4 and IPv6
hints.ai_socktype = SOCK_STREAM;
const char* node = 0;
@@ -88,10 +137,11 @@ const ::addrinfo& getAddrInfo(const SocketAddress& sa)
int n = ::getaddrinfo(node, service, &hints, &sa.addrInfo);
if (n != 0)
- throw Exception(QPID_MSG("Cannot resolve " << sa.host << ": " << ::gai_strerror(n)));
+ throw Exception(QPID_MSG("Cannot resolve " << sa.asString(false) << ": " << ::gai_strerror(n)));
+ sa.currentAddrInfo = sa.addrInfo;
}
- return *sa.addrInfo;
+ return *sa.currentAddrInfo;
}
}}
diff --git a/cpp/src/qpid/sys/posix/Thread.cpp b/cpp/src/qpid/sys/posix/Thread.cpp
index b466733260..a1d6396763 100644
--- a/cpp/src/qpid/sys/posix/Thread.cpp
+++ b/cpp/src/qpid/sys/posix/Thread.cpp
@@ -37,7 +37,8 @@ void* runRunnable(void* p)
}
}
-struct ThreadPrivate {
+class ThreadPrivate {
+public:
pthread_t thread;
ThreadPrivate(Runnable* runnable) {
diff --git a/cpp/src/qpid/sys/posix/Time.cpp b/cpp/src/qpid/sys/posix/Time.cpp
index b3858279b4..9661f0c5e8 100644
--- a/cpp/src/qpid/sys/posix/Time.cpp
+++ b/cpp/src/qpid/sys/posix/Time.cpp
@@ -27,6 +27,7 @@
#include <stdio.h>
#include <sys/time.h>
#include <unistd.h>
+#include <iomanip>
namespace {
int64_t max_abstime() { return std::numeric_limits<int64_t>::max(); }
@@ -103,6 +104,12 @@ void outputFormattedNow(std::ostream& o) {
o << " ";
}
+void outputHiresNow(std::ostream& o) {
+ ::timespec time;
+ ::clock_gettime(CLOCK_REALTIME, &time);
+ o << time.tv_sec << "." << std::setw(9) << std::setfill('0') << time.tv_nsec << "s ";
+}
+
void sleep(int secs) {
::sleep(secs);
}
diff --git a/cpp/src/qpid/sys/rdma/RdmaIO.cpp b/cpp/src/qpid/sys/rdma/RdmaIO.cpp
index c80c94cba6..78bcdec68e 100644
--- a/cpp/src/qpid/sys/rdma/RdmaIO.cpp
+++ b/cpp/src/qpid/sys/rdma/RdmaIO.cpp
@@ -140,8 +140,8 @@ namespace Rdma {
// Prepost recv buffers before we go any further
qp->allocateRecvBuffers(recvBufferCount, bufferSize+FrameHeaderSize);
- // Create xmit buffers
- qp->createSendBuffers(xmitBufferCount, bufferSize+FrameHeaderSize);
+ // Create xmit buffers, reserve space for frame header.
+ qp->createSendBuffers(xmitBufferCount, bufferSize, FrameHeaderSize);
}
AsynchIO::~AsynchIO() {
@@ -210,12 +210,14 @@ namespace Rdma {
}
break;
case 1:
- Buffer* ob = buff ? buff : getSendBuffer();
+ if (!buff)
+ buff = getSendBuffer();
// Add FrameHeader after frame data
FrameHeader header(credit);
- ::memcpy(ob->bytes()+ob->dataCount(), &header, FrameHeaderSize);
- ob->dataCount(ob->dataCount()+FrameHeaderSize);
- qp->postSend(ob);
+ assert(buff->dataCount() <= buff->byteCount()); // ensure app data doesn't impinge on reserved space.
+ ::memcpy(buff->bytes()+buff->dataCount(), &header, FrameHeaderSize);
+ buff->dataCount(buff->dataCount()+FrameHeaderSize);
+ qp->postSend(buff);
break;
}
}
diff --git a/cpp/src/qpid/sys/rdma/rdma_wrap.cpp b/cpp/src/qpid/sys/rdma/rdma_wrap.cpp
index 6d38c42502..efe454c5be 100644
--- a/cpp/src/qpid/sys/rdma/rdma_wrap.cpp
+++ b/cpp/src/qpid/sys/rdma/rdma_wrap.cpp
@@ -50,8 +50,9 @@ namespace Rdma {
return count;
}
- Buffer::Buffer(uint32_t lkey, char* bytes, const int32_t byteCount) :
- bufferSize(byteCount)
+ Buffer::Buffer(uint32_t lkey, char* bytes, const int32_t byteCount,
+ const int32_t reserve) :
+ bufferSize(byteCount + reserve), reserved(reserve)
{
sge.addr = (uintptr_t) bytes;
sge.length = 0;
@@ -163,21 +164,21 @@ namespace Rdma {
}
// Create buffers to use for writing
- void QueuePair::createSendBuffers(int sendBufferCount, int bufferSize)
+ void QueuePair::createSendBuffers(int sendBufferCount, int bufferSize, int reserved)
{
assert(!smr);
// Round up buffersize to cacheline (64 bytes)
- bufferSize = (bufferSize+63) & (~63);
+ int dataLength = (bufferSize+reserved+63) & (~63);
// Allocate memory block for all receive buffers
- char* mem = new char [sendBufferCount * bufferSize];
- smr = regMr(pd.get(), mem, sendBufferCount * bufferSize, ::IBV_ACCESS_LOCAL_WRITE);
+ char* mem = new char [sendBufferCount * dataLength];
+ smr = regMr(pd.get(), mem, sendBufferCount * dataLength, ::IBV_ACCESS_LOCAL_WRITE);
sendBuffers.reserve(sendBufferCount);
freeBuffers.reserve(sendBufferCount);
for (int i = 0; i<sendBufferCount; ++i) {
// Allocate xmit buffer
- sendBuffers.push_back(Buffer(smr->lkey, &mem[i*bufferSize], bufferSize));
+ sendBuffers.push_back(Buffer(smr->lkey, &mem[i*dataLength], bufferSize, reserved));
freeBuffers.push_back(i);
}
}
diff --git a/cpp/src/qpid/sys/rdma/rdma_wrap.h b/cpp/src/qpid/sys/rdma/rdma_wrap.h
index 28bddd2165..8e3429027b 100644
--- a/cpp/src/qpid/sys/rdma/rdma_wrap.h
+++ b/cpp/src/qpid/sys/rdma/rdma_wrap.h
@@ -57,8 +57,9 @@ namespace Rdma {
void dataCount(int32_t);
private:
- Buffer(uint32_t lkey, char* bytes, const int32_t byteCount);
+ Buffer(uint32_t lkey, char* bytes, const int32_t byteCount, const int32_t reserve=0);
int32_t bufferSize;
+ int32_t reserved; // for framing header
::ibv_sge sge;
};
@@ -66,8 +67,9 @@ namespace Rdma {
return (char*) sge.addr;
}
+ /** return the number of bytes available for application data */
inline int32_t Buffer::byteCount() const {
- return bufferSize;
+ return bufferSize - reserved;
}
inline int32_t Buffer::dataCount() const {
@@ -75,6 +77,8 @@ namespace Rdma {
}
inline void Buffer::dataCount(int32_t s) {
+ // catch any attempt to overflow a buffer
+ assert(s <= bufferSize + reserved);
sge.length = s;
}
@@ -136,7 +140,7 @@ namespace Rdma {
typedef boost::intrusive_ptr<QueuePair> intrusive_ptr;
// Create a buffers to use for writing
- void createSendBuffers(int sendBufferCount, int bufferSize);
+ void createSendBuffers(int sendBufferCount, int dataSize, int headerSize);
// Get a send buffer
Buffer* getSendBuffer();
diff --git a/cpp/src/qpid/sys/ssl/SslHandler.h b/cpp/src/qpid/sys/ssl/SslHandler.h
index a340109966..400fa317fd 100644
--- a/cpp/src/qpid/sys/ssl/SslHandler.h
+++ b/cpp/src/qpid/sys/ssl/SslHandler.h
@@ -35,7 +35,7 @@ namespace sys {
namespace ssl {
class SslIO;
-class SslIOBufferBase;
+struct SslIOBufferBase;
class SslSocket;
class SslHandler : public OutputControl {
diff --git a/cpp/src/qpid/sys/ssl/SslIo.cpp b/cpp/src/qpid/sys/ssl/SslIo.cpp
index a58a137473..4a59819183 100644
--- a/cpp/src/qpid/sys/ssl/SslIo.cpp
+++ b/cpp/src/qpid/sys/ssl/SslIo.cpp
@@ -68,29 +68,33 @@ __thread int64_t threadMaxReadTimeNs = 2 * 1000000; // start at 2ms
* Asynch Acceptor
*/
-SslAcceptor::SslAcceptor(const SslSocket& s, Callback callback) :
+template <class T>
+SslAcceptorTmpl<T>::SslAcceptorTmpl(const T& s, Callback callback) :
acceptedCallback(callback),
- handle(s, boost::bind(&SslAcceptor::readable, this, _1), 0, 0),
+ handle(s, boost::bind(&SslAcceptorTmpl<T>::readable, this, _1), 0, 0),
socket(s) {
s.setNonblocking();
ignoreSigpipe();
}
-SslAcceptor::~SslAcceptor()
+template <class T>
+SslAcceptorTmpl<T>::~SslAcceptorTmpl()
{
handle.stopWatch();
}
-void SslAcceptor::start(Poller::shared_ptr poller) {
+template <class T>
+void SslAcceptorTmpl<T>::start(Poller::shared_ptr poller) {
handle.startWatch(poller);
}
/*
* We keep on accepting as long as there is something to accept
*/
-void SslAcceptor::readable(DispatchHandle& h) {
- SslSocket* s;
+template <class T>
+void SslAcceptorTmpl<T>::readable(DispatchHandle& h) {
+ Socket* s;
do {
errno = 0;
// TODO: Currently we ignore the peers address, perhaps we should
@@ -110,6 +114,10 @@ void SslAcceptor::readable(DispatchHandle& h) {
h.rewatch();
}
+// Explicitly instantiate the templates we need
+template class SslAcceptorTmpl<SslSocket>;
+template class SslAcceptorTmpl<SslMuxSocket>;
+
/*
* Asynch Connector
*/
@@ -117,7 +125,7 @@ void SslAcceptor::readable(DispatchHandle& h) {
SslConnector::SslConnector(const SslSocket& s,
Poller::shared_ptr poller,
std::string hostname,
- uint16_t port,
+ std::string port,
ConnectedCallback connCb,
FailedCallback failCb) :
DispatchHandle(s,
diff --git a/cpp/src/qpid/sys/ssl/SslIo.h b/cpp/src/qpid/sys/ssl/SslIo.h
index 53ac69d8d6..c980d73831 100644
--- a/cpp/src/qpid/sys/ssl/SslIo.h
+++ b/cpp/src/qpid/sys/ssl/SslIo.h
@@ -29,26 +29,30 @@
namespace qpid {
namespace sys {
+
+class Socket;
+
namespace ssl {
-
+
class SslSocket;
/*
* Asynchronous ssl acceptor: accepts connections then does a callback
* with the accepted fd
*/
-class SslAcceptor {
+template <class T>
+class SslAcceptorTmpl {
public:
- typedef boost::function1<void, const SslSocket&> Callback;
+ typedef boost::function1<void, const Socket&> Callback;
private:
Callback acceptedCallback;
qpid::sys::DispatchHandle handle;
- const SslSocket& socket;
+ const T& socket;
public:
- SslAcceptor(const SslSocket& s, Callback callback);
- ~SslAcceptor();
+ SslAcceptorTmpl(const T& s, Callback callback);
+ ~SslAcceptorTmpl();
void start(qpid::sys::Poller::shared_ptr poller);
private:
@@ -73,7 +77,7 @@ public:
SslConnector(const SslSocket& socket,
Poller::shared_ptr poller,
std::string hostname,
- uint16_t port,
+ std::string port,
ConnectedCallback connCb,
FailedCallback failCb = 0);
diff --git a/cpp/src/qpid/sys/ssl/SslSocket.cpp b/cpp/src/qpid/sys/ssl/SslSocket.cpp
index 01e2658877..30234bb686 100644
--- a/cpp/src/qpid/sys/ssl/SslSocket.cpp
+++ b/cpp/src/qpid/sys/ssl/SslSocket.cpp
@@ -25,11 +25,13 @@
#include "qpid/Exception.h"
#include "qpid/sys/posix/check.h"
#include "qpid/sys/posix/PrivatePosix.h"
+#include "qpid/log/Statement.h"
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/errno.h>
+#include <poll.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
@@ -50,36 +52,6 @@ namespace sys {
namespace ssl {
namespace {
-std::string getName(int fd, bool local, bool includeService = false)
-{
- ::sockaddr_storage name; // big enough for any socket address
- ::socklen_t namelen = sizeof(name);
-
- int result = -1;
- if (local) {
- result = ::getsockname(fd, (::sockaddr*)&name, &namelen);
- } else {
- result = ::getpeername(fd, (::sockaddr*)&name, &namelen);
- }
-
- QPID_POSIX_CHECK(result);
-
- char servName[NI_MAXSERV];
- char dispName[NI_MAXHOST];
- if (includeService) {
- if (int rc=::getnameinfo((::sockaddr*)&name, namelen, dispName, sizeof(dispName),
- servName, sizeof(servName),
- NI_NUMERICHOST | NI_NUMERICSERV) != 0)
- throw QPID_POSIX_ERROR(rc);
- return std::string(dispName) + ":" + std::string(servName);
-
- } else {
- if (int rc=::getnameinfo((::sockaddr*)&name, namelen, dispName, sizeof(dispName), 0, 0, NI_NUMERICHOST) != 0)
- throw QPID_POSIX_ERROR(rc);
- return dispName;
- }
-}
-
std::string getService(int fd, bool local)
{
::sockaddr_storage name; // big enough for any socket address
@@ -132,7 +104,7 @@ std::string getDomainFromSubject(std::string subject)
}
-SslSocket::SslSocket() : IOHandle(new IOHandlePrivate()), socket(0), prototype(0)
+SslSocket::SslSocket() : socket(0), prototype(0)
{
impl->fd = ::socket (PF_INET, SOCK_STREAM, 0);
if (impl->fd < 0) throw QPID_POSIX_ERROR(errno);
@@ -144,7 +116,7 @@ SslSocket::SslSocket() : IOHandle(new IOHandlePrivate()), socket(0), prototype(0
* returned from accept. Because we use posix accept rather than
* PR_Accept, we have to reset the handshake.
*/
-SslSocket::SslSocket(IOHandlePrivate* ioph, PRFileDesc* model) : IOHandle(ioph), socket(0), prototype(0)
+SslSocket::SslSocket(IOHandlePrivate* ioph, PRFileDesc* model) : Socket(ioph), socket(0), prototype(0)
{
socket = SSL_ImportFD(model, PR_ImportTCPSocket(impl->fd));
NSS_CHECK(SSL_ResetHandshake(socket, true));
@@ -158,7 +130,7 @@ void SslSocket::setNonblocking() const
PR_SetSocketOption(socket, &option);
}
-void SslSocket::connect(const std::string& host, uint16_t port) const
+void SslSocket::connect(const std::string& host, const std::string& port) const
{
std::stringstream namestream;
namestream << host << ":" << port;
@@ -180,7 +152,7 @@ void SslSocket::connect(const std::string& host, uint16_t port) const
PRHostEnt hostEntry;
PR_CHECK(PR_GetHostByName(host.data(), hostBuffer, PR_NETDB_BUF_SIZE, &hostEntry));
PRNetAddr address;
- int value = PR_EnumerateHostEnt(0, &hostEntry, port, &address);
+ int value = PR_EnumerateHostEnt(0, &hostEntry, boost::lexical_cast<PRUint16>(port), &address);
if (value < 0) {
throw Exception(QPID_MSG("Error getting address for host: " << ErrorString()));
} else if (value == 0) {
@@ -238,6 +210,7 @@ int SslSocket::listen(uint16_t port, int backlog, const std::string& certName, b
SslSocket* SslSocket::accept() const
{
+ QPID_LOG(trace, "Accepting SSL connection.");
int afd = ::accept(impl->fd, 0, 0);
if ( afd >= 0) {
return new SslSocket(new IOHandlePrivate(afd), prototype);
@@ -248,36 +221,109 @@ SslSocket* SslSocket::accept() const
}
}
-int SslSocket::read(void *buf, size_t count) const
-{
- return PR_Read(socket, buf, count);
-}
+#define SSL_STREAM_MAX_WAIT_ms 20
+#define SSL_STREAM_MAX_RETRIES 2
-int SslSocket::write(const void *buf, size_t count) const
-{
- return PR_Write(socket, buf, count);
-}
+static bool isSslStream(int afd) {
+ int retries = SSL_STREAM_MAX_RETRIES;
+ unsigned char buf[5] = {};
-std::string SslSocket::getSockname() const
-{
- return getName(impl->fd, true);
+ do {
+ struct pollfd fd = {afd, POLLIN, 0};
+
+ /*
+ * Note that this is blocking the accept thread, so connections that
+ * send no data can limit the rate at which we can accept new
+ * connections.
+ */
+ if (::poll(&fd, 1, SSL_STREAM_MAX_WAIT_ms) > 0) {
+ errno = 0;
+ int result = recv(afd, buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT);
+ if (result == sizeof(buf)) {
+ break;
+ }
+ if (errno && errno != EAGAIN) {
+ int err = errno;
+ ::close(afd);
+ throw QPID_POSIX_ERROR(err);
+ }
+ }
+ } while (retries-- > 0);
+
+ if (retries < 0) {
+ return false;
+ }
+
+ /*
+ * SSLv2 Client Hello format
+ * http://www.mozilla.org/projects/security/pki/nss/ssl/draft02.html
+ *
+ * Bytes 0-1: RECORD-LENGTH
+ * Byte 2: MSG-CLIENT-HELLO (1)
+ * Byte 3: CLIENT-VERSION-MSB
+ * Byte 4: CLIENT-VERSION-LSB
+ *
+ * Allowed versions:
+ * 2.0 - SSLv2
+ * 3.0 - SSLv3
+ * 3.1 - TLS 1.0
+ * 3.2 - TLS 1.1
+ * 3.3 - TLS 1.2
+ *
+ * The version sent in the Client-Hello is the latest version supported by
+ * the client. NSS may send version 3.x in an SSLv2 header for
+ * maximum compatibility.
+ */
+ bool isSSL2Handshake = buf[2] == 1 && // MSG-CLIENT-HELLO
+ ((buf[3] == 3 && buf[4] <= 3) || // SSL 3.0 & TLS 1.0-1.2 (v3.1-3.3)
+ (buf[3] == 2 && buf[4] == 0)); // SSL 2
+
+ /*
+ * SSLv3/TLS Client Hello format
+ * RFC 2246
+ *
+ * Byte 0: ContentType (handshake - 22)
+ * Bytes 1-2: ProtocolVersion {major, minor}
+ *
+ * Allowed versions:
+ * 3.0 - SSLv3
+ * 3.1 - TLS 1.0
+ * 3.2 - TLS 1.1
+ * 3.3 - TLS 1.2
+ */
+ bool isSSL3Handshake = buf[0] == 22 && // handshake
+ (buf[1] == 3 && buf[2] <= 3); // SSL 3.0 & TLS 1.0-1.2 (v3.1-3.3)
+
+ return isSSL2Handshake || isSSL3Handshake;
}
-std::string SslSocket::getPeername() const
+Socket* SslMuxSocket::accept() const
{
- return getName(impl->fd, false);
+ int afd = ::accept(impl->fd, 0, 0);
+ if (afd >= 0) {
+ QPID_LOG(trace, "Accepting connection with optional SSL wrapper.");
+ if (isSslStream(afd)) {
+ QPID_LOG(trace, "Accepted SSL connection.");
+ return new SslSocket(new IOHandlePrivate(afd), prototype);
+ } else {
+ QPID_LOG(trace, "Accepted Plaintext connection.");
+ return new Socket(new IOHandlePrivate(afd));
+ }
+ } else if (errno == EAGAIN) {
+ return 0;
+ } else {
+ throw QPID_POSIX_ERROR(errno);
+ }
}
-std::string SslSocket::getPeerAddress() const
+int SslSocket::read(void *buf, size_t count) const
{
- if (!connectname.empty())
- return connectname;
- return getName(impl->fd, false, true);
+ return PR_Read(socket, buf, count);
}
-std::string SslSocket::getLocalAddress() const
+int SslSocket::write(const void *buf, size_t count) const
{
- return getName(impl->fd, true, true);
+ return PR_Write(socket, buf, count);
}
uint16_t SslSocket::getLocalPort() const
@@ -290,17 +336,6 @@ uint16_t SslSocket::getRemotePort() const
return atoi(getService(impl->fd, true).c_str());
}
-int SslSocket::getError() const
-{
- int result;
- socklen_t rSize = sizeof (result);
-
- if (::getsockopt(impl->fd, SOL_SOCKET, SO_ERROR, &result, &rSize) < 0)
- throw QPID_POSIX_ERROR(errno);
-
- return result;
-}
-
void SslSocket::setTcpNoDelay(bool nodelay) const
{
if (nodelay) {
diff --git a/cpp/src/qpid/sys/ssl/SslSocket.h b/cpp/src/qpid/sys/ssl/SslSocket.h
index 25712c98d5..eabadcbe23 100644
--- a/cpp/src/qpid/sys/ssl/SslSocket.h
+++ b/cpp/src/qpid/sys/ssl/SslSocket.h
@@ -23,6 +23,7 @@
*/
#include "qpid/sys/IOHandle.h"
+#include "qpid/sys/Socket.h"
#include <nspr.h>
#include <string>
@@ -36,7 +37,7 @@ class Duration;
namespace ssl {
-class SslSocket : public qpid::sys::IOHandle
+class SslSocket : public qpid::sys::Socket
{
public:
/** Create a socket wrapper for descriptor. */
@@ -53,7 +54,7 @@ public:
* NSSInit().*/
void setCertName(const std::string& certName);
- void connect(const std::string& host, uint16_t port) const;
+ void connect(const std::string& host, const std::string& port) const;
void close() const;
@@ -75,45 +76,13 @@ public:
int read(void *buf, size_t count) const;
int write(const void *buf, size_t count) const;
- /** Returns the "socket name" ie the address bound to
- * the near end of the socket
- */
- std::string getSockname() const;
-
- /** Returns the "peer name" ie the address bound to
- * the remote end of the socket
- */
- std::string getPeername() const;
-
- /**
- * Returns an address (host and port) for the remote end of the
- * socket
- */
- std::string getPeerAddress() const;
- /**
- * Returns an address (host and port) for the local end of the
- * socket
- */
- std::string getLocalAddress() const;
-
- /**
- * Returns the full address of the connection: local and remote host and port.
- */
- std::string getFullAddress() const { return getLocalAddress()+"-"+getPeerAddress(); }
-
uint16_t getLocalPort() const;
uint16_t getRemotePort() const;
- /**
- * Returns the error code stored in the socket. This may be used
- * to determine the result of a non-blocking connect.
- */
- int getError() const;
-
int getKeyLen() const;
std::string getClientAuthId() const;
-private:
+protected:
mutable std::string connectname;
mutable PRFileDesc* socket;
std::string certname;
@@ -126,6 +95,13 @@ private:
mutable PRFileDesc* prototype;
SslSocket(IOHandlePrivate* ioph, PRFileDesc* model);
+ friend class SslMuxSocket;
+};
+
+class SslMuxSocket : public SslSocket
+{
+public:
+ Socket* accept() const;
};
}}}
diff --git a/cpp/src/qpid/sys/windows/AsynchIO.cpp b/cpp/src/qpid/sys/windows/AsynchIO.cpp
index 38d8842521..30378d4c5f 100644
--- a/cpp/src/qpid/sys/windows/AsynchIO.cpp
+++ b/cpp/src/qpid/sys/windows/AsynchIO.cpp
@@ -30,6 +30,7 @@
#include "qpid/log/Statement.h"
#include "qpid/sys/windows/check.h"
+#include "qpid/sys/windows/mingw32_compat.h"
#include <boost/thread/once.hpp>
@@ -46,16 +47,13 @@ namespace {
/*
* The function pointers for AcceptEx and ConnectEx need to be looked up
- * at run time. Make sure this is done only once.
+ * at run time.
*/
-boost::once_flag lookUpAcceptExOnce = BOOST_ONCE_INIT;
-LPFN_ACCEPTEX fnAcceptEx = 0;
-typedef void (*lookUpFunc)(const qpid::sys::Socket &);
-
-void lookUpAcceptEx() {
- SOCKET h = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+const LPFN_ACCEPTEX lookUpAcceptEx(const qpid::sys::Socket& s) {
+ SOCKET h = toSocketHandle(s);
GUID guidAcceptEx = WSAID_ACCEPTEX;
DWORD dwBytes = 0;
+ LPFN_ACCEPTEX fnAcceptEx;
WSAIoctl(h,
SIO_GET_EXTENSION_FUNCTION_POINTER,
&guidAcceptEx,
@@ -65,9 +63,9 @@ void lookUpAcceptEx() {
&dwBytes,
NULL,
NULL);
- closesocket(h);
if (fnAcceptEx == 0)
throw qpid::Exception(QPID_MSG("Failed to look up AcceptEx"));
+ return fnAcceptEx;
}
}
@@ -94,18 +92,15 @@ private:
AsynchAcceptor::Callback acceptedCallback;
const Socket& socket;
+ const LPFN_ACCEPTEX fnAcceptEx;
};
AsynchAcceptor::AsynchAcceptor(const Socket& s, Callback callback)
: acceptedCallback(callback),
- socket(s) {
+ socket(s),
+ fnAcceptEx(lookUpAcceptEx(s)) {
s.setNonblocking();
-#if (BOOST_VERSION >= 103500) /* boost 1.35 or later reversed the args */
- boost::call_once(lookUpAcceptExOnce, lookUpAcceptEx);
-#else
- boost::call_once(lookUpAcceptEx, lookUpAcceptExOnce);
-#endif
}
AsynchAcceptor::~AsynchAcceptor()
@@ -114,7 +109,8 @@ AsynchAcceptor::~AsynchAcceptor()
}
void AsynchAcceptor::start(Poller::shared_ptr poller) {
- poller->monitorHandle(PollerHandle(socket), Poller::INPUT);
+ PollerHandle ph = PollerHandle(socket);
+ poller->monitorHandle(ph, Poller::INPUT);
restart ();
}
@@ -122,25 +118,26 @@ void AsynchAcceptor::restart(void) {
DWORD bytesReceived = 0; // Not used, needed for AcceptEx API
AsynchAcceptResult *result = new AsynchAcceptResult(acceptedCallback,
this,
- toSocketHandle(socket));
+ socket);
BOOL status;
- status = ::fnAcceptEx(toSocketHandle(socket),
- toSocketHandle(*result->newSocket),
- result->addressBuffer,
- 0,
- AsynchAcceptResult::SOCKADDRMAXLEN,
- AsynchAcceptResult::SOCKADDRMAXLEN,
- &bytesReceived,
- result->overlapped());
+ status = fnAcceptEx(toSocketHandle(socket),
+ toSocketHandle(*result->newSocket),
+ result->addressBuffer,
+ 0,
+ AsynchAcceptResult::SOCKADDRMAXLEN,
+ AsynchAcceptResult::SOCKADDRMAXLEN,
+ &bytesReceived,
+ result->overlapped());
QPID_WINDOWS_CHECK_ASYNC_START(status);
}
AsynchAcceptResult::AsynchAcceptResult(AsynchAcceptor::Callback cb,
AsynchAcceptor *acceptor,
- SOCKET listener)
- : callback(cb), acceptor(acceptor), listener(listener) {
- newSocket.reset (new Socket());
+ const Socket& listener)
+ : callback(cb), acceptor(acceptor),
+ listener(toSocketHandle(listener)),
+ newSocket(listener.createSameTypeSocket()) {
}
void AsynchAcceptResult::success(size_t /*bytesTransferred*/) {
@@ -154,7 +151,7 @@ void AsynchAcceptResult::success(size_t /*bytesTransferred*/) {
delete this;
}
-void AsynchAcceptResult::failure(int status) {
+void AsynchAcceptResult::failure(int /*status*/) {
//if (status != WSA_OPERATION_ABORTED)
// Can there be anything else? ;
delete this;
@@ -173,20 +170,20 @@ private:
FailedCallback failCallback;
const Socket& socket;
const std::string hostname;
- const uint16_t port;
+ const std::string port;
public:
AsynchConnector(const Socket& socket,
- std::string hostname,
- uint16_t port,
+ const std::string& hostname,
+ const std::string& port,
ConnectedCallback connCb,
FailedCallback failCb = 0);
void start(Poller::shared_ptr poller);
};
AsynchConnector::AsynchConnector(const Socket& sock,
- std::string hname,
- uint16_t p,
+ const std::string& hname,
+ const std::string& p,
ConnectedCallback connCb,
FailedCallback failCb) :
connCallback(connCb), failCallback(failCb), socket(sock),
@@ -216,8 +213,8 @@ AsynchAcceptor* AsynchAcceptor::create(const Socket& s,
}
AsynchConnector* qpid::sys::AsynchConnector::create(const Socket& s,
- std::string hostname,
- uint16_t port,
+ const std::string& hostname,
+ const std::string& port,
ConnectedCallback connCb,
FailedCallback failCb)
{
@@ -410,8 +407,9 @@ void AsynchIO::queueForDeletion() {
}
void AsynchIO::start(Poller::shared_ptr poller0) {
+ PollerHandle ph = PollerHandle(socket);
poller = poller0;
- poller->monitorHandle(PollerHandle(socket), Poller::INPUT);
+ poller->monitorHandle(ph, Poller::INPUT);
if (writeQueue.size() > 0) // Already have data queued for write
notifyPendingWrite();
startReading();
@@ -584,7 +582,6 @@ void AsynchIO::notifyIdle(void) {
void AsynchIO::startWrite(AsynchIO::BufferBase* buff) {
writeInProgress = true;
InterlockedIncrement(&opsInProgress);
- int writeCount = buff->byteCount-buff->dataCount;
AsynchWriteResult *result =
new AsynchWriteResult(boost::bind(&AsynchIO::completion, this, _1),
buff,
diff --git a/cpp/src/qpid/sys/windows/AsynchIoResult.h b/cpp/src/qpid/sys/windows/AsynchIoResult.h
index 66c89efc11..27e4c22138 100755
--- a/cpp/src/qpid/sys/windows/AsynchIoResult.h
+++ b/cpp/src/qpid/sys/windows/AsynchIoResult.h
@@ -83,22 +83,22 @@ class AsynchAcceptResult : public AsynchResult {
public:
AsynchAcceptResult(qpid::sys::AsynchAcceptor::Callback cb,
AsynchAcceptor *acceptor,
- SOCKET listener);
+ const qpid::sys::Socket& listener);
virtual void success (size_t bytesTransferred);
virtual void failure (int error);
private:
virtual void complete(void) {} // No-op for this class.
- std::auto_ptr<qpid::sys::Socket> newSocket;
qpid::sys::AsynchAcceptor::Callback callback;
AsynchAcceptor *acceptor;
SOCKET listener;
+ std::auto_ptr<qpid::sys::Socket> newSocket;
// AcceptEx needs a place to write the local and remote addresses
// when accepting the connection. Place those here; get enough for
// IPv6 addresses, even if the socket is IPv4.
- enum { SOCKADDRMAXLEN = sizeof sockaddr_in6 + 16,
+ enum { SOCKADDRMAXLEN = sizeof(sockaddr_in6) + 16,
SOCKADDRBUFLEN = 2 * SOCKADDRMAXLEN };
char addressBuffer[SOCKADDRBUFLEN];
};
diff --git a/cpp/src/qpid/sys/windows/IocpPoller.cpp b/cpp/src/qpid/sys/windows/IocpPoller.cpp
index d326ab02ac..1805dd2cd8 100755
--- a/cpp/src/qpid/sys/windows/IocpPoller.cpp
+++ b/cpp/src/qpid/sys/windows/IocpPoller.cpp
@@ -152,9 +152,9 @@ void Poller::monitorHandle(PollerHandle& handle, Direction dir) {
}
// All no-ops...
-void Poller::unmonitorHandle(PollerHandle& handle, Direction dir) {}
-void Poller::registerHandle(PollerHandle& handle) {}
-void Poller::unregisterHandle(PollerHandle& handle) {}
+void Poller::unmonitorHandle(PollerHandle& /*handle*/, Direction /*dir*/) {}
+void Poller::registerHandle(PollerHandle& /*handle*/) {}
+void Poller::unregisterHandle(PollerHandle& /*handle*/) {}
Poller::Event Poller::wait(Duration timeout) {
DWORD timeoutMs = 0;
diff --git a/cpp/src/qpid/sys/windows/Shlib.cpp b/cpp/src/qpid/sys/windows/Shlib.cpp
index 38027de93f..ba18747eb4 100644
--- a/cpp/src/qpid/sys/windows/Shlib.cpp
+++ b/cpp/src/qpid/sys/windows/Shlib.cpp
@@ -44,7 +44,8 @@ void Shlib::unload() {
}
void* Shlib::getSymbol(const char* name) {
- void* sym = GetProcAddress(static_cast<HMODULE>(handle), name);
+ // Double cast avoids warning about casting function pointer to object
+ void *sym = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(GetProcAddress(static_cast<HMODULE>(handle), name)));
if (sym == NULL)
throw QPID_WINDOWS_ERROR(GetLastError());
return sym;
diff --git a/cpp/src/qpid/sys/windows/Socket.cpp b/cpp/src/qpid/sys/windows/Socket.cpp
index 11fb8b4133..1fa4768329 100755..100644
--- a/cpp/src/qpid/sys/windows/Socket.cpp
+++ b/cpp/src/qpid/sys/windows/Socket.cpp
@@ -20,19 +20,18 @@
*/
#include "qpid/sys/Socket.h"
+
#include "qpid/sys/SocketAddress.h"
-#include "qpid/sys/windows/IoHandlePrivate.h"
#include "qpid/sys/windows/check.h"
-#include "qpid/sys/Time.h"
+#include "qpid/sys/windows/IoHandlePrivate.h"
-#include <cstdlib>
-#include <string.h>
+// Ensure we get all of winsock2.h
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501
+#endif
#include <winsock2.h>
-#include <boost/format.hpp>
-#include <boost/lexical_cast.hpp>
-
// Need to initialize WinSock. Ideally, this would be a singleton or embedded
// in some one-time initialization function. I tried boost singleton and could
// not get it to compile (and others located in google had the same problem).
@@ -84,53 +83,30 @@ namespace sys {
namespace {
-std::string getName(SOCKET fd, bool local, bool includeService = false)
+std::string getName(SOCKET fd, bool local)
{
- sockaddr_in name; // big enough for any socket address
- socklen_t namelen = sizeof(name);
+ ::sockaddr_storage name_s; // big enough for any socket address
+ ::sockaddr* name = (::sockaddr*)&name_s;
+ ::socklen_t namelen = sizeof(name_s);
+
if (local) {
- QPID_WINSOCK_CHECK(::getsockname(fd, (sockaddr*)&name, &namelen));
+ QPID_WINSOCK_CHECK(::getsockname(fd, name, &namelen));
} else {
- QPID_WINSOCK_CHECK(::getpeername(fd, (sockaddr*)&name, &namelen));
+ QPID_WINSOCK_CHECK(::getpeername(fd, name, &namelen));
}
- char servName[NI_MAXSERV];
- char dispName[NI_MAXHOST];
- if (includeService) {
- if (int rc = ::getnameinfo((sockaddr*)&name, namelen,
- dispName, sizeof(dispName),
- servName, sizeof(servName),
- NI_NUMERICHOST | NI_NUMERICSERV) != 0)
- throw qpid::Exception(QPID_MSG(gai_strerror(rc)));
- return std::string(dispName) + ":" + std::string(servName);
- } else {
- if (int rc = ::getnameinfo((sockaddr*)&name, namelen,
- dispName, sizeof(dispName),
- 0, 0,
- NI_NUMERICHOST) != 0)
- throw qpid::Exception(QPID_MSG(gai_strerror(rc)));
- return dispName;
- }
+ return SocketAddress::asString(name, namelen);
}
-std::string getService(SOCKET fd, bool local)
+uint16_t getLocalPort(int fd)
{
- sockaddr_in name; // big enough for any socket address
- socklen_t namelen = sizeof(name);
-
- if (local) {
- QPID_WINSOCK_CHECK(::getsockname(fd, (sockaddr*)&name, &namelen));
- } else {
- QPID_WINSOCK_CHECK(::getpeername(fd, (sockaddr*)&name, &namelen));
- }
+ ::sockaddr_storage name_s; // big enough for any socket address
+ ::sockaddr* name = (::sockaddr*)&name_s;
+ ::socklen_t namelen = sizeof(name_s);
+
+ QPID_WINSOCK_CHECK(::getsockname(fd, name, &namelen));
- char servName[NI_MAXSERV];
- if (int rc = ::getnameinfo((sockaddr*)&name, namelen,
- 0, 0,
- servName, sizeof(servName),
- NI_NUMERICHOST | NI_NUMERICSERV) != 0)
- throw qpid::Exception(QPID_MSG(gai_strerror(rc)));
- return servName;
+ return SocketAddress::getPort(name);
}
} // namespace
@@ -138,13 +114,7 @@ Socket::Socket() :
IOHandle(new IOHandlePrivate),
nonblocking(false),
nodelay(false)
-{
- SOCKET& socket = impl->fd;
- if (socket != INVALID_SOCKET) Socket::close();
- SOCKET s = ::socket (PF_INET, SOCK_STREAM, 0);
- if (s == INVALID_SOCKET) throw QPID_WINDOWS_ERROR(WSAGetLastError());
- socket = s;
-}
+{}
Socket::Socket(IOHandlePrivate* h) :
IOHandle(h),
@@ -152,8 +122,7 @@ Socket::Socket(IOHandlePrivate* h) :
nodelay(false)
{}
-void
-Socket::createSocket(const SocketAddress& sa) const
+void Socket::createSocket(const SocketAddress& sa) const
{
SOCKET& socket = impl->fd;
if (socket != INVALID_SOCKET) Socket::close();
@@ -168,24 +137,24 @@ Socket::createSocket(const SocketAddress& sa) const
if (nonblocking) setNonblocking();
if (nodelay) setTcpNoDelay();
} catch (std::exception&) {
- closesocket(s);
+ ::closesocket(s);
socket = INVALID_SOCKET;
throw;
}
}
-void Socket::setTimeout(const Duration& interval) const
-{
- const SOCKET& socket = impl->fd;
- int64_t nanosecs = interval;
- nanosecs /= (1000 * 1000); // nsecs -> usec -> msec
- int msec = 0;
- if (nanosecs > std::numeric_limits<int>::max())
- msec = std::numeric_limits<int>::max();
- else
- msec = static_cast<int>(nanosecs);
- setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (char *)&msec, sizeof(msec));
- setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&msec, sizeof(msec));
+Socket* Socket::createSameTypeSocket() const {
+ SOCKET& socket = impl->fd;
+ // Socket currently has no actual socket attached
+ if (socket == INVALID_SOCKET)
+ return new Socket;
+
+ ::sockaddr_storage sa;
+ ::socklen_t salen = sizeof(sa);
+ QPID_WINSOCK_CHECK(::getsockname(socket, (::sockaddr*)&sa, &salen));
+ SOCKET s = ::socket(sa.ss_family, SOCK_STREAM, 0); // Currently only work with SOCK_STREAM
+ if (s == INVALID_SOCKET) throw QPID_WINDOWS_ERROR(WSAGetLastError());
+ return new Socket(new IOHandlePrivate(s));
}
void Socket::setNonblocking() const {
@@ -193,30 +162,25 @@ void Socket::setNonblocking() const {
QPID_WINSOCK_CHECK(ioctlsocket(impl->fd, FIONBIO, &nonblock));
}
-void Socket::connect(const std::string& host, uint16_t port) const
+void Socket::connect(const std::string& host, const std::string& port) const
{
- SocketAddress sa(host, boost::lexical_cast<std::string>(port));
+ SocketAddress sa(host, port);
connect(sa);
}
void
Socket::connect(const SocketAddress& addr) const
{
+ peername = addr.asString(false);
+
+ createSocket(addr);
+
const SOCKET& socket = impl->fd;
- const addrinfo *addrs = &(getAddrInfo(addr));
- int error = 0;
+ int err;
WSASetLastError(0);
- while (addrs != 0) {
- if ((::connect(socket, addrs->ai_addr, addrs->ai_addrlen) == 0) ||
- (WSAGetLastError() == WSAEWOULDBLOCK))
- break;
- // Error... save this error code and see if there are other address
- // to try before throwing the exception.
- error = WSAGetLastError();
- addrs = addrs->ai_next;
- }
- if (error)
- throw qpid::Exception(QPID_MSG(strError(error) << ": " << connectname));
+ if ((::connect(socket, getAddrInfo(addr).ai_addr, getAddrInfo(addr).ai_addrlen) != 0) &&
+ ((err = ::WSAGetLastError()) != WSAEWOULDBLOCK))
+ throw qpid::Exception(QPID_MSG(strError(err) << ": " << peername));
}
void
@@ -247,24 +211,26 @@ int Socket::read(void *buf, size_t count) const
return received;
}
-int Socket::listen(uint16_t port, int backlog) const
+int Socket::listen(const std::string& host, const std::string& port, int backlog) const
+{
+ SocketAddress sa(host, port);
+ return listen(sa, backlog);
+}
+
+int Socket::listen(const SocketAddress& addr, int backlog) const
{
+ createSocket(addr);
+
const SOCKET& socket = impl->fd;
BOOL yes=1;
QPID_WINSOCK_CHECK(setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes)));
- struct sockaddr_in name;
- memset(&name, 0, sizeof(name));
- name.sin_family = AF_INET;
- name.sin_port = htons(port);
- name.sin_addr.s_addr = 0;
- if (::bind(socket, (struct sockaddr*)&name, sizeof(name)) == SOCKET_ERROR)
- throw Exception(QPID_MSG("Can't bind to port " << port << ": " << strError(WSAGetLastError())));
+
+ if (::bind(socket, getAddrInfo(addr).ai_addr, getAddrInfo(addr).ai_addrlen) == SOCKET_ERROR)
+ throw Exception(QPID_MSG("Can't bind to " << addr.asString() << ": " << strError(WSAGetLastError())));
if (::listen(socket, backlog) == SOCKET_ERROR)
- throw Exception(QPID_MSG("Can't listen on port " << port << ": " << strError(WSAGetLastError())));
-
- socklen_t namelen = sizeof(name);
- QPID_WINSOCK_CHECK(::getsockname(socket, (struct sockaddr*)&name, &namelen));
- return ntohs(name.sin_port);
+ throw Exception(QPID_MSG("Can't listen on " <<addr.asString() << ": " << strError(WSAGetLastError())));
+
+ return getLocalPort(socket);
}
Socket* Socket::accept() const
@@ -277,36 +243,20 @@ Socket* Socket::accept() const
else throw QPID_WINDOWS_ERROR(WSAGetLastError());
}
-std::string Socket::getSockname() const
-{
- return getName(impl->fd, true);
-}
-
-std::string Socket::getPeername() const
-{
- return getName(impl->fd, false);
-}
-
std::string Socket::getPeerAddress() const
{
- if (!connectname.empty())
- return std::string (connectname);
- return getName(impl->fd, false, true);
+ if (peername.empty()) {
+ peername = getName(impl->fd, false);
+ }
+ return peername;
}
std::string Socket::getLocalAddress() const
{
- return getName(impl->fd, true, true);
-}
-
-uint16_t Socket::getLocalPort() const
-{
- return atoi(getService(impl->fd, true).c_str());
-}
-
-uint16_t Socket::getRemotePort() const
-{
- return atoi(getService(impl->fd, true).c_str());
+ if (localname.empty()) {
+ localname = getName(impl->fd, true);
+ }
+ return localname;
}
int Socket::getError() const
diff --git a/cpp/src/qpid/sys/windows/SocketAddress.cpp b/cpp/src/qpid/sys/windows/SocketAddress.cpp
index 501cff1297..77bbf85810 100644
--- a/cpp/src/qpid/sys/windows/SocketAddress.cpp
+++ b/cpp/src/qpid/sys/windows/SocketAddress.cpp
@@ -21,7 +21,13 @@
#include "qpid/sys/SocketAddress.h"
-#include "qpid/sys/windows/check.h"
+#include "qpid/Exception.h"
+#include "qpid/Msg.h"
+
+// Ensure we get all of winsock2.h
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501
+#endif
#include <winsock2.h>
#include <ws2tcpip.h>
@@ -35,37 +41,111 @@ SocketAddress::SocketAddress(const std::string& host0, const std::string& port0)
port(port0),
addrInfo(0)
{
- ::addrinfo hints;
- ::memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_INET; // In order to allow AF_INET6 we'd have to change createTcp() as well
- hints.ai_socktype = SOCK_STREAM;
-
- const char* node = 0;
- if (host.empty()) {
- hints.ai_flags |= AI_PASSIVE;
- } else {
- node = host.c_str();
- }
- const char* service = port.empty() ? "0" : port.c_str();
+}
- int n = ::getaddrinfo(node, service, &hints, &addrInfo);
- if (n != 0)
- throw Exception(QPID_MSG("Cannot resolve " << host << ": " << ::gai_strerror(n)));
+SocketAddress::SocketAddress(const SocketAddress& sa) :
+ host(sa.host),
+ port(sa.port),
+ addrInfo(0)
+{
+}
+
+SocketAddress& SocketAddress::operator=(const SocketAddress& sa)
+{
+ SocketAddress temp(sa);
+
+ std::swap(temp, *this);
+ return *this;
}
SocketAddress::~SocketAddress()
{
- ::freeaddrinfo(addrInfo);
+ if (addrInfo) {
+ ::freeaddrinfo(addrInfo);
+ }
}
-std::string SocketAddress::asString() const
+std::string SocketAddress::asString(::sockaddr const * const addr, size_t addrlen)
{
- return host + ":" + port;
+ char servName[NI_MAXSERV];
+ char dispName[NI_MAXHOST];
+ if (int rc=::getnameinfo(addr, addrlen,
+ dispName, sizeof(dispName),
+ servName, sizeof(servName),
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0)
+ throw qpid::Exception(QPID_MSG(gai_strerror(rc)));
+ std::string s;
+ switch (addr->sa_family) {
+ case AF_INET: s += dispName; break;
+ case AF_INET6: s += "["; s += dispName; s+= "]"; break;
+ default: throw Exception(QPID_MSG("Unexpected socket type"));
+ }
+ s += ":";
+ s += servName;
+ return s;
+}
+
+uint16_t SocketAddress::getPort(::sockaddr const * const addr)
+{
+ switch (addr->sa_family) {
+ case AF_INET: return ntohs(((::sockaddr_in*)addr)->sin_port);
+ case AF_INET6: return ntohs(((::sockaddr_in6*)addr)->sin6_port);
+ default:throw Exception(QPID_MSG("Unexpected socket type"));
+ }
+}
+
+std::string SocketAddress::asString(bool numeric) const
+{
+ if (!numeric)
+ return host + ":" + port;
+ // Canonicalise into numeric id
+ const ::addrinfo& ai = getAddrInfo(*this);
+
+ return asString(ai.ai_addr, ai.ai_addrlen);
+}
+
+bool SocketAddress::nextAddress() {
+ bool r = currentAddrInfo->ai_next != 0;
+ if (r)
+ currentAddrInfo = currentAddrInfo->ai_next;
+ return r;
+}
+
+void SocketAddress::setAddrInfoPort(uint16_t port) {
+ if (!currentAddrInfo) return;
+
+ ::addrinfo& ai = *currentAddrInfo;
+ switch (ai.ai_family) {
+ case AF_INET: ((::sockaddr_in*)ai.ai_addr)->sin_port = htons(port); return;
+ case AF_INET6:((::sockaddr_in6*)ai.ai_addr)->sin6_port = htons(port); return;
+ default: throw Exception(QPID_MSG("Unexpected socket type"));
+ }
}
const ::addrinfo& getAddrInfo(const SocketAddress& sa)
{
- return *sa.addrInfo;
+ if (!sa.addrInfo) {
+ ::addrinfo hints;
+ ::memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_ADDRCONFIG; // Only use protocols that we have configured interfaces for
+ hints.ai_family = AF_UNSPEC; // Allow both IPv4 and IPv6
+ hints.ai_socktype = SOCK_STREAM;
+
+ const char* node = 0;
+ if (sa.host.empty()) {
+ hints.ai_flags |= AI_PASSIVE;
+ } else {
+ node = sa.host.c_str();
+ }
+ const char* service = sa.port.empty() ? "0" : sa.port.c_str();
+
+ int n = ::getaddrinfo(node, service, &hints, &sa.addrInfo);
+ if (n != 0)
+ throw Exception(QPID_MSG("Cannot resolve " << sa.asString(false) << ": " << ::gai_strerror(n)));
+ sa.currentAddrInfo = sa.addrInfo;
+ }
+
+ return *sa.currentAddrInfo;
}
}}
diff --git a/cpp/src/qpid/sys/windows/SslAsynchIO.h b/cpp/src/qpid/sys/windows/SslAsynchIO.h
index 3cdf2c8f08..edec081ced 100644
--- a/cpp/src/qpid/sys/windows/SslAsynchIO.h
+++ b/cpp/src/qpid/sys/windows/SslAsynchIO.h
@@ -39,9 +39,6 @@ namespace qpid {
namespace sys {
namespace windows {
-class Socket;
-class Poller;
-
/*
* SSL/Schannel shim between the frame-handling and AsynchIO layers.
* SslAsynchIO creates a regular AsynchIO object to handle I/O and this class
diff --git a/cpp/src/qpid/sys/windows/StrError.cpp b/cpp/src/qpid/sys/windows/StrError.cpp
index 9c1bfcd79c..546d399d16 100755
--- a/cpp/src/qpid/sys/windows/StrError.cpp
+++ b/cpp/src/qpid/sys/windows/StrError.cpp
@@ -30,6 +30,7 @@ namespace sys {
std::string strError(int err) {
const size_t bufsize = 512;
char buf[bufsize];
+ buf[0] = 0;
if (0 == FormatMessage (FORMAT_MESSAGE_MAX_WIDTH_MASK
| FORMAT_MESSAGE_FROM_SYSTEM,
0,
@@ -39,7 +40,11 @@ std::string strError(int err) {
bufsize,
0))
{
- strerror_s (buf, bufsize, err);
+#ifdef _MSC_VER
+ strerror_s(buf, bufsize, err);
+#else
+ return std::string(strerror(err));
+#endif
}
return std::string(buf);
}
diff --git a/cpp/src/qpid/sys/windows/Thread.cpp b/cpp/src/qpid/sys/windows/Thread.cpp
index 583a9613a3..23b0033be4 100755
--- a/cpp/src/qpid/sys/windows/Thread.cpp
+++ b/cpp/src/qpid/sys/windows/Thread.cpp
@@ -19,6 +19,11 @@
*
*/
+// Ensure definition of OpenThread in mingw
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501
+#endif
+
#include "qpid/sys/Thread.h"
#include "qpid/sys/Runnable.h"
#include "qpid/sys/windows/check.h"
@@ -26,50 +31,204 @@
#include <process.h>
#include <windows.h>
-namespace {
-unsigned __stdcall runRunnable(void* p)
-{
- static_cast<qpid::sys::Runnable*>(p)->run();
- _endthreadex(0);
- return 0;
-}
-}
+/*
+ * This implementation distinguishes between two types of thread: Qpid
+ * threads (based on qpid::sys::Runnable) and the rest. It provides a
+ * join() that will not deadlock against the Windows loader lock for
+ * Qpid threads.
+ *
+ * System thread identifiers are unique per Windows thread; thread
+ * handles are not. Thread identifiers can be recycled, but keeping a
+ * handle open against the thread prevents recycling as long as
+ * shared_ptr references to a ThreadPrivate structure remain.
+ *
+ * There is a 1-1 relationship between Qpid threads and their
+ * ThreadPrivate structure. Non-Qpid threads do not need to find the
+ * qpidThreadDone handle, so there may be a 1-many relationship for
+ * them.
+ *
+ * TLS storage is used for a lockless solution for static library
+ * builds. The special case of LoadLibrary/FreeLibrary requires
+ * additional synchronization variables and resource cleanup in
+ * DllMain. _DLL marks the dynamic case.
+ */
namespace qpid {
namespace sys {
class ThreadPrivate {
+public:
friend class Thread;
+ friend unsigned __stdcall runThreadPrivate(void*);
+ typedef boost::shared_ptr<ThreadPrivate> shared_ptr;
+ ~ThreadPrivate();
- HANDLE threadHandle;
+private:
unsigned threadId;
-
- ThreadPrivate(Runnable* runnable) {
- uintptr_t h = _beginthreadex(0,
- 0,
- runRunnable,
- runnable,
- 0,
- &threadId);
- QPID_WINDOWS_CHECK_CRT_NZ(h);
- threadHandle = reinterpret_cast<HANDLE>(h);
+ HANDLE threadHandle;
+ HANDLE initCompleted;
+ HANDLE qpidThreadDone;
+ Runnable* runnable;
+ shared_ptr keepAlive;
+
+ ThreadPrivate() : threadId(GetCurrentThreadId()), initCompleted(NULL),
+ qpidThreadDone(NULL), runnable(NULL) {
+ threadHandle = OpenThread (SYNCHRONIZE, FALSE, threadId);
+ QPID_WINDOWS_CHECK_CRT_NZ(threadHandle);
}
-
- ThreadPrivate()
- : threadHandle(GetCurrentThread()), threadId(GetCurrentThreadId()) {}
+
+ ThreadPrivate(Runnable* r) : threadHandle(NULL), initCompleted(NULL),
+ qpidThreadDone(NULL), runnable(r) {}
+
+ void start(shared_ptr& p);
+ static shared_ptr createThread(Runnable* r);
};
+}} // namespace qpid::sys
+
+
+namespace {
+using namespace qpid::sys;
+
+#ifdef _DLL
+class ScopedCriticalSection
+{
+ public:
+ ScopedCriticalSection(CRITICAL_SECTION& cs) : criticalSection(cs) { EnterCriticalSection(&criticalSection); }
+ ~ScopedCriticalSection() { LeaveCriticalSection(&criticalSection); }
+ private:
+ CRITICAL_SECTION& criticalSection;
+};
+
+CRITICAL_SECTION threadLock;
+long runningThreads = 0;
+HANDLE threadsDone;
+bool terminating = false;
+#endif
+
+
+DWORD volatile tlsIndex = TLS_OUT_OF_INDEXES;
+
+DWORD getTlsIndex() {
+ if (tlsIndex != TLS_OUT_OF_INDEXES)
+ return tlsIndex; // already set
+
+ DWORD trialIndex = TlsAlloc();
+ QPID_WINDOWS_CHECK_NOT(trialIndex, TLS_OUT_OF_INDEXES); // No OS resource
+
+ // only one thread gets to set the value
+ DWORD actualIndex = (DWORD) InterlockedCompareExchange((LONG volatile *) &tlsIndex, (LONG) trialIndex, (LONG) TLS_OUT_OF_INDEXES);
+ if (actualIndex == TLS_OUT_OF_INDEXES)
+ return trialIndex; // we won the race
+ else {
+ TlsFree(trialIndex);
+ return actualIndex;
+ }
+}
+
+} // namespace
+
+namespace qpid {
+namespace sys {
+
+unsigned __stdcall runThreadPrivate(void* p)
+{
+ ThreadPrivate* threadPrivate = static_cast<ThreadPrivate*>(p);
+ TlsSetValue(getTlsIndex(), threadPrivate);
+
+ WaitForSingleObject (threadPrivate->initCompleted, INFINITE);
+ CloseHandle (threadPrivate->initCompleted);
+ threadPrivate->initCompleted = NULL;
+
+ try {
+ threadPrivate->runnable->run();
+ } catch (...) {
+ // not our concern
+ }
+
+ SetEvent (threadPrivate->qpidThreadDone); // allow join()
+ threadPrivate->keepAlive.reset(); // may run ThreadPrivate destructor
+
+#ifdef _DLL
+ {
+ ScopedCriticalSection l(threadLock);
+ if (--runningThreads == 0)
+ SetEvent(threadsDone);
+ }
+#endif
+ return 0;
+}
+
+
+ThreadPrivate::shared_ptr ThreadPrivate::createThread(Runnable* runnable) {
+ ThreadPrivate::shared_ptr tp(new ThreadPrivate(runnable));
+ tp->start(tp);
+ return tp;
+}
+
+void ThreadPrivate::start(ThreadPrivate::shared_ptr& tp) {
+ getTlsIndex(); // fail here if OS problem, not in new thread
+
+ initCompleted = CreateEvent (NULL, TRUE, FALSE, NULL);
+ QPID_WINDOWS_CHECK_CRT_NZ(initCompleted);
+ qpidThreadDone = CreateEvent (NULL, TRUE, FALSE, NULL);
+ QPID_WINDOWS_CHECK_CRT_NZ(qpidThreadDone);
+
+#ifdef _DLL
+ {
+ ScopedCriticalSection l(threadLock);
+ if (terminating)
+ throw qpid::Exception(QPID_MSG("creating thread after exit/FreeLibrary"));
+ runningThreads++;
+ }
+#endif
+
+ uintptr_t h = _beginthreadex(0,
+ 0,
+ runThreadPrivate,
+ (void *)this,
+ 0,
+ &threadId);
+
+#ifdef _DLL
+ if (h == NULL) {
+ ScopedCriticalSection l(threadLock);
+ if (--runningThreads == 0)
+ SetEvent(threadsDone);
+ }
+#endif
+
+ QPID_WINDOWS_CHECK_CRT_NZ(h);
+
+ // Success
+ keepAlive = tp;
+ threadHandle = reinterpret_cast<HANDLE>(h);
+ SetEvent (initCompleted);
+}
+
+ThreadPrivate::~ThreadPrivate() {
+ if (threadHandle)
+ CloseHandle (threadHandle);
+ if (initCompleted)
+ CloseHandle (initCompleted);
+ if (qpidThreadDone)
+ CloseHandle (qpidThreadDone);
+}
+
+
Thread::Thread() {}
-Thread::Thread(Runnable* runnable) : impl(new ThreadPrivate(runnable)) {}
+Thread::Thread(Runnable* runnable) : impl(ThreadPrivate::createThread(runnable)) {}
-Thread::Thread(Runnable& runnable) : impl(new ThreadPrivate(&runnable)) {}
+Thread::Thread(Runnable& runnable) : impl(ThreadPrivate::createThread(&runnable)) {}
Thread::operator bool() {
return impl;
}
bool Thread::operator==(const Thread& t) const {
+ if (!impl || !t.impl)
+ return false;
return impl->threadId == t.impl->threadId;
}
@@ -79,10 +238,17 @@ bool Thread::operator!=(const Thread& t) const {
void Thread::join() {
if (impl) {
- DWORD status = WaitForSingleObject (impl->threadHandle, INFINITE);
+ DWORD status;
+ if (impl->runnable) {
+ HANDLE handles[2] = {impl->qpidThreadDone, impl->threadHandle};
+ // wait for either. threadHandle not signalled if loader
+ // lock held (FreeLibrary). qpidThreadDone not signalled
+ // if thread terminated by exit().
+ status = WaitForMultipleObjects (2, handles, false, INFINITE);
+ }
+ else
+ status = WaitForSingleObject (impl->threadHandle, INFINITE);
QPID_WINDOWS_CHECK_NOT(status, WAIT_FAILED);
- CloseHandle (impl->threadHandle);
- impl->threadHandle = 0;
}
}
@@ -92,9 +258,70 @@ unsigned long Thread::logId() {
/* static */
Thread Thread::current() {
+ ThreadPrivate* tlsValue = (ThreadPrivate *) TlsGetValue(getTlsIndex());
Thread t;
- t.impl.reset(new ThreadPrivate());
+ if (tlsValue != NULL) {
+ // called from within Runnable->run(), so keepAlive has positive use count
+ t.impl = tlsValue->keepAlive;
+ }
+ else
+ t.impl.reset(new ThreadPrivate());
return t;
}
-}} /* qpid::sys */
+}} // namespace qpid::sys
+
+
+#ifdef _DLL
+
+// DllMain: called possibly many times in a process lifetime if dll
+// loaded and freed repeatedly . Be mindful of Windows loader lock
+// and other DllMain restrictions.
+
+BOOL APIENTRY DllMain(HMODULE hm, DWORD reason, LPVOID reserved) {
+ switch (reason) {
+ case DLL_PROCESS_ATTACH:
+ InitializeCriticalSection(&threadLock);
+ threadsDone = CreateEvent(NULL, TRUE, FALSE, NULL);
+ break;
+
+ case DLL_PROCESS_DETACH:
+ terminating = true;
+ if (reserved != NULL) {
+ // process exit(): threads are stopped arbitrarily and
+ // possibly in an inconsistent state. Not even threadLock
+ // can be trusted. All static destructors have been
+ // called at this point and any resources this unit knows
+ // about will be released as part of process tear down by
+ // the OS. Accordingly, do nothing.
+ return TRUE;
+ }
+ else {
+ // FreeLibrary(): threads are still running and we are
+ // encouraged to clean up to avoid leaks. Mostly we just
+ // want any straggler threads to finish and notify
+ // threadsDone as the last thing they do.
+ while (1) {
+ {
+ ScopedCriticalSection l(threadLock);
+ if (runningThreads == 0)
+ break;
+ ResetEvent(threadsDone);
+ }
+ WaitForSingleObject(threadsDone, INFINITE);
+ }
+ if (tlsIndex != TLS_OUT_OF_INDEXES)
+ TlsFree(getTlsIndex());
+ CloseHandle(threadsDone);
+ DeleteCriticalSection(&threadLock);
+ }
+ break;
+
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
+#endif
diff --git a/cpp/src/qpid/sys/windows/Time.cpp b/cpp/src/qpid/sys/windows/Time.cpp
index 16d09fcdc0..25c50819cd 100644
--- a/cpp/src/qpid/sys/windows/Time.cpp
+++ b/cpp/src/qpid/sys/windows/Time.cpp
@@ -27,6 +27,17 @@
using namespace boost::posix_time;
+namespace {
+
+// High-res timing support. This will display times since program start,
+// more or less. Keep track of the start value and the conversion factor to
+// seconds.
+bool timeInitialized = false;
+LARGE_INTEGER start;
+double freq = 1.0;
+
+}
+
namespace qpid {
namespace sys {
@@ -91,10 +102,35 @@ void outputFormattedNow(std::ostream& o) {
char time_string[100];
::time( &rawtime );
+#ifdef _MSC_VER
::localtime_s(&timeinfo, &rawtime);
+#else
+ timeinfo = *(::localtime(&rawtime));
+#endif
::strftime(time_string, 100,
"%Y-%m-%d %H:%M:%S",
&timeinfo);
o << time_string << " ";
}
+
+void outputHiresNow(std::ostream& o) {
+ if (!timeInitialized) {
+ start.QuadPart = 0;
+ LARGE_INTEGER iFreq;
+ iFreq.QuadPart = 1;
+ QueryPerformanceCounter(&start);
+ QueryPerformanceFrequency(&iFreq);
+ freq = static_cast<double>(iFreq.QuadPart);
+ timeInitialized = true;
+ }
+ LARGE_INTEGER iNow;
+ iNow.QuadPart = 0;
+ QueryPerformanceCounter(&iNow);
+ iNow.QuadPart -= start.QuadPart;
+ if (iNow.QuadPart < 0)
+ iNow.QuadPart = 0;
+ double now = static_cast<double>(iNow.QuadPart);
+ now /= freq; // now is seconds after this
+ o << std::fixed << std::setprecision(8) << std::setw(16) << std::setfill('0') << now << "s ";
+}
}}
diff --git a/cpp/src/tests/qrsh_utils/6_get b/cpp/src/qpid/sys/windows/mingw32_compat.h
index 4b35ca98e6..51f613cc25 100755..100644
--- a/cpp/src/tests/qrsh_utils/6_get
+++ b/cpp/src/qpid/sys/windows/mingw32_compat.h
@@ -1,3 +1,5 @@
+#ifndef _sys_windows_mingw32_compat
+#define _sys_windows_mingw32_compat
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -9,7 +11,7 @@
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -17,13 +19,21 @@
* specific language governing permissions and limitations
* under the License.
*
- */
+ */
-#! /bin/bash
+#ifdef WIN32
+#ifndef _MSC_VER
-echo "getting /tmp/foo ..."
-./qrsh 127.0.0.1 5813 \
- mrg23 get /tmp/foo
+//
+// The following definitions for extension function GUIDs and signatures are taken from
+// MswSock.h in the Windows32 SDK. These rightfully belong in the mingw32 version of
+// mswsock.h, but are not included presently.
+//
+#define WSAID_ACCEPTEX {0xb5367df1,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}}
+typedef BOOL (PASCAL *LPFN_ACCEPTEX)(SOCKET,SOCKET,PVOID,DWORD,DWORD,DWORD,LPDWORD,LPOVERLAPPED);
+#endif
+#endif
+#endif
diff --git a/cpp/src/qpid/sys/windows/uuid.cpp b/cpp/src/qpid/sys/windows/uuid.cpp
index b5360622dc..3316ecbc00 100644
--- a/cpp/src/qpid/sys/windows/uuid.cpp
+++ b/cpp/src/qpid/sys/windows/uuid.cpp
@@ -19,7 +19,7 @@
*
*/
-#include <Rpc.h>
+#include <rpc.h>
#ifdef uuid_t /* Done in rpcdce.h */
# undef uuid_t
#endif
@@ -52,7 +52,11 @@ int uuid_parse (const char *in, uuid_t uu) {
void uuid_unparse (const uuid_t uu, char *out) {
unsigned char *formatted;
if (UuidToString((UUID*)uu, &formatted) == RPC_S_OK) {
+#ifdef _MSC_VER
strncpy_s (out, 36+1, (char*)formatted, _TRUNCATE);
+#else
+ strncpy (out, (char*)formatted, 36+1);
+#endif
RpcStringFree(&formatted);
}
}
diff --git a/cpp/src/qpid/types/Uuid.cpp b/cpp/src/qpid/types/Uuid.cpp
index 9face4e5d2..9862fa8946 100644
--- a/cpp/src/qpid/types/Uuid.cpp
+++ b/cpp/src/qpid/types/Uuid.cpp
@@ -20,6 +20,7 @@
*/
#include "qpid/types/Uuid.h"
#include "qpid/sys/uuid.h"
+#include "qpid/sys/IntegerTypes.h"
#include <sstream>
#include <iostream>
#include <string.h>
@@ -71,7 +72,8 @@ void Uuid::clear()
// Force int 0/!0 to false/true; avoids compile warnings.
bool Uuid::isNull() const
{
- return !!uuid_is_null(bytes);
+ // This const cast is for Solaris which has non const arguments
+ return !!uuid_is_null(const_cast<uint8_t*>(bytes));
}
Uuid::operator bool() const { return !isNull(); }
@@ -86,7 +88,8 @@ const unsigned char* Uuid::data() const
bool operator==(const Uuid& a, const Uuid& b)
{
- return uuid_compare(a.bytes, b.bytes) == 0;
+ // This const cast is for Solaris which has non const arguments
+ return uuid_compare(const_cast<uint8_t*>(a.bytes), const_cast<uint8_t*>(b.bytes)) == 0;
}
bool operator!=(const Uuid& a, const Uuid& b)
@@ -96,22 +99,26 @@ bool operator!=(const Uuid& a, const Uuid& b)
bool operator<(const Uuid& a, const Uuid& b)
{
- return uuid_compare(a.bytes, b.bytes) < 0;
+ // This const cast is for Solaris which has non const arguments
+ return uuid_compare(const_cast<uint8_t*>(a.bytes), const_cast<uint8_t*>(b.bytes)) < 0;
}
bool operator>(const Uuid& a, const Uuid& b)
{
- return uuid_compare(a.bytes, b.bytes) > 0;
+ // This const cast is for Solaris which has non const arguments
+ return uuid_compare(const_cast<uint8_t*>(a.bytes), const_cast<uint8_t*>(b.bytes)) > 0;
}
bool operator<=(const Uuid& a, const Uuid& b)
{
- return uuid_compare(a.bytes, b.bytes) <= 0;
+ // This const cast is for Solaris which has non const arguments
+ return uuid_compare(const_cast<uint8_t*>(a.bytes), const_cast<uint8_t*>(b.bytes)) <= 0;
}
bool operator>=(const Uuid& a, const Uuid& b)
{
- return uuid_compare(a.bytes, b.bytes) >= 0;
+ // This const cast is for Solaris which has non const arguments
+ return uuid_compare(const_cast<uint8_t*>(a.bytes), const_cast<uint8_t*>(b.bytes)) >= 0;
}
ostream& operator<<(ostream& out, Uuid uuid)
diff --git a/cpp/src/qpid/types/Variant.cpp b/cpp/src/qpid/types/Variant.cpp
index 5d8878bdac..f563d5de5b 100644
--- a/cpp/src/qpid/types/Variant.cpp
+++ b/cpp/src/qpid/types/Variant.cpp
@@ -19,7 +19,6 @@
*
*/
#include "qpid/types/Variant.h"
-#include "qpid/Msg.h"
#include "qpid/log/Statement.h"
#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
@@ -108,15 +107,27 @@ class VariantImpl
} value;
std::string encoding;//optional encoding for variable length data
- std::string getTypeName(VariantType type) const;
template<class T> T convertFromString() const
{
std::string* s = reinterpret_cast<std::string*>(value.v);
- try {
- return boost::lexical_cast<T>(*s);
- } catch(const boost::bad_lexical_cast&) {
- throw InvalidConversion(QPID_MSG("Cannot convert " << *s));
+ if (std::numeric_limits<T>::is_signed || s->find('-') != 0) {
+ //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
+ }
}
+ throw InvalidConversion(QPID_MSG("Cannot convert " << *s));
}
};
@@ -370,11 +381,11 @@ int8_t VariantImpl::asInt8() const
return int8_t(value.ui16);
break;
case VAR_UINT32:
- if (value.ui32 <= (uint) std::numeric_limits<int8_t>::max())
+ if (value.ui32 <= (uint32_t) std::numeric_limits<int8_t>::max())
return int8_t(value.ui32);
break;
case VAR_UINT64:
- if (value.ui64 <= (uint) std::numeric_limits<int8_t>::max())
+ if (value.ui64 <= (uint64_t) std::numeric_limits<int8_t>::max())
return int8_t(value.ui64);
break;
case VAR_STRING: return convertFromString<int8_t>();
@@ -401,11 +412,11 @@ int16_t VariantImpl::asInt16() const
return int16_t(value.ui16);
break;
case VAR_UINT32:
- if (value.ui32 <= (uint) std::numeric_limits<int16_t>::max())
+ if (value.ui32 <= (uint32_t) std::numeric_limits<int16_t>::max())
return int16_t(value.ui32);
break;
case VAR_UINT64:
- if (value.ui64 <= (uint) std::numeric_limits<int16_t>::max())
+ if (value.ui64 <= (uint64_t) std::numeric_limits<int16_t>::max())
return int16_t(value.ui64);
break;
case VAR_STRING: return convertFromString<int16_t>();
@@ -430,7 +441,7 @@ int32_t VariantImpl::asInt32() const
return int32_t(value.ui32);
break;
case VAR_UINT64:
- if (value.ui64 <= (uint32_t) std::numeric_limits<int32_t>::max())
+ if (value.ui64 <= (uint64_t) std::numeric_limits<int32_t>::max())
return int32_t(value.ui64);
break;
case VAR_STRING: return convertFromString<int32_t>();
@@ -582,7 +593,7 @@ const std::string& VariantImpl::getString() const
void VariantImpl::setEncoding(const std::string& s) { encoding = s; }
const std::string& VariantImpl::getEncoding() const { return encoding; }
-std::string VariantImpl::getTypeName(VariantType type) const
+std::string getTypeName(VariantType type)
{
switch (type) {
case VAR_VOID: return "void";
diff --git a/cpp/src/replication.mk b/cpp/src/replication.mk
index dbe071f405..e5da32f88b 100644
--- a/cpp/src/replication.mk
+++ b/cpp/src/replication.mk
@@ -19,14 +19,14 @@
# Make file for building two plugins for asynchronously replicating
# queues.
-dmodule_LTLIBRARIES += replicating_listener.la replication_exchange.la
+dmoduleexec_LTLIBRARIES += replicating_listener.la replication_exchange.la
# a queue event listener plugin that creates messages on a replication
# queue corresponding to enqueue and dequeue events:
replicating_listener_la_SOURCES = \
qpid/replication/constants.h \
qpid/replication/ReplicatingEventListener.cpp \
- qpid/replication/ReplicatingEventListener.h
+ qpid/replication/ReplicatingEventListener.h
replicating_listener_la_LIBADD = libqpidbroker.la
if SUNOS
@@ -41,7 +41,7 @@ replicating_listener_la_LDFLAGS = $(PLUGINLDFLAGS)
replication_exchange_la_SOURCES = \
qpid/replication/constants.h \
qpid/replication/ReplicationExchange.cpp \
- qpid/replication/ReplicationExchange.h
+ qpid/replication/ReplicationExchange.h
replication_exchange_la_LIBADD = libqpidbroker.la
diff --git a/cpp/src/ssl.mk b/cpp/src/ssl.mk
index 5fbdd55438..4dba9bb61c 100644
--- a/cpp/src/ssl.mk
+++ b/cpp/src/ssl.mk
@@ -18,7 +18,7 @@
#
#
# Makefile fragment, conditionally included in Makefile.am
-#
+#
libsslcommon_la_SOURCES = \
qpid/sys/ssl/check.h \
qpid/sys/ssl/check.cpp \
@@ -47,7 +47,7 @@ ssl_la_CXXFLAGS=$(AM_CXXFLAGS) $(SSL_CFLAGS)
ssl_la_LDFLAGS = $(PLUGINLDFLAGS)
-dmodule_LTLIBRARIES += ssl.la
+dmoduleexec_LTLIBRARIES += ssl.la
sslconnector_la_SOURCES = \
qpid/client/SslConnector.cpp
@@ -60,5 +60,5 @@ sslconnector_la_CXXFLAGS = $(AM_CXXFLAGS) -DQPIDC_CONF_FILE=\"$(confdir)/qpidc.c
sslconnector_la_LDFLAGS = $(PLUGINLDFLAGS)
-cmodule_LTLIBRARIES += \
+cmoduleexec_LTLIBRARIES += \
sslconnector.la
diff --git a/cpp/src/tests/.valgrind.supp b/cpp/src/tests/.valgrind.supp
index 0e3e045437..2c6a1509ff 100644
--- a/cpp/src/tests/.valgrind.supp
+++ b/cpp/src/tests/.valgrind.supp
@@ -73,61 +73,6 @@
}
{
- boost 103200 -- we think Boost is responsible for these leaks.
- Memcheck:Leak
- fun:_Znwm
- fun:_ZN5boost15program_options??options_description*
-}
-
-{
- boost 103200 -- we think Boost is responsible for these leaks.
- Memcheck:Leak
- fun:_Znwm
- fun:_ZN5boost9unit_test9test_case*
-}
-
-{
- boost 103200 -- we think Boost is responsible for these leaks.
- Memcheck:Leak
- fun:calloc
- fun:_dlerror_run
- fun:dlopen@@GLIBC_2.2.5
- fun:_ZN4qpid3sys5Shlib4loadEPKc
- fun:_Z9testShlibv
- fun:_ZN5boost9unit_test9ut_detail17unit_test_monitor8functionEv
- obj:/usr/lib64/libboost_unit_test_framework.so.1.32.0
- fun:_ZN5boost17execution_monitor7executeEbi
- fun:_ZN5boost9unit_test9ut_detail17unit_test_monitor21execute_and_translateEPNS0_9test_caseEMS3_FvvEi
- fun:_ZN5boost9unit_test9test_case3runEv
- fun:_ZN5boost9unit_test10test_suite6do_runEv
- fun:_ZN5boost9unit_test9test_case3runEv
- fun:main
-}
-
-{
- boost 103200 -- we think Boost is responsible for these leaks.
- Memcheck:Leak
- fun:calloc
- fun:_dl_allocate_tls
- fun:pthread_create@@GLIBC_2.2.5
- fun:_ZN4qpid6broker5Timer5startEv
- fun:_ZN4qpid6broker5TimerC1Ev
- fun:_ZN4qpid6broker10DtxManagerC1Ev
- fun:_ZN4qpid6broker6BrokerC1ERKNS1_7OptionsE
- fun:_ZN4qpid6broker6Broker6createERKNS1_7OptionsE
- fun:_ZN15SessionFixtureTI15ProxyConnectionEC2Ev
- fun:_Z14testQueueQueryv
- fun:_ZN5boost9unit_test9ut_detail17unit_test_monitor8functionEv
- obj:/usr/lib64/libboost_unit_test_framework.so.1.32.0
- fun:_ZN5boost17execution_monitor7executeEbi
- fun:_ZN5boost9unit_test9ut_detail17unit_test_monitor21execute_and_translateEPNS0_9test_caseEMS3_FvvEi
- fun:_ZN5boost9unit_test9test_case3runEv
- fun:_ZN5boost9unit_test10test_suite6do_runEv
- fun:_ZN5boost9unit_test9test_case3runEv
- fun:main
-}
-
-{
INVESTIGATE
Memcheck:Leak
fun:calloc
@@ -155,25 +100,6 @@
}
{
- boost 103200 -- mgoulish -- fix this, sometime
- Memcheck:Leak
- fun:*
- fun:*
- obj:*
- fun:*
- fun:_ZN4qpid34options_description_less_easy_initclEPKcPKN5boost15program_options14value_semanticES2_
-}
-
-{
- boost 103200 -- mgoulish -- fix this, sometime
- Memcheck:Leak
- fun:*
- fun:*
- fun:*
- fun:_ZN4qpid34options_description_less_easy_initclEPKcPKN5boost15program_options14value_semanticES2_
-}
-
-{
INVESTIGATE
Memcheck:Param
socketcall.sendto(msg)
diff --git a/cpp/src/tests/Address.cpp b/cpp/src/tests/Address.cpp
index f41f27b6df..0fd3585958 100644
--- a/cpp/src/tests/Address.cpp
+++ b/cpp/src/tests/Address.cpp
@@ -119,6 +119,17 @@ QPID_AUTO_TEST_CASE(testParseQuotedNameAndSubject)
BOOST_CHECK_EQUAL(std::string("my subject with ; in it"), address.getSubject());
}
+QPID_AUTO_TEST_CASE(testParseOptionsWithEmptyStringAsValue)
+{
+ Address address("my-topic; {a:'', x:101}");
+ BOOST_CHECK_EQUAL(std::string("my-topic"), address.getName());
+ Variant a = address.getOptions()["a"];
+ BOOST_CHECK_EQUAL(VAR_STRING, a.getType());
+ std::string aVal = a;
+ BOOST_CHECK(aVal.size() == 0);
+ BOOST_CHECK_EQUAL((uint16_t) 101, address.getOptions()["x"].asInt64());
+}
+
QPID_AUTO_TEST_SUITE_END()
}}
diff --git a/cpp/src/tests/BrokerFixture.h b/cpp/src/tests/BrokerFixture.h
index 672d954572..92c6d22b57 100644
--- a/cpp/src/tests/BrokerFixture.h
+++ b/cpp/src/tests/BrokerFixture.h
@@ -22,8 +22,6 @@
*
*/
-#include "SocketProxy.h"
-
#include "qpid/broker/Broker.h"
#include "qpid/client/Connection.h"
#include "qpid/client/ConnectionImpl.h"
@@ -71,16 +69,15 @@ struct BrokerFixture : private boost::noncopyable {
brokerThread = qpid::sys::Thread(*broker);
};
- void shutdownBroker()
- {
- broker->shutdown();
- broker = BrokerPtr();
+ void shutdownBroker() {
+ if (broker) {
+ broker->shutdown();
+ brokerThread.join();
+ broker = BrokerPtr();
+ }
}
- ~BrokerFixture() {
- if (broker) broker->shutdown();
- brokerThread.join();
- }
+ ~BrokerFixture() { shutdownBroker(); }
/** Open a connection to the broker. */
void open(qpid::client::Connection& c) {
@@ -97,20 +94,6 @@ struct LocalConnection : public qpid::client::Connection {
~LocalConnection() { close(); }
};
-/** A local client connection via a socket proxy. */
-struct ProxyConnection : public qpid::client::Connection {
- SocketProxy proxy;
- ProxyConnection(int brokerPort) : proxy(brokerPort) {
- open("localhost", proxy.getPort());
- }
- ProxyConnection(const qpid::client::ConnectionSettings& s) : proxy(s.port) {
- qpid::client::ConnectionSettings proxySettings(s);
- proxySettings.port = proxy.getPort();
- open(proxySettings);
- }
- ~ProxyConnection() { close(); }
-};
-
/** Convenience class to create and open a connection and session
* and some related useful objects.
*/
@@ -147,7 +130,6 @@ struct SessionFixtureT : BrokerFixture, ClientT<ConnectionType,SessionType> {
};
typedef SessionFixtureT<LocalConnection> SessionFixture;
-typedef SessionFixtureT<ProxyConnection> ProxySessionFixture;
}} // namespace qpid::tests
diff --git a/cpp/src/tests/BrokerMgmtAgent.cpp b/cpp/src/tests/BrokerMgmtAgent.cpp
index d0c6668b72..1d5289dc90 100644
--- a/cpp/src/tests/BrokerMgmtAgent.cpp
+++ b/cpp/src/tests/BrokerMgmtAgent.cpp
@@ -599,13 +599,12 @@ namespace qpid {
// populate the agent with multiple test objects
const size_t objCount = 50;
std::vector<TestManageable *> tmv;
- uint32_t objLen;
for (size_t i = 0; i < objCount; i++) {
std::stringstream key;
key << "testobj-" << i;
TestManageable *tm = new TestManageable(agent, key.str());
- objLen = tm->GetManagementObject()->writePropertiesSize();
+ (void) tm->GetManagementObject()->writePropertiesSize();
agent->addObject(tm->GetManagementObject(), key.str());
tmv.push_back(tm);
}
diff --git a/cpp/src/tests/BrokerOptions.cpp b/cpp/src/tests/BrokerOptions.cpp
new file mode 100644
index 0000000000..b36d96916a
--- /dev/null
+++ b/cpp/src/tests/BrokerOptions.cpp
@@ -0,0 +1,79 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+/** Unit tests for various broker configuration options **/
+
+#include "unit_test.h"
+#include "test_tools.h"
+#include "MessagingFixture.h"
+
+#include "qpid/messaging/Address.h"
+#include "qpid/messaging/Connection.h"
+#include "qpid/messaging/Message.h"
+#include "qpid/messaging/Receiver.h"
+#include "qpid/messaging/Sender.h"
+#include "qpid/messaging/Session.h"
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(BrokerOptionsTestSuite)
+
+using namespace qpid::broker;
+using namespace qpid::messaging;
+using namespace qpid::types;
+using namespace qpid;
+
+QPID_AUTO_TEST_CASE(testDisabledTimestamp)
+{
+ // by default, there should be no timestamp added by the broker
+ MessagingFixture fix;
+
+ Sender sender = fix.session.createSender("test-q; {create:always, delete:sender}");
+ messaging::Message msg("hi");
+ sender.send(msg);
+
+ Receiver receiver = fix.session.createReceiver("test-q");
+ messaging::Message in;
+ BOOST_CHECK(receiver.fetch(in, Duration::IMMEDIATE));
+ Variant::Map props = in.getProperties();
+ BOOST_CHECK(props.find("x-amqp-0-10.timestamp") == props.end());
+}
+
+QPID_AUTO_TEST_CASE(testEnabledTimestamp)
+{
+ // when enabled, the 0.10 timestamp is added by the broker
+ Broker::Options opts;
+ opts.timestampRcvMsgs = true;
+ MessagingFixture fix(opts, true);
+
+ Sender sender = fix.session.createSender("test-q; {create:always, delete:sender}");
+ messaging::Message msg("one");
+ sender.send(msg);
+
+ Receiver receiver = fix.session.createReceiver("test-q");
+ messaging::Message in;
+ BOOST_CHECK(receiver.fetch(in, Duration::IMMEDIATE));
+ Variant::Map props = in.getProperties();
+ BOOST_CHECK(props.find("x-amqp-0-10.timestamp") != props.end());
+ BOOST_CHECK(props["x-amqp-0-10.timestamp"]);
+}
+
+QPID_AUTO_TEST_SUITE_END()
+
+}}
diff --git a/cpp/src/tests/CMakeLists.txt b/cpp/src/tests/CMakeLists.txt
index 3b3b232671..7d781e5eb3 100644
--- a/cpp/src/tests/CMakeLists.txt
+++ b/cpp/src/tests/CMakeLists.txt
@@ -107,7 +107,6 @@ set(unit_tests_to_build
MessagingSessionTests
SequenceSet
StringUtils
- IncompleteMessageList
RangeSet
AtomicValue
QueueTest
@@ -119,6 +118,7 @@ set(unit_tests_to_build
MessageTest
QueueRegistryTest
QueuePolicyTest
+ QueueFlowLimitTest
FramingTest
HeaderTest
SequenceNumberTest
@@ -264,6 +264,19 @@ add_executable (qpid-send qpid-send.cpp Statistics.cpp ${platform_test_additions
target_link_libraries (qpid-send qpidmessaging)
remember_location(qpid-send)
+add_executable (qpid-ping qpid-ping.cpp ${platform_test_additions})
+target_link_libraries (qpid-ping qpidclient)
+remember_location(qpid-ping)
+
+add_executable (datagen datagen.cpp ${platform_test_additions})
+target_link_libraries (datagen qpidclient)
+remember_location(datagen)
+
+add_executable (msg_group_test msg_group_test.cpp ${platform_test_additions})
+target_link_libraries (msg_group_test qpidmessaging)
+remember_location(msg_group_test)
+
+
# qpid-perftest and qpid-latency-test are generally useful so install them
install (TARGETS qpid-perftest qpid-latency-test RUNTIME
DESTINATION ${QPID_INSTALL_BINDIR})
@@ -278,7 +291,7 @@ set(test_wrap ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_test${test_script_suffix}
add_test (unit_test ${test_wrap} ${unit_test_LOCATION})
add_test (start_broker ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/start_broker${test_script_suffix})
-add_test (qpid-client-test ${test_wrap} ${qpid-client_test_LOCATION})
+add_test (qpid-client-test ${test_wrap} ${qpid-client-test_LOCATION})
add_test (quick_perftest ${test_wrap} ${qpid-perftest_LOCATION} --summary --count 100)
add_test (quick_topictest ${test_wrap} ${CMAKE_CURRENT_SOURCE_DIR}/quick_topictest${test_script_suffix})
add_test (quick_txtest ${test_wrap} ${qpid-txtest_LOCATION} --queues 4 --tx-count 10 --quiet)
@@ -288,6 +301,7 @@ if (PYTHON_EXECUTABLE)
endif (PYTHON_EXECUTABLE)
add_test (stop_broker ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/stop_broker${test_script_suffix})
if (PYTHON_EXECUTABLE)
+ add_test (ipv6_test ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/ipv6_test${test_script_suffix})
add_test (federation_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_federation_tests${test_script_suffix})
if (BUILD_ACL)
add_test (acl_tests ${shell} ${CMAKE_CURRENT_SOURCE_DIR}/run_acl_tests${test_script_suffix})
diff --git a/cpp/src/tests/ClientSessionTest.cpp b/cpp/src/tests/ClientSessionTest.cpp
index 939f8f2b88..30441cd03c 100644
--- a/cpp/src/tests/ClientSessionTest.cpp
+++ b/cpp/src/tests/ClientSessionTest.cpp
@@ -102,9 +102,9 @@ struct SimpleListener : public MessageListener
}
};
-struct ClientSessionFixture : public ProxySessionFixture
+struct ClientSessionFixture : public SessionFixture
{
- ClientSessionFixture(Broker::Options opts = Broker::Options()) : ProxySessionFixture(opts) {
+ ClientSessionFixture(Broker::Options opts = Broker::Options()) : SessionFixture(opts) {
session.queueDeclare(arg::queue="my-queue");
}
};
@@ -150,16 +150,6 @@ QPID_AUTO_TEST_CASE(testDispatcherThread)
BOOST_CHECK_EQUAL(boost::lexical_cast<string>(i), listener.messages[i].getData());
}
-// FIXME aconway 2009-06-17: test for unimplemented feature, enable when implemented.
-void testSuspend0Timeout() {
- ClientSessionFixture fix;
- fix.session.suspend(); // session has 0 timeout.
- try {
- fix.connection.resume(fix.session);
- BOOST_FAIL("Expected InvalidArgumentException.");
- } catch(const InternalErrorException&) {}
-}
-
QPID_AUTO_TEST_CASE(testUseSuspendedError)
{
ClientSessionFixture fix;
@@ -171,18 +161,6 @@ QPID_AUTO_TEST_CASE(testUseSuspendedError)
} catch(const NotAttachedException&) {}
}
-// FIXME aconway 2009-06-17: test for unimplemented feature, enable when implemented.
-void testSuspendResume() {
- ClientSessionFixture fix;
- fix.session.timeout(60);
- fix.session.suspend();
- // Make sure we are still subscribed after resume.
- fix.connection.resume(fix.session);
- fix.session.messageTransfer(arg::content=Message("my-message", "my-queue"));
- BOOST_CHECK_EQUAL("my-message", fix.subs.get("my-queue", TIME_SEC).getData());
-}
-
-
QPID_AUTO_TEST_CASE(testSendToSelf) {
ClientSessionFixture fix;
SimpleListener mylistener;
@@ -271,8 +249,12 @@ QPID_AUTO_TEST_CASE(testOpenFailure) {
QPID_AUTO_TEST_CASE(testPeriodicExpiration) {
Broker::Options opts;
opts.queueCleanInterval = 1;
+ opts.queueFlowStopRatio = 0;
+ opts.queueFlowResumeRatio = 0;
ClientSessionFixture fix(opts);
- fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true);
+ FieldTable args;
+ args.setInt("qpid.max_count",10);
+ fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
for (uint i = 0; i < 10; i++) {
Message m((boost::format("Message_%1%") % (i+1)).str(), "my-queue");
@@ -283,6 +265,7 @@ QPID_AUTO_TEST_CASE(testPeriodicExpiration) {
BOOST_CHECK_EQUAL(fix.session.queueQuery(string("my-queue")).getMessageCount(), 10u);
qpid::sys::sleep(2);
BOOST_CHECK_EQUAL(fix.session.queueQuery(string("my-queue")).getMessageCount(), 5u);
+ fix.session.messageTransfer(arg::content=Message("Message_11", "my-queue"));//ensure policy is also updated
}
QPID_AUTO_TEST_CASE(testExpirationOnPop) {
diff --git a/cpp/src/tests/ExchangeTest.cpp b/cpp/src/tests/ExchangeTest.cpp
index 88a1cd99c2..fe72f42a46 100644
--- a/cpp/src/tests/ExchangeTest.cpp
+++ b/cpp/src/tests/ExchangeTest.cpp
@@ -253,7 +253,7 @@ QPID_AUTO_TEST_CASE(testIVEOption)
TopicExchange topic ("topic1", false, args);
intrusive_ptr<Message> msg1 = cmessage("direct1", "abc");
- msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString("a", "abc");
+ msg1->insertCustomProperty("a", "abc");
DeliverableMessage dmsg1(msg1);
FieldTable args2;
diff --git a/cpp/src/tests/ForkedBroker.cpp b/cpp/src/tests/ForkedBroker.cpp
index 53eaa7e1ce..10674b5175 100644
--- a/cpp/src/tests/ForkedBroker.cpp
+++ b/cpp/src/tests/ForkedBroker.cpp
@@ -68,8 +68,7 @@ ForkedBroker::~ForkedBroker() {
}
if (!dataDir.empty())
{
- int unused_ret; // Suppress warnings about ignoring return value.
- unused_ret = ::system(("rm -rf "+dataDir).c_str());
+ (void) ::system(("rm -rf "+dataDir).c_str());
}
}
diff --git a/cpp/src/tests/IncompleteMessageList.cpp b/cpp/src/tests/IncompleteMessageList.cpp
deleted file mode 100644
index 10782572e5..0000000000
--- a/cpp/src/tests/IncompleteMessageList.cpp
+++ /dev/null
@@ -1,134 +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 <iostream>
-#include <sstream>
-#include "qpid/broker/Message.h"
-#include "qpid/broker/NullMessageStore.h"
-#include "qpid/broker/Queue.h"
-#include "qpid/broker/IncompleteMessageList.h"
-
-#include "unit_test.h"
-
-namespace qpid {
-namespace tests {
-
-QPID_AUTO_TEST_SUITE(IncompleteMessageListTestSuite)
-
-using namespace qpid::broker;
-using namespace qpid::framing;
-
-struct Checker
-{
- std::list<SequenceNumber> ids;
-
- Checker() { }
-
- Checker(uint start, uint end) {
- for (uint i = start; i <= end; i++) {
- ids.push_back(i);
- }
- }
-
- Checker& expect(const SequenceNumber& id) {
- ids.push_back(id);
- return *this;
- }
-
- void operator()(boost::intrusive_ptr<Message> msg) {
- BOOST_CHECK(!ids.empty());
- BOOST_CHECK_EQUAL(msg->getCommandId(), ids.front());
- ids.pop_front();
- }
-};
-
-QPID_AUTO_TEST_CASE(testProcessSimple)
-{
- IncompleteMessageList list;
- SequenceNumber counter(1);
- //fill up list with messages
- for (int i = 0; i < 5; i++) {
- boost::intrusive_ptr<Message> msg(new Message(counter++));
- list.add(msg);
- }
- //process and ensure they are all passed to completion listener
- list.process(Checker(1, 5), false);
- //process again and ensure none are resent to listener
- list.process(Checker(), false);
-}
-
-QPID_AUTO_TEST_CASE(testProcessWithIncomplete)
-{
- Queue::shared_ptr queue;
- IncompleteMessageList list;
- SequenceNumber counter(1);
- boost::intrusive_ptr<Message> middle;
- //fill up list with messages
- for (int i = 0; i < 5; i++) {
- boost::intrusive_ptr<Message> msg(new Message(counter++));
- list.add(msg);
- if (i == 2) {
- //mark a message in the middle as incomplete
- msg->enqueueAsync(queue, 0);
- middle = msg;
- }
- }
- //process and ensure only message upto incomplete message are passed to listener
- list.process(Checker(1, 2), false);
- //mark message complete and re-process to get remaining messages sent to listener
- middle->enqueueComplete();
- list.process(Checker(3, 5), false);
-}
-
-
-struct MockStore : public NullMessageStore
-{
- Queue::shared_ptr queue;
- boost::intrusive_ptr<Message> msg;
-
- void flush(const qpid::broker::PersistableQueue& q) {
- BOOST_CHECK_EQUAL(queue.get(), &q);
- msg->enqueueComplete();
- }
-};
-
-QPID_AUTO_TEST_CASE(testSyncProcessWithIncomplete)
-{
- IncompleteMessageList list;
- SequenceNumber counter(1);
- MockStore store;
- store.queue = Queue::shared_ptr(new Queue("mock-queue", false, &store));
- //fill up list with messages
- for (int i = 0; i < 5; i++) {
- boost::intrusive_ptr<Message> msg(new Message(counter++));
- list.add(msg);
- if (i == 2) {
- //mark a message in the middle as incomplete
- msg->enqueueAsync(store.queue, &store);
- store.msg = msg;
- }
- }
- //process with sync bit specified and ensure that all messages are passed to listener
- list.process(Checker(1, 5), true);
-}
-
-QPID_AUTO_TEST_SUITE_END()
-
-}} // namespace qpid::tests
diff --git a/cpp/src/tests/Makefile.am b/cpp/src/tests/Makefile.am
index 07405bcd8f..3c9ca1b70f 100644
--- a/cpp/src/tests/Makefile.am
+++ b/cpp/src/tests/Makefile.am
@@ -75,7 +75,7 @@ unit_test_SOURCES= unit_test.cpp unit_test.h \
MessagingThreadTests.cpp \
MessagingFixture.h \
ClientSessionTest.cpp \
- BrokerFixture.h SocketProxy.h \
+ BrokerFixture.h \
exception_test.cpp \
RefCounted.cpp \
SessionState.cpp logging.cpp \
@@ -87,7 +87,6 @@ unit_test_SOURCES= unit_test.cpp unit_test.h \
InlineVector.cpp \
SequenceSet.cpp \
StringUtils.cpp \
- IncompleteMessageList.cpp \
RangeSet.cpp \
AtomicValue.cpp \
QueueTest.cpp \
@@ -99,6 +98,7 @@ unit_test_SOURCES= unit_test.cpp unit_test.h \
MessageTest.cpp \
QueueRegistryTest.cpp \
QueuePolicyTest.cpp \
+ QueueFlowLimitTest.cpp \
FramingTest.cpp \
HeaderTest.cpp \
SequenceNumberTest.cpp \
@@ -124,7 +124,8 @@ unit_test_SOURCES= unit_test.cpp unit_test.h \
Variant.cpp \
Address.cpp \
ClientMessage.cpp \
- Qmf2.cpp
+ Qmf2.cpp \
+ BrokerOptions.cpp
if HAVE_XML
unit_test_SOURCES+= XmlClientSessionTest.cpp
@@ -286,31 +287,27 @@ check_PROGRAMS+=datagen
datagen_SOURCES=datagen.cpp
datagen_LDADD=$(lib_common) $(lib_client)
-check_PROGRAMS+=qrsh_server
-qrsh_server_SOURCES=qrsh_server.cpp
-qrsh_server_LDADD=$(lib_client)
-
-check_PROGRAMS+=qrsh_run
-qrsh_run_SOURCES=qrsh_run.cpp
-qrsh_run_LDADD=$(lib_client)
-
-check_PROGRAMS+=qrsh
-qrsh_SOURCES=qrsh.cpp
-qrsh_LDADD=$(lib_client)
-
check_PROGRAMS+=qpid-stream
qpid_stream_INCLUDES=$(PUBLIC_INCLUDES)
qpid_stream_SOURCES=qpid-stream.cpp
qpid_stream_LDADD=$(lib_messaging)
+check_PROGRAMS+=msg_group_test
+msg_group_test_INCLUDES=$(PUBLIC_INCLUDES)
+msg_group_test_SOURCES=msg_group_test.cpp
+msg_group_test_LDADD=$(lib_messaging)
+
TESTS_ENVIRONMENT = \
VALGRIND=$(VALGRIND) \
LIBTOOL="$(LIBTOOL)" \
QPID_DATA_DIR= \
$(srcdir)/run_test
-system_tests = qpid-client-test quick_perftest quick_topictest run_header_test quick_txtest
-TESTS += start_broker $(system_tests) python_tests stop_broker run_federation_tests run_acl_tests run_cli_tests replication_test dynamic_log_level_test
+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 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
EXTRA_DIST += \
run_test vg_check \
@@ -325,6 +322,8 @@ EXTRA_DIST += \
config.null \
ais_check \
run_federation_tests \
+ run_federation_sys_tests \
+ run_long_federation_sys_tests \
run_cli_tests \
run_acl_tests \
.valgrind.supp \
@@ -349,7 +348,10 @@ EXTRA_DIST += \
run_test.ps1 \
start_broker.ps1 \
stop_broker.ps1 \
- topictest.ps1
+ topictest.ps1 \
+ run_queue_flow_limit_tests \
+ run_msg_group_tests \
+ ipv6_test
check_LTLIBRARIES += libdlclose_noop.la
libdlclose_noop_la_LDFLAGS = -module -rpath $(abs_builddir)
@@ -360,7 +362,11 @@ CLEANFILES+=valgrind.out *.log *.vglog* dummy_test qpidd.port $(unit_wrappers)
# Longer running stability tests, not run by default check: target.
# Not run under valgrind, too slow
-LONG_TESTS+=start_broker fanout_perftest shared_perftest multiq_perftest topic_perftest run_ring_queue_test stop_broker \
+LONG_TESTS+=start_broker \
+ fanout_perftest shared_perftest multiq_perftest topic_perftest run_ring_queue_test \
+ run_msg_group_tests_soak \
+ stop_broker \
+ run_long_federation_sys_tests \
run_failover_soak reliable_replication_test \
federated_cluster_test_with_node_failure
@@ -372,7 +378,8 @@ EXTRA_DIST+= \
run_failover_soak \
reliable_replication_test \
federated_cluster_test_with_node_failure \
- sasl_test_setup.sh
+ sasl_test_setup.sh \
+ run_msg_group_tests_soak
check-long:
$(MAKE) check TESTS="$(LONG_TESTS)" VALGRIND=
diff --git a/cpp/src/tests/MessageReplayTracker.cpp b/cpp/src/tests/MessageReplayTracker.cpp
index 3d79ee53c2..e35f673683 100644
--- a/cpp/src/tests/MessageReplayTracker.cpp
+++ b/cpp/src/tests/MessageReplayTracker.cpp
@@ -51,7 +51,7 @@ class ReplayBufferChecker
QPID_AUTO_TEST_CASE(testReplay)
{
- ProxySessionFixture fix;
+ SessionFixture fix;
fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true);
MessageReplayTracker tracker(10);
@@ -77,7 +77,7 @@ QPID_AUTO_TEST_CASE(testReplay)
QPID_AUTO_TEST_CASE(testCheckCompletion)
{
- ProxySessionFixture fix;
+ SessionFixture fix;
fix.session.queueDeclare(arg::queue="my-queue", arg::exclusive=true, arg::autoDelete=true);
MessageReplayTracker tracker(10);
diff --git a/cpp/src/tests/MessagingFixture.h b/cpp/src/tests/MessagingFixture.h
index 715de09bad..2312a87e9d 100644
--- a/cpp/src/tests/MessagingFixture.h
+++ b/cpp/src/tests/MessagingFixture.h
@@ -27,15 +27,19 @@
#include "qpid/client/Connection.h"
#include "qpid/client/Session.h"
#include "qpid/framing/Uuid.h"
+#include "qpid/messaging/Address.h"
#include "qpid/messaging/Connection.h"
#include "qpid/messaging/Session.h"
#include "qpid/messaging/Sender.h"
#include "qpid/messaging/Receiver.h"
#include "qpid/messaging/Message.h"
+#include "qpid/types/Variant.h"
namespace qpid {
namespace tests {
+using qpid::types::Variant;
+
struct BrokerAdmin
{
qpid::client::Connection connection;
@@ -223,6 +227,119 @@ inline void receive(messaging::Receiver& receiver, uint count = 1, uint start =
}
}
+
+class MethodInvoker
+{
+ public:
+ MethodInvoker(messaging::Session& session) : replyTo("#; {create:always, node:{x-declare:{auto-delete:true}}}"),
+ sender(session.createSender("qmf.default.direct/broker")),
+ receiver(session.createReceiver(replyTo)) {}
+
+ void createExchange(const std::string& name, const std::string& type, bool durable=false)
+ {
+ Variant::Map params;
+ params["name"]=name;
+ params["type"]="exchange";
+ params["properties"] = Variant::Map();
+ params["properties"].asMap()["exchange-type"] = type;
+ params["properties"].asMap()["durable"] = durable;
+ methodRequest("create", params);
+ }
+
+ void deleteExchange(const std::string& name)
+ {
+ Variant::Map params;
+ params["name"]=name;
+ params["type"]="exchange";
+ methodRequest("delete", params);
+ }
+
+ void createQueue(const std::string& name, bool durable=false, bool autodelete=false,
+ const Variant::Map& options=Variant::Map())
+ {
+ Variant::Map params;
+ params["name"]=name;
+ params["type"]="queue";
+ params["properties"] = options;
+ params["properties"].asMap()["durable"] = durable;
+ params["properties"].asMap()["auto-delete"] = autodelete;
+ methodRequest("create", params);
+ }
+
+ void deleteQueue(const std::string& name)
+ {
+ Variant::Map params;
+ params["name"]=name;
+ params["type"]="queue";
+ methodRequest("delete", params);
+ }
+
+ void bind(const std::string& exchange, const std::string& queue, const std::string& key,
+ const Variant::Map& options=Variant::Map())
+ {
+ Variant::Map params;
+ params["name"]=(boost::format("%1%/%2%/%3%") % (exchange) % (queue) % (key)).str();
+ params["type"]="binding";
+ params["properties"] = options;
+ methodRequest("create", params);
+ }
+
+ void unbind(const std::string& exchange, const std::string& queue, const std::string& key)
+ {
+ Variant::Map params;
+ params["name"]=(boost::format("%1%/%2%/%3%") % (exchange) % (queue) % (key)).str();
+ params["type"]="binding";
+ methodRequest("delete", params);
+ }
+
+ void methodRequest(const std::string& method, const Variant::Map& inParams, Variant::Map* outParams = 0)
+ {
+ Variant::Map content;
+ Variant::Map objectId;
+ objectId["_object_name"] = "org.apache.qpid.broker:broker:amqp-broker";
+ content["_object_id"] = objectId;
+ content["_method_name"] = method;
+ content["_arguments"] = inParams;
+
+ messaging::Message request;
+ request.setReplyTo(replyTo);
+ request.getProperties()["x-amqp-0-10.app-id"] = "qmf2";
+ request.getProperties()["qmf.opcode"] = "_method_request";
+ encode(content, request);
+
+ sender.send(request);
+
+ messaging::Message response;
+ if (receiver.fetch(response, messaging::Duration::SECOND*5)) {
+ if (response.getProperties()["x-amqp-0-10.app-id"] == "qmf2") {
+ std::string opcode = response.getProperties()["qmf.opcode"];
+ if (opcode == "_method_response") {
+ if (outParams) {
+ Variant::Map m;
+ decode(response, m);
+ *outParams = m["_arguments"].asMap();
+ }
+ } else if (opcode == "_exception") {
+ Variant::Map m;
+ decode(response, m);
+ throw Exception(QPID_MSG("Error: " << m["_values"]));
+ } else {
+ throw Exception(QPID_MSG("Invalid response received, unexpected opcode: " << opcode));
+ }
+ } else {
+ throw Exception(QPID_MSG("Invalid response received, not a qmfv2 message: app-id="
+ << response.getProperties()["x-amqp-0-10.app-id"]));
+ }
+ } else {
+ throw Exception(QPID_MSG("No response received"));
+ }
+ }
+ private:
+ messaging::Address replyTo;
+ messaging::Sender sender;
+ messaging::Receiver receiver;
+};
+
}} // namespace qpid::tests
#endif /*!TESTS_MESSAGINGFIXTURE_H*/
diff --git a/cpp/src/tests/MessagingSessionTests.cpp b/cpp/src/tests/MessagingSessionTests.cpp
index 991ec847bf..9d5db84bb4 100644
--- a/cpp/src/tests/MessagingSessionTests.cpp
+++ b/cpp/src/tests/MessagingSessionTests.cpp
@@ -611,6 +611,28 @@ QPID_AUTO_TEST_CASE(testAssertPolicyQueue)
fix.admin.deleteQueue("q");
}
+QPID_AUTO_TEST_CASE(testAssertExchangeOption)
+{
+ MessagingFixture fix;
+ std::string a1 = "e; {create:always, assert:always, node:{type:topic, x-declare:{type:direct, arguments:{qpid.msg_sequence:True}}}}";
+ Sender s1 = fix.session.createSender(a1);
+ s1.close();
+ Receiver r1 = fix.session.createReceiver(a1);
+ r1.close();
+
+ std::string a2 = "e; {assert:receiver, node:{type:topic, x-declare:{type:fanout, arguments:{qpid.msg_sequence:True}}}}";
+ Sender s2 = fix.session.createSender(a2);
+ s2.close();
+ BOOST_CHECK_THROW(fix.session.createReceiver(a2), qpid::messaging::AssertionFailed);
+
+ std::string a3 = "e; {assert:sender, node:{x-declare:{arguments:{qpid.msg_sequence:False}}}}";
+ BOOST_CHECK_THROW(fix.session.createSender(a3), qpid::messaging::AssertionFailed);
+ Receiver r3 = fix.session.createReceiver(a3);
+ r3.close();
+
+ fix.admin.deleteExchange("e");
+}
+
QPID_AUTO_TEST_CASE(testGetSender)
{
QueueFixture fix;
@@ -890,6 +912,212 @@ QPID_AUTO_TEST_CASE(testAcknowledge)
BOOST_CHECK(!fix.session.createReceiver(fix.queue).fetch(m, Duration::IMMEDIATE));
}
+QPID_AUTO_TEST_CASE(testQmfCreateAndDelete)
+{
+ MessagingFixture fix(Broker::Options(), true/*enable management*/);
+ MethodInvoker control(fix.session);
+ control.createQueue("my-queue");
+ control.createExchange("my-exchange", "topic");
+ control.bind("my-exchange", "my-queue", "subject1");
+
+ Sender sender = fix.session.createSender("my-exchange");
+ Receiver receiver = fix.session.createReceiver("my-queue");
+ Message out;
+ out.setSubject("subject1");
+ out.setContent("one");
+ sender.send(out);
+ Message in;
+ BOOST_CHECK(receiver.fetch(in, Duration::SECOND*5));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+ control.unbind("my-exchange", "my-queue", "subject1");
+ control.bind("my-exchange", "my-queue", "subject2");
+
+ out.setContent("two");
+ sender.send(out);//should be dropped
+
+ out.setSubject("subject2");
+ out.setContent("three");
+ sender.send(out);//should not be dropped
+
+ BOOST_CHECK(receiver.fetch(in, Duration::SECOND*5));
+ BOOST_CHECK_EQUAL(out.getContent(), in.getContent());
+ BOOST_CHECK(!receiver.fetch(in, Duration::IMMEDIATE));
+ sender.close();
+ receiver.close();
+
+ control.deleteExchange("my-exchange");
+ messaging::Session other = fix.connection.createSession();
+ {
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(other.createSender("my-exchange"), qpid::messaging::NotFound);
+ }
+ control.deleteQueue("my-queue");
+ other = fix.connection.createSession();
+ {
+ ScopedSuppressLogging sl;
+ BOOST_CHECK_THROW(other.createReceiver("my-queue"), qpid::messaging::NotFound);
+ }
+}
+
+QPID_AUTO_TEST_CASE(testRejectAndCredit)
+{
+ //Ensure credit is restored on completing rejected messages
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+
+ const uint count(10);
+ receiver.setCapacity(count);
+ for (uint i = 0; i < count; i++) {
+ sender.send(Message((boost::format("Message_%1%") % (i+1)).str()));
+ }
+
+ Message in;
+ for (uint i = 0; i < count; ++i) {
+ if (receiver.fetch(in, Duration::SECOND)) {
+ BOOST_CHECK_EQUAL(in.getContent(), (boost::format("Message_%1%") % (i+1)).str());
+ fix.session.reject(in);
+ } else {
+ BOOST_FAIL((boost::format("Message_%1% not received as expected") % (i+1)).str());
+ break;
+ }
+ }
+ //send another batch of messages
+ for (uint i = 0; i < count; i++) {
+ sender.send(Message((boost::format("Message_%1%") % (i+count)).str()));
+ }
+
+ for (uint i = 0; i < count; ++i) {
+ if (receiver.fetch(in, Duration::SECOND)) {
+ BOOST_CHECK_EQUAL(in.getContent(), (boost::format("Message_%1%") % (i+count)).str());
+ } else {
+ BOOST_FAIL((boost::format("Message_%1% not received as expected") % (i+count)).str());
+ break;
+ }
+ }
+ fix.session.acknowledge();
+ receiver.close();
+ sender.close();
+}
+
+QPID_AUTO_TEST_CASE(testTtlForever)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ Message out("I want to live forever!");
+ out.setTtl(Duration::FOREVER);
+ sender.send(out, true);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(Duration::IMMEDIATE);
+ fix.session.acknowledge();
+ BOOST_CHECK_EQUAL(in.getContent(), out.getContent());
+ BOOST_CHECK(in.getTtl() == Duration::FOREVER);
+}
+
+QPID_AUTO_TEST_CASE(testExclusiveTopicSubscriber)
+{
+ TopicFixture fix;
+ std::string address = (boost::format("%1%; { link: { name: 'my-subscription', x-declare: { auto-delete: true, exclusive: true }}}") % fix.topic).str();
+ Sender sender = fix.session.createSender(fix.topic);
+ Receiver receiver1 = fix.session.createReceiver(address);
+ {
+ ScopedSuppressLogging sl;
+ try {
+ fix.session.createReceiver(address);
+ fix.session.sync();
+ BOOST_FAIL("Expected exception.");
+ } catch (const MessagingException& /*e*/) {}
+ }
+}
+
+QPID_AUTO_TEST_CASE(testNonExclusiveSubscriber)
+{
+ TopicFixture fix;
+ std::string address = (boost::format("%1%; {node:{type:topic}, link:{name:'my-subscription', x-declare:{auto-delete:true, exclusive:false}}}") % fix.topic).str();
+ Receiver receiver1 = fix.session.createReceiver(address);
+ Receiver receiver2 = fix.session.createReceiver(address);
+ Sender sender = fix.session.createSender(fix.topic);
+ sender.send(Message("one"), true);
+ Message in = receiver1.fetch(Duration::IMMEDIATE);
+ BOOST_CHECK_EQUAL(in.getContent(), std::string("one"));
+ sender.send(Message("two"), true);
+ in = receiver2.fetch(Duration::IMMEDIATE);
+ BOOST_CHECK_EQUAL(in.getContent(), std::string("two"));
+ fix.session.acknowledge();
+}
+
+QPID_AUTO_TEST_CASE(testAcknowledgeUpTo)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender(fix.queue);
+ const uint count(20);
+ for (uint i = 0; i < count; ++i) {
+ sender.send(Message((boost::format("Message_%1%") % (i+1)).str()));
+ }
+
+ Session other = fix.connection.createSession();
+ Receiver receiver = other.createReceiver(fix.queue);
+ std::vector<Message> messages;
+ for (uint i = 0; i < count; ++i) {
+ Message msg = receiver.fetch();
+ BOOST_CHECK_EQUAL(msg.getContent(), (boost::format("Message_%1%") % (i+1)).str());
+ messages.push_back(msg);
+ }
+ const uint batch = 10;
+ other.acknowledgeUpTo(messages[batch-1]);//acknowledge first 10 messages only
+
+ messages.clear();
+ other.sync();
+ other.close();
+
+ other = fix.connection.createSession();
+ receiver = other.createReceiver(fix.queue);
+ Message msg;
+ for (uint i = 0; i < (count-batch); ++i) {
+ msg = receiver.fetch();
+ BOOST_CHECK_EQUAL(msg.getContent(), (boost::format("Message_%1%") % (i+1+batch)).str());
+ }
+ other.acknowledgeUpTo(msg);
+ other.sync();
+ other.close();
+
+ Message m;
+ //check queue is empty
+ BOOST_CHECK(!fix.session.createReceiver(fix.queue).fetch(m, Duration::IMMEDIATE));
+}
+
+QPID_AUTO_TEST_CASE(testCreateBindingsOnStandardExchange)
+{
+ QueueFixture fix;
+ Sender sender = fix.session.createSender((boost::format("amq.direct; {create:always, node:{type:topic, x-bindings:[{queue:%1%, key:my-subject}]}}") % fix.queue).str());
+ Message out("test-message");
+ out.setSubject("my-subject");
+ sender.send(out);
+ Receiver receiver = fix.session.createReceiver(fix.queue);
+ Message in = receiver.fetch(Duration::SECOND * 5);
+ fix.session.acknowledge();
+ BOOST_CHECK_EQUAL(in.getContent(), out.getContent());
+ BOOST_CHECK_EQUAL(in.getSubject(), out.getSubject());
+}
+
+QPID_AUTO_TEST_CASE(testUnsubscribeOnClose)
+{
+ MessagingFixture fix;
+ Sender sender = fix.session.createSender("my-exchange/my-subject; {create: always, delete:sender, node:{type:topic, x-declare:{alternate-exchange:amq.fanout}}}");
+ Receiver receiver = fix.session.createReceiver("my-exchange/my-subject");
+ Receiver deadletters = fix.session.createReceiver("amq.fanout");
+
+ sender.send(Message("first"));
+ Message in = receiver.fetch(Duration::SECOND);
+ BOOST_CHECK_EQUAL(in.getContent(), std::string("first"));
+ fix.session.acknowledge();
+ receiver.close();
+ sender.send(Message("second"));
+ in = deadletters.fetch(Duration::SECOND);
+ BOOST_CHECK_EQUAL(in.getContent(), std::string("second"));
+ fix.session.acknowledge();
+}
+
QPID_AUTO_TEST_SUITE_END()
}} // namespace qpid::tests
diff --git a/cpp/src/tests/Qmf2.cpp b/cpp/src/tests/Qmf2.cpp
index 66c774accd..bc263d5c6d 100644
--- a/cpp/src/tests/Qmf2.cpp
+++ b/cpp/src/tests/Qmf2.cpp
@@ -23,12 +23,36 @@
#include "qmf/QueryImpl.h"
#include "qmf/SchemaImpl.h"
#include "qmf/exceptions.h"
-
+#include "qpid/messaging/Connection.h"
+#include "qmf/PosixEventNotifierImpl.h"
+#include "qmf/AgentSession.h"
+#include "qmf/AgentSessionImpl.h"
+#include "qmf/ConsoleSession.h"
+#include "qmf/ConsoleSessionImpl.h"
#include "unit_test.h"
+using namespace std;
using namespace qpid::types;
+using namespace qpid::messaging;
using namespace qmf;
+bool isReadable(int fd)
+{
+ fd_set rfds;
+ struct timeval tv;
+ int nfds, result;
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+ nfds = fd + 1;
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ result = select(nfds, &rfds, NULL, NULL, &tv);
+
+ return result > 0;
+}
+
namespace qpid {
namespace tests {
@@ -315,6 +339,84 @@ QPID_AUTO_TEST_CASE(testSchema)
BOOST_CHECK_THROW(method.getArgument(3), QmfException);
}
+QPID_AUTO_TEST_CASE(testAgentSessionEventListener)
+{
+ Connection connection("localhost");
+ AgentSession session(connection, "");
+ posix::EventNotifier notifier(session);
+
+ AgentSessionImpl& sessionImpl = AgentSessionImplAccess::get(session);
+
+ BOOST_CHECK(sessionImpl.getEventNotifier() != 0);
+}
+
+QPID_AUTO_TEST_CASE(testConsoleSessionEventListener)
+{
+ Connection connection("localhost");
+ ConsoleSession session(connection, "");
+ posix::EventNotifier notifier(session);
+
+ ConsoleSessionImpl& sessionImpl = ConsoleSessionImplAccess::get(session);
+
+ BOOST_CHECK(sessionImpl.getEventNotifier() != 0);
+}
+
+QPID_AUTO_TEST_CASE(testGetHandle)
+{
+ Connection connection("localhost");
+ ConsoleSession session(connection, "");
+ posix::EventNotifier notifier(session);
+
+ BOOST_CHECK(notifier.getHandle() > 0);
+}
+
+QPID_AUTO_TEST_CASE(testSetReadableToFalse)
+{
+ Connection connection("localhost");
+ ConsoleSession session(connection, "");
+ posix::EventNotifier notifier(session);
+ PosixEventNotifierImplAccess::get(notifier).setReadable(false);
+
+ bool readable(isReadable(notifier.getHandle()));
+ BOOST_CHECK(!readable);
+}
+
+QPID_AUTO_TEST_CASE(testSetReadable)
+{
+ Connection connection("localhost");
+ ConsoleSession session(connection, "");
+ posix::EventNotifier notifier(session);
+ PosixEventNotifierImplAccess::get(notifier).setReadable(true);
+
+ bool readable(isReadable(notifier.getHandle()));
+ BOOST_CHECK(readable);
+}
+
+QPID_AUTO_TEST_CASE(testSetReadableMultiple)
+{
+ Connection connection("localhost");
+ ConsoleSession session(connection, "");
+ posix::EventNotifier notifier(session);
+ for (int i = 0; i < 15; i++)
+ PosixEventNotifierImplAccess::get(notifier).setReadable(true);
+ PosixEventNotifierImplAccess::get(notifier).setReadable(false);
+
+ bool readable(isReadable(notifier.getHandle()));
+ BOOST_CHECK(!readable);
+}
+
+QPID_AUTO_TEST_CASE(testDeleteNotifier)
+{
+ Connection connection("localhost");
+ ConsoleSession session(connection, "");
+ ConsoleSessionImpl& sessionImpl = ConsoleSessionImplAccess::get(session);
+ {
+ posix::EventNotifier notifier(session);
+ BOOST_CHECK(sessionImpl.getEventNotifier() != 0);
+ }
+ BOOST_CHECK(sessionImpl.getEventNotifier() == 0);
+}
+
QPID_AUTO_TEST_SUITE_END()
}} // namespace qpid::tests
diff --git a/cpp/src/tests/QueueEvents.cpp b/cpp/src/tests/QueueEvents.cpp
index bd18fa45fb..cea8bbf0db 100644
--- a/cpp/src/tests/QueueEvents.cpp
+++ b/cpp/src/tests/QueueEvents.cpp
@@ -147,7 +147,7 @@ struct EventRecorder
QPID_AUTO_TEST_CASE(testSystemLevelEventProcessing)
{
- ProxySessionFixture fixture;
+ SessionFixture fixture;
//register dummy event listener to broker
EventRecorder listener;
fixture.broker->getQueueEvents().registerListener("recorder", boost::bind(&EventRecorder::handle, &listener, _1));
@@ -194,7 +194,7 @@ QPID_AUTO_TEST_CASE(testSystemLevelEventProcessing)
QPID_AUTO_TEST_CASE(testSystemLevelEventProcessing_enqueuesOnly)
{
- ProxySessionFixture fixture;
+ SessionFixture fixture;
//register dummy event listener to broker
EventRecorder listener;
fixture.broker->getQueueEvents().registerListener("recorder", boost::bind(&EventRecorder::handle, &listener, _1));
diff --git a/cpp/src/tests/QueueFlowLimitTest.cpp b/cpp/src/tests/QueueFlowLimitTest.cpp
new file mode 100644
index 0000000000..8a6923fb09
--- /dev/null
+++ b/cpp/src/tests/QueueFlowLimitTest.cpp
@@ -0,0 +1,463 @@
+/*
+ *
+ * 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 <sstream>
+#include <deque>
+#include "unit_test.h"
+#include "test_tools.h"
+
+#include "qpid/broker/QueuePolicy.h"
+#include "qpid/broker/QueueFlowLimit.h"
+#include "qpid/sys/Time.h"
+#include "qpid/framing/reply_exceptions.h"
+#include "MessageUtils.h"
+#include "BrokerFixture.h"
+
+using namespace qpid::broker;
+using namespace qpid::framing;
+
+namespace qpid {
+namespace tests {
+
+QPID_AUTO_TEST_SUITE(QueueFlowLimitTestSuite)
+
+namespace {
+
+class TestFlow : public QueueFlowLimit
+{
+public:
+ TestFlow(uint32_t flowStopCount, uint32_t flowResumeCount,
+ uint64_t flowStopSize, uint64_t flowResumeSize) :
+ QueueFlowLimit(0, flowStopCount, flowResumeCount, flowStopSize, flowResumeSize)
+ {}
+ virtual ~TestFlow() {}
+
+ static TestFlow *createTestFlow(const qpid::framing::FieldTable& settings)
+ {
+ FieldTable::ValuePtr v;
+
+ v = settings.get(flowStopCountKey);
+ uint32_t flowStopCount = (v) ? (uint32_t)v->get<int64_t>() : 0;
+ v = settings.get(flowResumeCountKey);
+ uint32_t flowResumeCount = (v) ? (uint32_t)v->get<int64_t>() : 0;
+ v = settings.get(flowStopSizeKey);
+ uint64_t flowStopSize = (v) ? (uint64_t)v->get<int64_t>() : 0;
+ v = settings.get(flowResumeSizeKey);
+ uint64_t flowResumeSize = (v) ? (uint64_t)v->get<int64_t>() : 0;
+
+ return new TestFlow(flowStopCount, flowResumeCount, flowStopSize, flowResumeSize);
+ }
+
+ static QueueFlowLimit *getQueueFlowLimit(const qpid::framing::FieldTable& settings)
+ {
+ return QueueFlowLimit::createLimit(0, settings);
+ }
+};
+
+
+
+QueuedMessage createMessage(uint32_t size)
+{
+ static uint32_t seqNum;
+ QueuedMessage msg;
+ msg.payload = MessageUtils::createMessage();
+ msg.position = ++seqNum;
+ MessageUtils::addContent(msg.payload, std::string (size, 'x'));
+ return msg;
+}
+}
+
+QPID_AUTO_TEST_CASE(testFlowCount)
+{
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopCountKey, 7);
+ args.setInt(QueueFlowLimit::flowResumeCountKey, 5);
+
+ std::auto_ptr<TestFlow> flow(TestFlow::createTestFlow(args));
+
+ BOOST_CHECK_EQUAL((uint32_t) 7, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL((uint32_t) 5, flow->getFlowResumeCount());
+ BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+
+ std::deque<QueuedMessage> msgs;
+ for (size_t i = 0; i < 6; i++) {
+ msgs.push_back(createMessage(10));
+ flow->enqueued(msgs.back());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ }
+ BOOST_CHECK(!flow->isFlowControlActive()); // 6 on queue
+ msgs.push_back(createMessage(10));
+ flow->enqueued(msgs.back());
+ BOOST_CHECK(!flow->isFlowControlActive()); // 7 on queue
+ msgs.push_back(createMessage(10));
+ flow->enqueued(msgs.back());
+ BOOST_CHECK(flow->isFlowControlActive()); // 8 on queue, ON
+ msgs.push_back(createMessage(10));
+ flow->enqueued(msgs.back());
+ BOOST_CHECK(flow->isFlowControlActive()); // 9 on queue, no change to flow control
+
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 8 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 7 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 6 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 5 on queue, no change
+
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive()); // 4 on queue, OFF
+}
+
+
+QPID_AUTO_TEST_CASE(testFlowSize)
+{
+ FieldTable args;
+ args.setUInt64(QueueFlowLimit::flowStopSizeKey, 70);
+ args.setUInt64(QueueFlowLimit::flowResumeSizeKey, 50);
+
+ std::auto_ptr<TestFlow> flow(TestFlow::createTestFlow(args));
+
+ BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowResumeCount());
+ BOOST_CHECK_EQUAL((uint32_t) 70, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint32_t) 50, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+
+ std::deque<QueuedMessage> msgs;
+ for (size_t i = 0; i < 6; i++) {
+ msgs.push_back(createMessage(10));
+ flow->enqueued(msgs.back());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ }
+ BOOST_CHECK(!flow->isFlowControlActive()); // 60 on queue
+ BOOST_CHECK_EQUAL(6u, flow->getFlowCount());
+ BOOST_CHECK_EQUAL(60u, flow->getFlowSize());
+
+ QueuedMessage msg_9 = createMessage(9);
+ flow->enqueued(msg_9);
+ BOOST_CHECK(!flow->isFlowControlActive()); // 69 on queue
+ QueuedMessage tinyMsg_1 = createMessage(1);
+ flow->enqueued(tinyMsg_1);
+ BOOST_CHECK(!flow->isFlowControlActive()); // 70 on queue
+
+ QueuedMessage tinyMsg_2 = createMessage(1);
+ flow->enqueued(tinyMsg_2);
+ BOOST_CHECK(flow->isFlowControlActive()); // 71 on queue, ON
+ msgs.push_back(createMessage(10));
+ flow->enqueued(msgs.back());
+ BOOST_CHECK(flow->isFlowControlActive()); // 81 on queue
+ BOOST_CHECK_EQUAL(10u, flow->getFlowCount());
+ BOOST_CHECK_EQUAL(81u, flow->getFlowSize());
+
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 71 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 61 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // 51 on queue
+
+ flow->dequeued(tinyMsg_1);
+ BOOST_CHECK(flow->isFlowControlActive()); // 50 on queue
+ flow->dequeued(tinyMsg_2);
+ BOOST_CHECK(!flow->isFlowControlActive()); // 49 on queue, OFF
+
+ flow->dequeued(msg_9);
+ BOOST_CHECK(!flow->isFlowControlActive()); // 40 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive()); // 30 on queue
+ flow->dequeued(msgs.front());
+ msgs.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive()); // 20 on queue
+ BOOST_CHECK_EQUAL(2u, flow->getFlowCount());
+ BOOST_CHECK_EQUAL(20u, flow->getFlowSize());
+}
+
+QPID_AUTO_TEST_CASE(testFlowArgs)
+{
+ FieldTable args;
+ const uint64_t stop(0x2FFFFFFFFull);
+ const uint64_t resume(0x1FFFFFFFFull);
+ args.setInt(QueueFlowLimit::flowStopCountKey, 30);
+ args.setInt(QueueFlowLimit::flowResumeCountKey, 21);
+ args.setUInt64(QueueFlowLimit::flowStopSizeKey, stop);
+ args.setUInt64(QueueFlowLimit::flowResumeSizeKey, resume);
+
+ std::auto_ptr<TestFlow> flow(TestFlow::createTestFlow(args));
+
+ BOOST_CHECK_EQUAL((uint32_t) 30, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL((uint32_t) 21, flow->getFlowResumeCount());
+ BOOST_CHECK_EQUAL(stop, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL(resume, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+}
+
+
+QPID_AUTO_TEST_CASE(testFlowCombo)
+{
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopCountKey, 10);
+ args.setInt(QueueFlowLimit::flowResumeCountKey, 5);
+ args.setUInt64(QueueFlowLimit::flowStopSizeKey, 200);
+ args.setUInt64(QueueFlowLimit::flowResumeSizeKey, 100);
+
+ std::deque<QueuedMessage> msgs_1;
+ std::deque<QueuedMessage> msgs_10;
+ std::deque<QueuedMessage> msgs_50;
+ std::deque<QueuedMessage> msgs_100;
+
+ QueuedMessage msg;
+
+ std::auto_ptr<TestFlow> flow(TestFlow::createTestFlow(args));
+ BOOST_CHECK(!flow->isFlowControlActive()); // count:0 size:0
+
+ // verify flow control comes ON when only count passes its stop point.
+
+ for (size_t i = 0; i < 10; i++) {
+ msgs_10.push_back(createMessage(10));
+ flow->enqueued(msgs_10.back());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ }
+ // count:10 size:100
+
+ msgs_1.push_back(createMessage(1));
+ flow->enqueued(msgs_1.back()); // count:11 size: 101 ->ON
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ for (size_t i = 0; i < 6; i++) {
+ flow->dequeued(msgs_10.front());
+ msgs_10.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive());
+ }
+ // count:5 size: 41
+
+ flow->dequeued(msgs_1.front()); // count: 4 size: 40 ->OFF
+ msgs_1.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive());
+
+ for (size_t i = 0; i < 4; i++) {
+ flow->dequeued(msgs_10.front());
+ msgs_10.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive());
+ }
+ // count:0 size:0
+
+ // verify flow control comes ON when only size passes its stop point.
+
+ msgs_100.push_back(createMessage(100));
+ flow->enqueued(msgs_100.back()); // count:1 size: 100
+ BOOST_CHECK(!flow->isFlowControlActive());
+
+ msgs_50.push_back(createMessage(50));
+ flow->enqueued(msgs_50.back()); // count:2 size: 150
+ BOOST_CHECK(!flow->isFlowControlActive());
+
+ msgs_50.push_back(createMessage(50));
+ flow->enqueued(msgs_50.back()); // count:3 size: 200
+ BOOST_CHECK(!flow->isFlowControlActive());
+
+ msgs_1.push_back(createMessage(1));
+ flow->enqueued(msgs_1.back()); // count:4 size: 201 ->ON
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ flow->dequeued(msgs_100.front()); // count:3 size:101
+ msgs_100.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ flow->dequeued(msgs_1.front()); // count:2 size:100
+ msgs_1.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ flow->dequeued(msgs_50.front()); // count:1 size:50 ->OFF
+ msgs_50.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive());
+
+ // verify flow control remains ON until both thresholds drop below their
+ // resume point.
+
+ for (size_t i = 0; i < 8; i++) {
+ msgs_10.push_back(createMessage(10));
+ flow->enqueued(msgs_10.back());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ }
+ // count:9 size:130
+
+ msgs_10.push_back(createMessage(10));
+ flow->enqueued(msgs_10.back()); // count:10 size: 140
+ BOOST_CHECK(!flow->isFlowControlActive());
+
+ msgs_1.push_back(createMessage(1));
+ flow->enqueued(msgs_1.back()); // count:11 size: 141 ->ON
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ msgs_100.push_back(createMessage(100));
+ flow->enqueued(msgs_100.back()); // count:12 size: 241 (both thresholds crossed)
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ // at this point: 9@10 + 1@50 + 1@100 + 1@1 == 12@241
+
+ flow->dequeued(msgs_50.front()); // count:11 size:191
+ msgs_50.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive());
+
+ for (size_t i = 0; i < 9; i++) {
+ flow->dequeued(msgs_10.front());
+ msgs_10.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive());
+ }
+ // count:2 size:101
+ flow->dequeued(msgs_1.front()); // count:1 size:100
+ msgs_1.pop_front();
+ BOOST_CHECK(flow->isFlowControlActive()); // still active due to size
+
+ flow->dequeued(msgs_100.front()); // count:0 size:0 ->OFF
+ msgs_100.pop_front();
+ BOOST_CHECK(!flow->isFlowControlActive());
+}
+
+
+QPID_AUTO_TEST_CASE(testFlowDefaultArgs)
+{
+ QueueFlowLimit::setDefaults(2950001, // max queue byte count
+ 80, // 80% stop threshold
+ 70); // 70% resume threshold
+ FieldTable args;
+ QueueFlowLimit *ptr = TestFlow::getQueueFlowLimit(args);
+
+ BOOST_CHECK(ptr);
+ std::auto_ptr<QueueFlowLimit> flow(ptr);
+ BOOST_CHECK_EQUAL((uint64_t) 2360001, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint64_t) 2065000, flow->getFlowResumeSize());
+ BOOST_CHECK_EQUAL( 0u, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL( 0u, flow->getFlowResumeCount());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+}
+
+
+QPID_AUTO_TEST_CASE(testFlowOverrideArgs)
+{
+ QueueFlowLimit::setDefaults(2950001, // max queue byte count
+ 80, // 80% stop threshold
+ 70); // 70% resume threshold
+ {
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopCountKey, 35000);
+ args.setInt(QueueFlowLimit::flowResumeCountKey, 30000);
+
+ QueueFlowLimit *ptr = TestFlow::getQueueFlowLimit(args);
+ BOOST_CHECK(ptr);
+ std::auto_ptr<QueueFlowLimit> flow(ptr);
+
+ BOOST_CHECK_EQUAL((uint32_t) 35000, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL((uint32_t) 30000, flow->getFlowResumeCount());
+ BOOST_CHECK_EQUAL((uint64_t) 0, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint64_t) 0, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+ }
+ {
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopSizeKey, 350000);
+ args.setInt(QueueFlowLimit::flowResumeSizeKey, 300000);
+
+ QueueFlowLimit *ptr = TestFlow::getQueueFlowLimit(args);
+ BOOST_CHECK(ptr);
+ std::auto_ptr<QueueFlowLimit> flow(ptr);
+
+ BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL((uint32_t) 0, flow->getFlowResumeCount());
+ BOOST_CHECK_EQUAL((uint64_t) 350000, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint64_t) 300000, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+ }
+ {
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopCountKey, 35000);
+ args.setInt(QueueFlowLimit::flowResumeCountKey, 30000);
+ args.setInt(QueueFlowLimit::flowStopSizeKey, 350000);
+ args.setInt(QueueFlowLimit::flowResumeSizeKey, 300000);
+
+ QueueFlowLimit *ptr = TestFlow::getQueueFlowLimit(args);
+ BOOST_CHECK(ptr);
+ std::auto_ptr<QueueFlowLimit> flow(ptr);
+
+ BOOST_CHECK_EQUAL((uint32_t) 35000, flow->getFlowStopCount());
+ BOOST_CHECK_EQUAL((uint32_t) 30000, flow->getFlowResumeCount());
+ BOOST_CHECK_EQUAL((uint64_t) 350000, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint64_t) 300000, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+ }
+}
+
+
+QPID_AUTO_TEST_CASE(testFlowOverrideDefaults)
+{
+ QueueFlowLimit::setDefaults(2950001, // max queue byte count
+ 97, // stop threshold
+ 73); // resume threshold
+ FieldTable args;
+ QueueFlowLimit *ptr = TestFlow::getQueueFlowLimit(args);
+ BOOST_CHECK(ptr);
+ std::auto_ptr<QueueFlowLimit> flow(ptr);
+
+ BOOST_CHECK_EQUAL((uint32_t) 2861501, flow->getFlowStopSize());
+ BOOST_CHECK_EQUAL((uint32_t) 2153500, flow->getFlowResumeSize());
+ BOOST_CHECK(!flow->isFlowControlActive());
+ BOOST_CHECK(flow->monitorFlowControl());
+}
+
+
+QPID_AUTO_TEST_CASE(testFlowDisable)
+{
+ {
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopCountKey, 0);
+ QueueFlowLimit *ptr = TestFlow::getQueueFlowLimit(args);
+ BOOST_CHECK(!ptr);
+ }
+ {
+ FieldTable args;
+ args.setInt(QueueFlowLimit::flowStopSizeKey, 0);
+ QueueFlowLimit *ptr = TestFlow::getQueueFlowLimit(args);
+ BOOST_CHECK(!ptr);
+ }
+}
+
+
+QPID_AUTO_TEST_SUITE_END()
+
+}} // namespace qpid::tests
diff --git a/cpp/src/tests/QueuePolicyTest.cpp b/cpp/src/tests/QueuePolicyTest.cpp
index 90af9c7dd9..f735e09449 100644
--- a/cpp/src/tests/QueuePolicyTest.cpp
+++ b/cpp/src/tests/QueuePolicyTest.cpp
@@ -23,6 +23,7 @@
#include "test_tools.h"
#include "qpid/broker/QueuePolicy.h"
+#include "qpid/broker/QueueFlowLimit.h"
#include "qpid/client/QueueOptions.h"
#include "qpid/sys/Time.h"
#include "qpid/framing/reply_exceptions.h"
@@ -38,6 +39,7 @@ namespace tests {
QPID_AUTO_TEST_SUITE(QueuePolicyTestSuite)
+namespace {
QueuedMessage createMessage(uint32_t size)
{
QueuedMessage msg;
@@ -45,7 +47,7 @@ QueuedMessage createMessage(uint32_t size)
MessageUtils::addContent(msg.payload, std::string (size, 'x'));
return msg;
}
-
+}
QPID_AUTO_TEST_CASE(testCount)
{
@@ -150,7 +152,7 @@ QPID_AUTO_TEST_CASE(testRingPolicyCount)
std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::RING);
policy->update(args);
- ProxySessionFixture f;
+ SessionFixture f;
std::string q("my-ring-queue");
f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
for (int i = 0; i < 10; i++) {
@@ -185,7 +187,7 @@ QPID_AUTO_TEST_CASE(testRingPolicySize)
std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy("test", 0, 500, QueuePolicy::RING);
policy->update(args);
- ProxySessionFixture f;
+ SessionFixture f;
std::string q("my-ring-queue");
f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
@@ -257,7 +259,7 @@ QPID_AUTO_TEST_CASE(testStrictRingPolicy)
std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::RING_STRICT);
policy->update(args);
- ProxySessionFixture f;
+ SessionFixture f;
std::string q("my-ring-queue");
f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
LocalQueue incoming;
@@ -283,7 +285,7 @@ QPID_AUTO_TEST_CASE(testPolicyWithDtx)
std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::REJECT);
policy->update(args);
- ProxySessionFixture f;
+ SessionFixture f;
std::string q("my-policy-queue");
f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
LocalQueue incoming;
@@ -340,8 +342,10 @@ QPID_AUTO_TEST_CASE(testFlowToDiskWithNoStore)
//fallback to rejecting messages
QueueOptions args;
args.setSizePolicy(FLOW_TO_DISK, 0, 5);
+ // Disable flow control, or else we'll never hit the max limit
+ args.setInt(QueueFlowLimit::flowStopCountKey, 0);
- ProxySessionFixture f;
+ SessionFixture f;
std::string q("my-queue");
f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
LocalQueue incoming;
@@ -367,7 +371,7 @@ QPID_AUTO_TEST_CASE(testPolicyFailureOnCommit)
std::auto_ptr<QueuePolicy> policy = QueuePolicy::createQueuePolicy("test", 5, 0, QueuePolicy::REJECT);
policy->update(args);
- ProxySessionFixture f;
+ SessionFixture f;
std::string q("q");
f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
f.session.txSelect();
@@ -382,8 +386,9 @@ QPID_AUTO_TEST_CASE(testCapacityConversion)
{
FieldTable args;
args.setString("qpid.max_count", "5");
+ args.setString("qpid.flow_stop_count", "0");
- ProxySessionFixture f;
+ SessionFixture f;
std::string q("q");
f.session.queueDeclare(arg::queue=q, arg::exclusive=true, arg::autoDelete=true, arg::arguments=args);
for (int i = 0; i < 5; i++) {
diff --git a/cpp/src/tests/QueueTest.cpp b/cpp/src/tests/QueueTest.cpp
index 80c69ac386..aaa2721021 100644
--- a/cpp/src/tests/QueueTest.cpp
+++ b/cpp/src/tests/QueueTest.cpp
@@ -36,6 +36,9 @@
#include "qpid/framing/AMQFrame.h"
#include "qpid/framing/MessageTransferBody.h"
#include "qpid/framing/reply_exceptions.h"
+#include "qpid/broker/QueuePolicy.h"
+#include "qpid/broker/QueueFlowLimit.h"
+
#include <iostream>
#include "boost/format.hpp"
@@ -53,12 +56,12 @@ class TestConsumer : public virtual Consumer{
public:
typedef boost::shared_ptr<TestConsumer> shared_ptr;
- intrusive_ptr<Message> last;
+ QueuedMessage last;
bool received;
- TestConsumer(bool acquire = true):Consumer(acquire), received(false) {};
+ TestConsumer(std::string name="test", bool acquire = true):Consumer(name, acquire), received(false) {};
virtual bool deliver(QueuedMessage& msg){
- last = msg.payload;
+ last = msg;
received = true;
return true;
};
@@ -78,13 +81,14 @@ public:
Message& getMessage() { return *(msg.get()); }
};
-intrusive_ptr<Message> create_message(std::string exchange, std::string routingKey) {
+intrusive_ptr<Message> create_message(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()));
msg->getFrames().append(method);
msg->getFrames().append(header);
msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey);
+ if (ttl) msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setTtl(ttl);
return msg;
}
@@ -145,16 +149,16 @@ QPID_AUTO_TEST_CASE(testConsumers){
queue->deliver(msg1);
BOOST_CHECK(queue->dispatch(c1));
- BOOST_CHECK_EQUAL(msg1.get(), c1->last.get());
+ BOOST_CHECK_EQUAL(msg1.get(), c1->last.payload.get());
queue->deliver(msg2);
BOOST_CHECK(queue->dispatch(c2));
- BOOST_CHECK_EQUAL(msg2.get(), c2->last.get());
+ BOOST_CHECK_EQUAL(msg2.get(), c2->last.payload.get());
c1->received = false;
queue->deliver(msg3);
BOOST_CHECK(queue->dispatch(c1));
- BOOST_CHECK_EQUAL(msg3.get(), c1->last.get());
+ BOOST_CHECK_EQUAL(msg3.get(), c1->last.payload.get());
//Test cancellation:
queue->cancel(c1);
@@ -210,7 +214,7 @@ QPID_AUTO_TEST_CASE(testDequeue){
if (!consumer->received)
sleep(2);
- BOOST_CHECK_EQUAL(msg3.get(), consumer->last.get());
+ BOOST_CHECK_EQUAL(msg3.get(), consumer->last.payload.get());
BOOST_CHECK_EQUAL(uint32_t(0), queue->getMessageCount());
received = queue->get().payload;
@@ -244,7 +248,7 @@ QPID_AUTO_TEST_CASE(testBound){
exchange2.reset();
//unbind the queue from all exchanges it knows it has been bound to:
- queue->unbind(exchanges, queue);
+ queue->unbind(exchanges);
//ensure the remaining exchanges don't still have the queue bound to them:
FailOnDeliver deliverable;
@@ -254,26 +258,26 @@ QPID_AUTO_TEST_CASE(testBound){
QPID_AUTO_TEST_CASE(testPersistLastNodeStanding){
client::QueueOptions args;
- args.setPersistLastNode();
+ args.setPersistLastNode();
- Queue::shared_ptr queue(new Queue("my-queue", true));
+ 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");
- //enqueue 2 messages
+ //enqueue 2 messages
queue->deliver(msg1);
queue->deliver(msg2);
- //change mode
- queue->setLastNodeFailure();
+ //change mode
+ queue->setLastNodeFailure();
- //enqueue 1 message
+ //enqueue 1 message
queue->deliver(msg3);
- //check all have persistent ids.
+ //check all have persistent ids.
BOOST_CHECK(msg1->isPersistent());
BOOST_CHECK(msg2->isPersistent());
BOOST_CHECK(msg3->isPersistent());
@@ -283,54 +287,58 @@ QPID_AUTO_TEST_CASE(testPersistLastNodeStanding){
QPID_AUTO_TEST_CASE(testSeek){
- Queue::shared_ptr queue(new Queue("my-queue", true));
+ 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");
- //enqueue 2 messages
+ //enqueue 2 messages
queue->deliver(msg1);
queue->deliver(msg2);
queue->deliver(msg3);
- TestConsumer::shared_ptr consumer(new TestConsumer(false));
+ TestConsumer::shared_ptr consumer(new TestConsumer("test", false));
SequenceNumber seq(2);
consumer->position = seq;
QueuedMessage qm;
queue->dispatch(consumer);
-
- BOOST_CHECK_EQUAL(msg3.get(), consumer->last.get());
+
+ BOOST_CHECK_EQUAL(msg3.get(), consumer->last.payload.get());
queue->dispatch(consumer);
queue->dispatch(consumer); // make sure over-run is safe
-
+
}
QPID_AUTO_TEST_CASE(testSearch){
- Queue::shared_ptr queue(new Queue("my-queue", true));
+ 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");
- //enqueue 2 messages
+ //enqueue 2 messages
queue->deliver(msg1);
queue->deliver(msg2);
queue->deliver(msg3);
SequenceNumber seq(2);
- QueuedMessage qm = queue->find(seq);
-
+ QueuedMessage qm;
+ TestConsumer::shared_ptr c1(new TestConsumer());
+
+ BOOST_CHECK(queue->find(seq, qm));
+
BOOST_CHECK_EQUAL(seq.getValue(), qm.position.getValue());
-
- queue->acquire(qm);
+
+ queue->acquire(qm, c1->getName());
BOOST_CHECK_EQUAL(queue->getMessageCount(), 2u);
SequenceNumber seq1(3);
- QueuedMessage qm1 = queue->find(seq1);
+ QueuedMessage qm1;
+ BOOST_CHECK(queue->find(seq1, qm1));
BOOST_CHECK_EQUAL(seq1.getValue(), qm1.position.getValue());
-
+
}
const std::string nullxid = "";
@@ -416,10 +424,10 @@ QPID_AUTO_TEST_CASE(testLVQOrdering){
client::QueueOptions args;
// set queue mode
- args.setOrdering(client::LVQ);
+ args.setOrdering(client::LVQ);
- Queue::shared_ptr queue(new Queue("my-queue", true ));
- queue->configure(args);
+ 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");
@@ -430,16 +438,16 @@ QPID_AUTO_TEST_CASE(testLVQOrdering){
//set deliever match for LVQ a,b,c,a
string key;
- args.getLVQKey(key);
+ args.getLVQKey(key);
BOOST_CHECK_EQUAL(key, "qpid.LVQ_key");
- msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
- msg2->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"b");
- msg3->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"c");
- msg4->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+ msg1->insertCustomProperty(key,"a");
+ msg2->insertCustomProperty(key,"b");
+ msg3->insertCustomProperty(key,"c");
+ msg4->insertCustomProperty(key,"a");
- //enqueue 4 message
+ //enqueue 4 message
queue->deliver(msg1);
queue->deliver(msg2);
queue->deliver(msg3);
@@ -459,9 +467,9 @@ QPID_AUTO_TEST_CASE(testLVQOrdering){
intrusive_ptr<Message> msg5 = create_message("e", "A");
intrusive_ptr<Message> msg6 = create_message("e", "B");
intrusive_ptr<Message> msg7 = create_message("e", "C");
- msg5->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
- msg6->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"b");
- msg7->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"c");
+ msg5->insertCustomProperty(key,"a");
+ msg6->insertCustomProperty(key,"b");
+ msg7->insertCustomProperty(key,"c");
queue->deliver(msg5);
queue->deliver(msg6);
queue->deliver(msg7);
@@ -496,7 +504,7 @@ QPID_AUTO_TEST_CASE(testLVQEmptyKey){
BOOST_CHECK_EQUAL(key, "qpid.LVQ_key");
- msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+ msg1->insertCustomProperty(key,"a");
queue->deliver(msg1);
queue->deliver(msg2);
BOOST_CHECK_EQUAL(queue->getMessageCount(), 2u);
@@ -508,6 +516,8 @@ QPID_AUTO_TEST_CASE(testLVQAcquire){
client::QueueOptions args;
// set queue mode
args.setOrdering(client::LVQ);
+ // disable flow control, as this test violates the enqueue/dequeue sequence.
+ args.setInt(QueueFlowLimit::flowStopCountKey, 0);
Queue::shared_ptr queue(new Queue("my-queue", true ));
queue->configure(args);
@@ -526,12 +536,12 @@ QPID_AUTO_TEST_CASE(testLVQAcquire){
BOOST_CHECK_EQUAL(key, "qpid.LVQ_key");
- msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
- msg2->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"b");
- msg3->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"c");
- msg4->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
- msg5->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"b");
- msg6->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"c");
+ msg1->insertCustomProperty(key,"a");
+ msg2->insertCustomProperty(key,"b");
+ msg3->insertCustomProperty(key,"c");
+ msg4->insertCustomProperty(key,"a");
+ msg5->insertCustomProperty(key,"b");
+ msg6->insertCustomProperty(key,"c");
//enqueue 4 message
queue->deliver(msg1);
@@ -546,12 +556,13 @@ QPID_AUTO_TEST_CASE(testLVQAcquire){
QueuedMessage qmsg2(queue.get(), msg2, ++sequence);
framing::SequenceNumber sequence1(10);
QueuedMessage qmsg3(queue.get(), 0, sequence1);
+ TestConsumer::shared_ptr dummy(new TestConsumer());
- BOOST_CHECK(!queue->acquire(qmsg));
- BOOST_CHECK(queue->acquire(qmsg2));
+ BOOST_CHECK(!queue->acquire(qmsg, dummy->getName()));
+ BOOST_CHECK(queue->acquire(qmsg2, dummy->getName()));
// Acquire the massage again to test failure case.
- BOOST_CHECK(!queue->acquire(qmsg2));
- BOOST_CHECK(!queue->acquire(qmsg3));
+ BOOST_CHECK(!queue->acquire(qmsg2, dummy->getName()));
+ BOOST_CHECK(!queue->acquire(qmsg3, dummy->getName()));
BOOST_CHECK_EQUAL(queue->getMessageCount(), 2u);
@@ -561,7 +572,7 @@ QPID_AUTO_TEST_CASE(testLVQAcquire){
// set mode to no browse and check
args.setOrdering(client::LVQ_NO_BROWSE);
queue->configure(args);
- TestConsumer::shared_ptr c1(new TestConsumer(false));
+ TestConsumer::shared_ptr c1(new TestConsumer("test", false));
queue->dispatch(c1);
queue->dispatch(c1);
@@ -595,8 +606,8 @@ QPID_AUTO_TEST_CASE(testLVQMultiQueue){
args.getLVQKey(key);
BOOST_CHECK_EQUAL(key, "qpid.LVQ_key");
- msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
- msg2->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
+ msg1->insertCustomProperty(key,"a");
+ msg2->insertCustomProperty(key,"a");
queue1->deliver(msg1);
queue2->deliver(msg1);
@@ -630,7 +641,7 @@ QPID_AUTO_TEST_CASE(testLVQRecover){
Queue::shared_ptr queue1(new Queue("my-queue", true, &testStore));
intrusive_ptr<Message> received;
- queue1->configure(args);
+ queue1->create(args);
intrusive_ptr<Message> msg1 = create_message("e", "A");
intrusive_ptr<Message> msg2 = create_message("e", "A");
@@ -639,9 +650,9 @@ QPID_AUTO_TEST_CASE(testLVQRecover){
args.getLVQKey(key);
BOOST_CHECK_EQUAL(key, "qpid.LVQ_key");
- msg1->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
- msg2->getProperties<MessageProperties>()->getApplicationHeaders().setString(key,"a");
- // 3
+ msg1->insertCustomProperty(key,"a");
+ msg2->insertCustomProperty(key,"a");
+ // 3
queue1->deliver(msg1);
// 4
queue1->setLastNodeFailure();
@@ -660,13 +671,8 @@ 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");
- if (i % 2) {
- if (oddTtl) m->getProperties<DeliveryProperties>()->setTtl(oddTtl);
- } else {
- if (evenTtl) m->getProperties<DeliveryProperties>()->setTtl(evenTtl);
- }
- m->setTimestamp(new broker::ExpiryPolicy);
+ intrusive_ptr<Message> m = create_message("exchange", "key", i % 2 ? oddTtl : evenTtl);
+ m->computeExpiration(new broker::ExpiryPolicy);
queue.deliver(m);
}
}
@@ -676,7 +682,7 @@ QPID_AUTO_TEST_CASE(testPurgeExpired) {
addMessagesToQueue(10, queue);
BOOST_CHECK_EQUAL(queue.getMessageCount(), 10u);
::usleep(300*1000);
- queue.purgeExpired();
+ queue.purgeExpired(0);
BOOST_CHECK_EQUAL(queue.getMessageCount(), 5u);
}
@@ -687,7 +693,7 @@ QPID_AUTO_TEST_CASE(testQueueCleaner) {
addMessagesToQueue(10, *queue, 200, 400);
BOOST_CHECK_EQUAL(queue->getMessageCount(), 10u);
- QueueCleaner cleaner(queues, timer);
+ QueueCleaner cleaner(queues, &timer);
cleaner.start(100 * qpid::sys::TIME_MSEC);
::usleep(300*1000);
BOOST_CHECK_EQUAL(queue->getMessageCount(), 5u);
@@ -695,6 +701,280 @@ QPID_AUTO_TEST_CASE(testQueueCleaner) {
BOOST_CHECK_EQUAL(queue->getMessageCount(), 0u);
}
+
+namespace {
+ // helper for group tests
+ void verifyAcquire( Queue::shared_ptr queue,
+ TestConsumer::shared_ptr c,
+ std::deque<QueuedMessage>& results,
+ const std::string& expectedGroup,
+ const int expectedId )
+ {
+ queue->dispatch(c);
+ results.push_back(c->last);
+ std::string group = c->last.payload->getProperties<MessageProperties>()->getApplicationHeaders().getAsString("GROUP-ID");
+ int id = c->last.payload->getProperties<MessageProperties>()->getApplicationHeaders().getAsInt("MY-ID");
+ BOOST_CHECK_EQUAL( group, expectedGroup );
+ BOOST_CHECK_EQUAL( id, expectedId );
+ }
+}
+
+QPID_AUTO_TEST_CASE(testGroupsMultiConsumer) {
+ //
+ // Verify that consumers of grouped messages own the groups once a message is acquired,
+ // and release the groups once all acquired messages have been dequeued or requeued
+ //
+ FieldTable args;
+ Queue::shared_ptr queue(new Queue("my_queue", true));
+ args.setString("qpid.group_header_key", "GROUP-ID");
+ args.setInt("qpid.shared_msg_group", 1);
+ queue->configure(args);
+
+ std::string groups[] = { std::string("a"), std::string("a"), std::string("a"),
+ 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");
+ msg->insertCustomProperty("GROUP-ID", groups[i]);
+ msg->insertCustomProperty("MY-ID", i);
+ queue->deliver(msg);
+ }
+
+ // Queue = a-0, a-1, a-2, b-3, b-4, b-5, c-6, c-7, c-8...
+ // Owners= ---, ---, ---, ---, ---, ---, ---, ---, ---,
+
+ BOOST_CHECK_EQUAL(uint32_t(9), queue->getMessageCount());
+
+ TestConsumer::shared_ptr c1(new TestConsumer("C1"));
+ TestConsumer::shared_ptr c2(new TestConsumer("C2"));
+
+ queue->consume(c1);
+ queue->consume(c2);
+
+ std::deque<QueuedMessage> dequeMeC1;
+ std::deque<QueuedMessage> dequeMeC2;
+
+
+ verifyAcquire(queue, c1, dequeMeC1, "a", 0 ); // c1 now owns group "a" (acquire a-0)
+ verifyAcquire(queue, c2, dequeMeC2, "b", 3 ); // c2 should now own group "b" (acquire b-3)
+
+ // now let c1 complete the 'a-0' message - this should free the 'a' group
+ queue->dequeue( 0, dequeMeC1.front() );
+ dequeMeC1.pop_front();
+
+ // Queue = a-1, a-2, b-3, b-4, b-5, c-6, c-7, c-8...
+ // Owners= ---, ---, ^C2, ^C2, ^C2, ---, ---, ---
+
+ // now c2 should pick up the next 'a-1', since it is oldest free
+ verifyAcquire(queue, c2, dequeMeC2, "a", 1 ); // c2 should now own groups "a" and "b"
+
+ // Queue = a-1, a-2, b-3, b-4, b-5, c-6, c-7, c-8...
+ // Owners= ^C2, ^C2, ^C2, ^C2, ^C2, ---, ---, ---
+
+ // c1 should only be able to snarf up the first "c" message now...
+ verifyAcquire(queue, c1, dequeMeC1, "c", 6 ); // should skip to the first "c"
+
+ // Queue = a-1, a-2, b-3, b-4, b-5, c-6, c-7, c-8...
+ // Owners= ^C2, ^C2, ^C2, ^C2, ^C2, ^C1, ^C1, ^C1
+
+ // hmmm... what if c2 now dequeues "b-3"? (now only has a-1 acquired)
+ queue->dequeue( 0, dequeMeC2.front() );
+ dequeMeC2.pop_front();
+
+ // Queue = a-1, a-2, b-4, b-5, c-6, c-7, c-8...
+ // Owners= ^C2, ^C2, ---, ---, ^C1, ^C1, ^C1
+
+ // b group is free, c is owned by c1 - c1's next get should grab 'b-4'
+ verifyAcquire(queue, c1, dequeMeC1, "b", 4 );
+
+ // Queue = a-1, a-2, b-4, b-5, c-6, c-7, c-8...
+ // Owners= ^C2, ^C2, ^C1, ^C1, ^C1, ^C1, ^C1
+
+ // c2 can now only grab a-2, and that's all
+ verifyAcquire(queue, c2, dequeMeC2, "a", 2 );
+
+ // now C2 can't get any more, since C1 owns "b" and "c" group...
+ bool gotOne = queue->dispatch(c2);
+ BOOST_CHECK( !gotOne );
+
+ // hmmm... what if c1 now dequeues "c-6"? (now only own's b-4)
+ queue->dequeue( 0, dequeMeC1.front() );
+ dequeMeC1.pop_front();
+
+ // Queue = a-1, a-2, b-4, b-5, c-7, c-8...
+ // Owners= ^C2, ^C2, ^C1, ^C1, ---, ---
+
+ // c2 can now grab c-7
+ verifyAcquire(queue, c2, dequeMeC2, "c", 7 );
+
+ // Queue = a-1, a-2, b-4, b-5, c-7, c-8...
+ // Owners= ^C2, ^C2, ^C1, ^C1, ^C2, ^C2
+
+ // what happens if C-2 "requeues" a-1 and a-2?
+ queue->requeue( dequeMeC2.front() );
+ dequeMeC2.pop_front();
+ queue->requeue( dequeMeC2.front() );
+ dequeMeC2.pop_front(); // now just has c-7 acquired
+
+ // Queue = a-1, a-2, b-4, b-5, c-7, c-8...
+ // Owners= ---, ---, ^C1, ^C1, ^C2, ^C2
+
+ // now c1 will grab a-1 and a-2...
+ verifyAcquire(queue, c1, dequeMeC1, "a", 1 );
+ verifyAcquire(queue, c1, dequeMeC1, "a", 2 );
+
+ // Queue = a-1, a-2, b-4, b-5, c-7, c-8...
+ // Owners= ^C1, ^C1, ^C1, ^C1, ^C2, ^C2
+
+ // c2 can now acquire c-8 only
+ verifyAcquire(queue, c2, dequeMeC2, "c", 8 );
+
+ // and c1 can get b-5
+ verifyAcquire(queue, c1, dequeMeC1, "b", 5 );
+
+ // should be no more acquire-able for anyone now:
+ gotOne = queue->dispatch(c1);
+ BOOST_CHECK( !gotOne );
+ gotOne = queue->dispatch(c2);
+ BOOST_CHECK( !gotOne );
+
+ // requeue all of C1's acquired messages, then cancel C1
+ while (!dequeMeC1.empty()) {
+ queue->requeue(dequeMeC1.front());
+ dequeMeC1.pop_front();
+ }
+ queue->cancel(c1);
+
+ // Queue = a-1, a-2, b-4, b-5, c-7, c-8...
+ // Owners= ---, ---, ---, ---, ^C2, ^C2
+
+ // b-4, a-1, a-2, b-5 all should be available, right?
+ verifyAcquire(queue, c2, dequeMeC2, "a", 1 );
+
+ while (!dequeMeC2.empty()) {
+ queue->dequeue(0, dequeMeC2.front());
+ dequeMeC2.pop_front();
+ }
+
+ // Queue = a-2, b-4, b-5
+ // Owners= ---, ---, ---
+
+ TestConsumer::shared_ptr c3(new TestConsumer("C3"));
+ std::deque<QueuedMessage> dequeMeC3;
+
+ verifyAcquire(queue, c3, dequeMeC3, "a", 2 );
+ verifyAcquire(queue, c2, dequeMeC2, "b", 4 );
+
+ // Queue = a-2, b-4, b-5
+ // Owners= ^C3, ^C2, ^C2
+
+ gotOne = queue->dispatch(c3);
+ BOOST_CHECK( !gotOne );
+
+ verifyAcquire(queue, c2, dequeMeC2, "b", 5 );
+
+ while (!dequeMeC2.empty()) {
+ queue->dequeue(0, dequeMeC2.front());
+ dequeMeC2.pop_front();
+ }
+
+ // Queue = a-2,
+ // Owners= ^C3,
+
+ intrusive_ptr<Message> msg = create_message("e", "A");
+ msg->insertCustomProperty("GROUP-ID", "a");
+ msg->insertCustomProperty("MY-ID", 9);
+ queue->deliver(msg);
+
+ // Queue = a-2, a-9
+ // Owners= ^C3, ^C3
+
+ gotOne = queue->dispatch(c2);
+ BOOST_CHECK( !gotOne );
+
+ msg = create_message("e", "A");
+ msg->insertCustomProperty("GROUP-ID", "b");
+ msg->insertCustomProperty("MY-ID", 10);
+ queue->deliver(msg);
+
+ // Queue = a-2, a-9, b-10
+ // Owners= ^C3, ^C3, ----
+
+ verifyAcquire(queue, c2, dequeMeC2, "b", 10 );
+ verifyAcquire(queue, c3, dequeMeC3, "a", 9 );
+
+ gotOne = queue->dispatch(c3);
+ BOOST_CHECK( !gotOne );
+
+ queue->cancel(c2);
+ queue->cancel(c3);
+}
+
+
+QPID_AUTO_TEST_CASE(testGroupsMultiConsumerDefaults) {
+ //
+ // Verify that the same default group name is automatically applied to messages that
+ // do not specify a group name.
+ //
+ FieldTable args;
+ Queue::shared_ptr queue(new Queue("my_queue", true));
+ args.setString("qpid.group_header_key", "GROUP-ID");
+ args.setInt("qpid.shared_msg_group", 1);
+ queue->configure(args);
+
+ for (int i = 0; i < 3; ++i) {
+ intrusive_ptr<Message> msg = create_message("e", "A");
+ // no "GROUP-ID" header
+ msg->insertCustomProperty("MY-ID", i);
+ queue->deliver(msg);
+ }
+
+ // Queue = 0, 1, 2
+
+ BOOST_CHECK_EQUAL(uint32_t(3), queue->getMessageCount());
+
+ TestConsumer::shared_ptr c1(new TestConsumer("C1"));
+ TestConsumer::shared_ptr c2(new TestConsumer("C2"));
+
+ queue->consume(c1);
+ queue->consume(c2);
+
+ std::deque<QueuedMessage> dequeMeC1;
+ std::deque<QueuedMessage> dequeMeC2;
+
+ queue->dispatch(c1); // c1 now owns default group (acquired 0)
+ dequeMeC1.push_back(c1->last);
+ int id = c1->last.payload->getProperties<MessageProperties>()->getApplicationHeaders().getAsInt("MY-ID");
+ BOOST_CHECK_EQUAL( id, 0 );
+
+ bool gotOne = queue->dispatch(c2); // c2 should get nothing
+ BOOST_CHECK( !gotOne );
+
+ queue->dispatch(c1); // c1 now acquires 1
+ dequeMeC1.push_back(c1->last);
+ id = c1->last.payload->getProperties<MessageProperties>()->getApplicationHeaders().getAsInt("MY-ID");
+ BOOST_CHECK_EQUAL( id, 1 );
+
+ gotOne = queue->dispatch(c2); // c2 should still get nothing
+ BOOST_CHECK( !gotOne );
+
+ while (!dequeMeC1.empty()) {
+ queue->dequeue(0, dequeMeC1.front());
+ dequeMeC1.pop_front();
+ }
+
+ // now default group should be available...
+ queue->dispatch(c2); // c2 now owns default group (acquired 2)
+ id = c2->last.payload->getProperties<MessageProperties>()->getApplicationHeaders().getAsInt("MY-ID");
+ BOOST_CHECK_EQUAL( id, 2 );
+
+ gotOne = queue->dispatch(c1); // c1 should get nothing
+ BOOST_CHECK( !gotOne );
+
+ queue->cancel(c1);
+ queue->cancel(c2);
+}
+
QPID_AUTO_TEST_CASE(testMultiQueueLastNode){
TestMessageStoreOC testStore;
@@ -702,9 +982,9 @@ QPID_AUTO_TEST_CASE(testMultiQueueLastNode){
args.setPersistLastNode();
Queue::shared_ptr queue1(new Queue("queue1", true, &testStore ));
- queue1->configure(args);
+ queue1->create(args);
Queue::shared_ptr queue2(new Queue("queue2", true, &testStore ));
- queue2->configure(args);
+ queue2->create(args);
intrusive_ptr<Message> msg1 = create_message("e", "A");
@@ -790,7 +1070,7 @@ not requeued to the store.
Queue::shared_ptr queue1(new Queue("my-queue", true, &testStore));
intrusive_ptr<Message> received;
- queue1->configure(args);
+ queue1->create(args);
// check requeue 1
intrusive_ptr<Message> msg1 = create_message("e", "C");
@@ -870,28 +1150,40 @@ QPID_AUTO_TEST_CASE(testFlowToDiskBlocking){
intrusive_ptr<Message> msg02 = mkMsg(testStore, std::string(5, 'X')); // transient w/ content
DeliverableMessage dmsg02(msg02);
- BOOST_CHECK_THROW(sbtFanout1.route(dmsg02, "", 0), ResourceLimitExceededException);
+ {
+ ScopedSuppressLogging sl; // suppress expected error messages.
+ BOOST_CHECK_THROW(sbtFanout1.route(dmsg02, "", 0), ResourceLimitExceededException);
+ }
msg02->tryReleaseContent();
BOOST_CHECK_EQUAL(msg02->isContentReleased(), false);
BOOST_CHECK_EQUAL(1u, tq1->getMessageCount());
intrusive_ptr<Message> msg03 = mkMsg(testStore, std::string(5, 'X'), true); // durable w/ content
DeliverableMessage dmsg03(msg03);
- BOOST_CHECK_THROW(sbtFanout1.route(dmsg03, "", 0), ResourceLimitExceededException);
+ {
+ ScopedSuppressLogging sl; // suppress expected error messages.
+ BOOST_CHECK_THROW(sbtFanout1.route(dmsg03, "", 0), ResourceLimitExceededException);
+ }
msg03->tryReleaseContent();
BOOST_CHECK_EQUAL(msg03->isContentReleased(), false);
BOOST_CHECK_EQUAL(1u, tq1->getMessageCount());
intrusive_ptr<Message> msg04 = mkMsg(testStore); // transient no content
DeliverableMessage dmsg04(msg04);
- BOOST_CHECK_THROW(sbtFanout1.route(dmsg04, "", 0), ResourceLimitExceededException);
+ {
+ ScopedSuppressLogging sl; // suppress expected error messages.
+ BOOST_CHECK_THROW(sbtFanout1.route(dmsg04, "", 0), ResourceLimitExceededException);
+ }
msg04->tryReleaseContent();
BOOST_CHECK_EQUAL(msg04->isContentReleased(), false);
BOOST_CHECK_EQUAL(1u, tq1->getMessageCount());
intrusive_ptr<Message> msg05 = mkMsg(testStore, "", true); // durable no content
DeliverableMessage dmsg05(msg05);
- BOOST_CHECK_THROW(sbtFanout1.route(dmsg05, "", 0), ResourceLimitExceededException);
+ {
+ ScopedSuppressLogging sl; // suppress expected error messages.
+ BOOST_CHECK_THROW(sbtFanout1.route(dmsg05, "", 0), ResourceLimitExceededException);
+ }
msg05->tryReleaseContent();
BOOST_CHECK_EQUAL(msg05->isContentReleased(), false);
BOOST_CHECK_EQUAL(1u, tq1->getMessageCount());
diff --git a/cpp/src/tests/ReplicationTest.cpp b/cpp/src/tests/ReplicationTest.cpp
index 7310a3fe20..1219a6b59e 100644
--- a/cpp/src/tests/ReplicationTest.cpp
+++ b/cpp/src/tests/ReplicationTest.cpp
@@ -74,7 +74,7 @@ QPID_AUTO_TEST_CASE(testReplicationExchange)
{
qpid::broker::Broker::Options brokerOpts(getBrokerOpts(list_of<string>("qpidd")
("--replication-exchange-name=qpid.replication")));
- ProxySessionFixture f(brokerOpts);
+ SessionFixture f(brokerOpts);
std::string dataQ("queue-1");
diff --git a/cpp/src/tests/SessionState.cpp b/cpp/src/tests/SessionState.cpp
index 157cabfb63..3be9bb0cbc 100644
--- a/cpp/src/tests/SessionState.cpp
+++ b/cpp/src/tests/SessionState.cpp
@@ -43,7 +43,7 @@ using namespace qpid::framing;
// Apply f to [begin, end) and accumulate the result
template <class Iter, class T, class F>
T applyAccumulate(Iter begin, Iter end, T seed, const F& f) {
- return std::accumulate(begin, end, seed, bind(std::plus<T>(), _1, bind(f, _2)));
+ return std::accumulate(begin, end, seed, boost::bind(std::plus<T>(), _1, boost::bind(f, _2)));
}
// Create a frame with a one-char string.
@@ -105,8 +105,8 @@ size_t transferN(qpid::SessionState& s, string content) {
char last = content[content.size()-1];
content.resize(content.size()-1);
size += applyAccumulate(content.begin(), content.end(), 0,
- bind(&send, ref(s),
- bind(contentFrameChar, _1, false)));
+ boost::bind(&send, boost::ref(s),
+ boost::bind(contentFrameChar, _1, false)));
size += send(s, contentFrameChar(last, true));
}
return size;
@@ -115,7 +115,7 @@ size_t transferN(qpid::SessionState& s, string content) {
// Send multiple transfers with single-byte content.
size_t transfers(qpid::SessionState& s, string content) {
return applyAccumulate(content.begin(), content.end(), 0,
- bind(transfer1Char, ref(s), _1));
+ boost::bind(transfer1Char, boost::ref(s), _1));
}
size_t contentFrameSize(size_t n=1) { return AMQFrame(( AMQContentBody())).encodedSize() + n; }
diff --git a/cpp/src/tests/SocketProxy.h b/cpp/src/tests/SocketProxy.h
deleted file mode 100644
index 0c6f39d62e..0000000000
--- a/cpp/src/tests/SocketProxy.h
+++ /dev/null
@@ -1,181 +0,0 @@
-#ifndef SOCKETPROXY_H
-#define SOCKETPROXY_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/IOHandle.h"
-#ifdef _WIN32
-# include "qpid/sys/windows/IoHandlePrivate.h"
- typedef SOCKET FdType;
-#else
-# include "qpid/sys/posix/PrivatePosix.h"
- typedef int FdType;
-#endif
-#include "qpid/sys/Socket.h"
-#include "qpid/sys/Runnable.h"
-#include "qpid/sys/Thread.h"
-#include "qpid/sys/Mutex.h"
-#include "qpid/log/Statement.h"
-
-namespace qpid {
-namespace tests {
-
-/**
- * A simple socket proxy that forwards to another socket.
- * Used between client & local broker to simulate network failures.
- */
-class SocketProxy : private qpid::sys::Runnable
-{
- // Need a Socket we can get the fd from
- class LowSocket : public qpid::sys::Socket {
- public:
-#ifdef _WIN32
- FdType getFd() { return toSocketHandle(*this); }
-#else
- FdType getFd() { return toFd(impl); }
-#endif
- };
-
- public:
- /** Connect to connectPort on host, start a forwarding thread.
- * Listen for connection on getPort().
- */
- SocketProxy(int connectPort, const std::string host="localhost")
- : closed(false), joined(true),
- port(listener.listen()), dropClient(), dropServer()
- {
- client.connect(host, connectPort);
- joined = false;
- thread = qpid::sys::Thread(static_cast<qpid::sys::Runnable*>(this));
- }
-
- ~SocketProxy() { close(); if (!joined) thread.join(); }
-
- /** Simulate a network disconnect. */
- void close() {
- {
- qpid::sys::Mutex::ScopedLock l(lock);
- if (closed) { return; }
- closed=true;
- }
- if (thread && thread != qpid::sys::Thread::current()) {
- thread.join();
- joined = true;
- }
- client.close();
- }
-
- /** Simulate lost packets, drop data from client */
- void dropClientData(bool drop=true) { dropClient=drop; }
-
- /** Simulate lost packets, drop data from server */
- void dropServerData(bool drop=true) { dropServer=drop; }
-
- bool isClosed() const {
- qpid::sys::Mutex::ScopedLock l(lock);
- return closed;
- }
-
- uint16_t getPort() const { return port; }
-
- private:
- static void throwErrno(const std::string& msg) {
- throw qpid::Exception(msg+":"+qpid::sys::strError(errno));
- }
- static void throwIf(bool condition, const std::string& msg) {
- if (condition) throw qpid::Exception(msg);
- }
-
- void run() {
- std::auto_ptr<LowSocket> server;
- try {
- fd_set socks;
- FdType maxFd = listener.getFd();
- struct timeval tmo;
- for (;;) {
- FD_ZERO(&socks);
- FD_SET(maxFd, &socks);
- tmo.tv_sec = 0;
- tmo.tv_usec = 500 * 1000;
- if (select(maxFd+1, &socks, 0, 0, &tmo) == 0) {
- qpid::sys::Mutex::ScopedLock l(lock);
- throwIf(closed, "SocketProxy: Closed by close()");
- continue;
- }
- throwIf(!FD_ISSET(maxFd, &socks), "SocketProxy: Accept failed");
- break; // Accept ready... go to next step
- }
- server.reset(reinterpret_cast<LowSocket *>(listener.accept()));
- maxFd = server->getFd();
- if (client.getFd() > maxFd)
- maxFd = client.getFd();
- char buffer[1024];
- for (;;) {
- FD_ZERO(&socks);
- tmo.tv_sec = 0;
- tmo.tv_usec = 500 * 1000;
- FD_SET(client.getFd(), &socks);
- FD_SET(server->getFd(), &socks);
- if (select(maxFd+1, &socks, 0, 0, &tmo) == 0) {
- qpid::sys::Mutex::ScopedLock l(lock);
- throwIf(closed, "SocketProxy: Closed by close()");
- continue;
- }
- // Something is set; relay data as needed until something closes
- if (FD_ISSET(server->getFd(), &socks)) {
- int n = server->read(buffer, sizeof(buffer));
- throwIf(n <= 0, "SocketProxy: server disconnected");
- if (!dropServer) client.write(buffer, n);
- }
- if (FD_ISSET(client.getFd(), &socks)) {
- int n = client.read(buffer, sizeof(buffer));
- throwIf(n <= 0, "SocketProxy: client disconnected");
- if (!dropServer) server->write(buffer, n);
- }
- if (!FD_ISSET(client.getFd(), &socks) &&
- !FD_ISSET(server->getFd(), &socks))
- throwIf(true, "SocketProxy: No handle ready");
- }
- }
- catch (const std::exception& e) {
- QPID_LOG(debug, "SocketProxy::run exception: " << e.what());
- }
- try {
- if (server.get()) server->close();
- close();
- }
- catch (const std::exception& e) {
- QPID_LOG(debug, "SocketProxy::run exception in client/server close()" << e.what());
- }
- }
-
- mutable qpid::sys::Mutex lock;
- mutable bool closed;
- bool joined;
- LowSocket client, listener;
- uint16_t port;
- qpid::sys::Thread thread;
- bool dropClient, dropServer;
-};
-
-}} // namespace qpid::tests
-
-#endif
diff --git a/cpp/src/tests/TimerTest.cpp b/cpp/src/tests/TimerTest.cpp
index 7df94164e0..6a0a196f4e 100644
--- a/cpp/src/tests/TimerTest.cpp
+++ b/cpp/src/tests/TimerTest.cpp
@@ -77,8 +77,10 @@ class TestTask : public TimerTask
BOOST_CHECK(fired);
BOOST_CHECK_EQUAL(expected_position, position);
Duration actual(start, end);
-#ifdef _WIN32
+#ifdef _MSC_VER
uint64_t difference = _abs64(expected - actual);
+#elif defined(_WIN32)
+ uint64_t difference = labs(expected - actual);
#else
uint64_t difference = abs(expected - actual);
#endif
diff --git a/cpp/src/tests/TxPublishTest.cpp b/cpp/src/tests/TxPublishTest.cpp
index 6b44d95baa..152581e4ba 100644
--- a/cpp/src/tests/TxPublishTest.cpp
+++ b/cpp/src/tests/TxPublishTest.cpp
@@ -50,10 +50,9 @@ struct TxPublishTest
TxPublishTest() :
queue1(new Queue("queue1", false, &store, 0)),
queue2(new Queue("queue2", false, &store, 0)),
- msg(MessageUtils::createMessage("exchange", "routing_key", false, "id")),
+ msg(MessageUtils::createMessage("exchange", "routing_key", true)),
op(msg)
{
- msg->getProperties<DeliveryProperties>()->setDeliveryMode(PERSISTENT);
op.deliverTo(queue1);
op.deliverTo(queue2);
}
@@ -74,7 +73,7 @@ QPID_AUTO_TEST_CASE(testPrepare)
BOOST_CHECK_EQUAL(pmsg, t.store.enqueued[0].second);
BOOST_CHECK_EQUAL(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))->isEnqueueComplete());
+ BOOST_CHECK_EQUAL( true, ( boost::static_pointer_cast<PersistableMessage>(t.msg))->isIngressComplete());
}
QPID_AUTO_TEST_CASE(testCommit)
@@ -87,7 +86,7 @@ QPID_AUTO_TEST_CASE(testCommit)
BOOST_CHECK_EQUAL((uint32_t) 1, t.queue1->getMessageCount());
intrusive_ptr<Message> msg_dequeue = t.queue1->get().payload;
- BOOST_CHECK_EQUAL( true, (boost::static_pointer_cast<PersistableMessage>(msg_dequeue))->isEnqueueComplete());
+ BOOST_CHECK_EQUAL( true, (boost::static_pointer_cast<PersistableMessage>(msg_dequeue))->isIngressComplete());
BOOST_CHECK_EQUAL(t.msg, msg_dequeue);
BOOST_CHECK_EQUAL((uint32_t) 1, t.queue2->getMessageCount());
diff --git a/cpp/src/tests/Url.cpp b/cpp/src/tests/Url.cpp
index 234a62ee91..b30de682bc 100644
--- a/cpp/src/tests/Url.cpp
+++ b/cpp/src/tests/Url.cpp
@@ -60,6 +60,32 @@ QPID_AUTO_TEST_CASE(TestParseXyz) {
BOOST_CHECK_EQUAL(Url("xyz:host").str(), "amqp:xyz:host:5672");
}
+QPID_AUTO_TEST_CASE(TestParseTricky) {
+ BOOST_CHECK_EQUAL(Url("amqp").str(), "amqp:tcp:amqp:5672");
+ BOOST_CHECK_EQUAL(Url("amqp:tcp").str(), "amqp:tcp:tcp:5672");
+ // These are ambiguous parses and arguably not the best result
+ BOOST_CHECK_EQUAL(Url("amqp:876").str(), "amqp:tcp:876:5672");
+ BOOST_CHECK_EQUAL(Url("tcp:567").str(), "amqp:tcp:567:5672");
+}
+
+QPID_AUTO_TEST_CASE(TestParseIPv6) {
+ Url u1("[::]");
+ BOOST_CHECK_EQUAL(u1[0].host, "::");
+ BOOST_CHECK_EQUAL(u1[0].port, 5672);
+ Url u2("[::1]");
+ BOOST_CHECK_EQUAL(u2[0].host, "::1");
+ BOOST_CHECK_EQUAL(u2[0].port, 5672);
+ Url u3("[::127.0.0.1]");
+ BOOST_CHECK_EQUAL(u3[0].host, "::127.0.0.1");
+ BOOST_CHECK_EQUAL(u3[0].port, 5672);
+ Url u4("[2002::222:68ff:fe0b:e61a]");
+ BOOST_CHECK_EQUAL(u4[0].host, "2002::222:68ff:fe0b:e61a");
+ BOOST_CHECK_EQUAL(u4[0].port, 5672);
+ Url u5("[2002::222:68ff:fe0b:e61a]:123");
+ BOOST_CHECK_EQUAL(u5[0].host, "2002::222:68ff:fe0b:e61a");
+ BOOST_CHECK_EQUAL(u5[0].port, 123);
+}
+
QPID_AUTO_TEST_CASE(TestParseMultiAddress) {
Url::addProtocol("xyz");
URL_CHECK_STR("amqp:tcp:host:0,xyz:foo:123,tcp:foo:0,xyz:bar:1");
diff --git a/cpp/src/tests/Variant.cpp b/cpp/src/tests/Variant.cpp
index b4188f524b..40f1c0cf75 100644
--- a/cpp/src/tests/Variant.cpp
+++ b/cpp/src/tests/Variant.cpp
@@ -86,6 +86,64 @@ QPID_AUTO_TEST_CASE(testConversions)
BOOST_CHECK_THROW(value.asBool(), InvalidConversion);
}
+QPID_AUTO_TEST_CASE(testConversionsFromString)
+{
+ Variant value;
+ value = "5";
+ BOOST_CHECK_EQUAL(5, value.asInt16());
+ BOOST_CHECK_EQUAL(5u, value.asUint16());
+
+ value = "-5";
+ BOOST_CHECK_EQUAL(-5, value.asInt16());
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+
+ value = "18446744073709551615";
+ BOOST_CHECK_EQUAL(18446744073709551615ull, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt64(), InvalidConversion);
+
+ value = "9223372036854775808";
+ BOOST_CHECK_EQUAL(9223372036854775808ull, value.asUint64());
+ BOOST_CHECK_THROW(value.asInt64(), InvalidConversion);
+
+ value = "-9223372036854775809";
+ BOOST_CHECK_THROW(value.asUint64(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt64(), InvalidConversion);
+
+ value = "2147483648";
+ BOOST_CHECK_EQUAL(2147483648ul, value.asUint32());
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+
+ value = "-2147483649";
+ BOOST_CHECK_THROW(value.asUint32(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt32(), InvalidConversion);
+
+ value = "32768";
+ BOOST_CHECK_EQUAL(32768u, value.asUint16());
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+
+ value = "-32769";
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+ BOOST_CHECK_THROW(value.asInt16(), InvalidConversion);
+
+ value = "-2.5";
+ BOOST_CHECK_EQUAL(-2.5, value.asFloat());
+
+ value = "-0.875432e10";
+ BOOST_CHECK_EQUAL(-0.875432e10, value.asDouble());
+
+ value = "-0";
+ BOOST_CHECK_EQUAL(0, value.asInt16());
+ BOOST_CHECK_EQUAL(0u, value.asUint16());
+
+ value = "-000";
+ BOOST_CHECK_EQUAL(0, value.asInt16());
+ BOOST_CHECK_EQUAL(0u, value.asUint16());
+
+ value = "-0010";
+ BOOST_CHECK_EQUAL(-10, value.asInt16());
+ BOOST_CHECK_THROW(value.asUint16(), InvalidConversion);
+}
+
QPID_AUTO_TEST_CASE(testSizeConversionsUint)
{
Variant value;
diff --git a/cpp/src/tests/XmlClientSessionTest.cpp b/cpp/src/tests/XmlClientSessionTest.cpp
index b3b7f12b53..b94c35ece0 100644
--- a/cpp/src/tests/XmlClientSessionTest.cpp
+++ b/cpp/src/tests/XmlClientSessionTest.cpp
@@ -90,7 +90,7 @@ struct SimpleListener : public MessageListener
}
};
-struct ClientSessionFixture : public ProxySessionFixture
+struct ClientSessionFixture : public SessionFixture
{
void declareSubscribe(const string& q="odd_blue",
const string& dest="xml")
diff --git a/cpp/src/tests/acl.py b/cpp/src/tests/acl.py
index 2d6a5b489d..65d5242e51 100755
--- a/cpp/src/tests/acl.py
+++ b/cpp/src/tests/acl.py
@@ -26,10 +26,11 @@ from qpid.datatypes import uuid4
from qpid.testlib import TestBase010
from qmf.console import Session
from qpid.datatypes import Message
+import qpid.messaging
class ACLFile:
- def __init__(self):
- self.f = open('data_dir/policy.acl','w');
+ def __init__(self, policy='data_dir/policy.acl'):
+ self.f = open(policy,'w')
def write(self,line):
self.f.write(line)
@@ -50,14 +51,24 @@ class ACLTests(TestBase010):
acl = self.qmf.getObjects(_class="acl")[0]
return acl.reloadACLFile()
+ def get_acl_file(self):
+ return ACLFile(self.config.defines.get("policy-file", "data_dir/policy.acl"))
+
def setUp(self):
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('acl allow all all\n')
aclf.close()
TestBase010.setUp(self)
self.startQmf()
self.reload_acl()
-
+
+ def tearDown(self):
+ aclf = self.get_acl_file()
+ aclf.write('acl allow all all\n')
+ aclf.close()
+ self.reload_acl()
+ TestBase010.tearDown(self)
+
#=====================================
# ACL general tests
#=====================================
@@ -66,7 +77,7 @@ class ACLTests(TestBase010):
"""
Test the deny all mode
"""
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('acl allow anonymous all all\n')
aclf.write('acl allow bob@QPID create queue\n')
aclf.write('acl deny all all')
@@ -94,7 +105,7 @@ class ACLTests(TestBase010):
"""
Test the allow all mode
"""
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('acl deny bob@QPID bind exchange\n')
aclf.write('acl allow all all')
aclf.close()
@@ -126,7 +137,7 @@ class ACLTests(TestBase010):
"""
Test empty groups
"""
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('acl group\n')
aclf.write('acl group admins bob@QPID joe@QPID\n')
aclf.write('acl allow all all')
@@ -140,7 +151,7 @@ class ACLTests(TestBase010):
"""
Test illegal acl formats
"""
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('acl group admins bob@QPID joe@QPID\n')
aclf.write('acl allow all all')
aclf.close()
@@ -154,7 +165,7 @@ class ACLTests(TestBase010):
Test illegal extension lines
"""
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('group admins bob@QPID \n')
aclf.write(' \ \n')
aclf.write('joe@QPID \n')
@@ -172,7 +183,7 @@ class ACLTests(TestBase010):
"""
Test proper extention lines
"""
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('group test1 joe@EXAMPLE.com \\ \n') # should be allowed
aclf.write(' jack@EXAMPLE.com \\ \n') # should be allowed
aclf.write('jill@TEST.COM \\ \n') # should be allowed
@@ -189,7 +200,7 @@ class ACLTests(TestBase010):
Test a user defined without a realm
Ex. group admin rajith
"""
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('group admin bob\n') # shouldn't be allowed
aclf.write('acl deny admin bind exchange\n')
aclf.write('acl allow all all')
@@ -204,7 +215,7 @@ class ACLTests(TestBase010):
Test a user defined without a realm
Ex. group admin rajith
"""
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('group test1 joe@EXAMPLE.com\n') # should be allowed
aclf.write('group test2 jack_123-jill@EXAMPLE.com\n') # should be allowed
aclf.write('group test4 host/somemachine.example.com@EXAMPLE.COM\n') # should be allowed
@@ -215,7 +226,7 @@ class ACLTests(TestBase010):
if (result.text.find("ACL format error",0,len(result.text)) != -1):
self.fail(result)
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('group test1 joe$H@EXAMPLE.com\n') # shouldn't be allowed
aclf.write('acl allow all all')
aclf.close()
@@ -233,7 +244,7 @@ class ACLTests(TestBase010):
Test illegal queue policy
"""
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('acl deny bob@QPID create queue name=q2 exclusive=true policytype=ding\n')
aclf.write('acl allow all all')
aclf.close()
@@ -249,7 +260,7 @@ class ACLTests(TestBase010):
Test illegal queue policy
"""
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('acl deny bob@QPID create queue name=q2 maxqueuesize=-1\n')
aclf.write('acl allow all all')
aclf.close()
@@ -260,7 +271,7 @@ class ACLTests(TestBase010):
if (result.text != expected):
self.fail(result)
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('acl deny bob@QPID create queue name=q2 maxqueuesize=9223372036854775808\n')
aclf.write('acl allow all all')
aclf.close()
@@ -277,7 +288,7 @@ class ACLTests(TestBase010):
Test illegal queue policy
"""
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('acl deny bob@QPID create queue name=q2 maxqueuecount=-1\n')
aclf.write('acl allow all all')
aclf.close()
@@ -288,7 +299,7 @@ class ACLTests(TestBase010):
if (result.text != expected):
self.fail(result)
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('acl deny bob@QPID create queue name=q2 maxqueuecount=9223372036854775808\n')
aclf.write('acl allow all all')
aclf.close()
@@ -308,7 +319,7 @@ class ACLTests(TestBase010):
"""
Test cases for queue acl in allow mode
"""
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('acl deny bob@QPID create queue name=q1 durable=true passive=true\n')
aclf.write('acl deny bob@QPID create queue name=q2 exclusive=true policytype=ring\n')
aclf.write('acl deny bob@QPID access queue name=q3\n')
@@ -411,7 +422,7 @@ class ACLTests(TestBase010):
"""
Test cases for queue acl in deny mode
"""
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('acl allow bob@QPID create queue name=q1 durable=true passive=true\n')
aclf.write('acl allow bob@QPID create queue name=q2 exclusive=true policytype=ring\n')
aclf.write('acl allow bob@QPID access queue name=q3\n')
@@ -534,7 +545,7 @@ class ACLTests(TestBase010):
"""
Test cases for exchange acl in allow mode
"""
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('acl deny bob@QPID create exchange name=testEx durable=true passive=true\n')
aclf.write('acl deny bob@QPID create exchange name=ex1 type=direct\n')
aclf.write('acl deny bob@QPID access exchange name=myEx queuename=q1 routingkey=rk1.*\n')
@@ -665,7 +676,7 @@ class ACLTests(TestBase010):
"""
Test cases for exchange acl in deny mode
"""
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('acl allow bob@QPID create exchange name=myEx durable=true passive=false\n')
aclf.write('acl allow bob@QPID bind exchange name=amq.topic queuename=bar routingkey=foo.*\n')
aclf.write('acl allow bob@QPID unbind exchange name=amq.topic queuename=bar routingkey=foo.*\n')
@@ -772,6 +783,52 @@ class ACLTests(TestBase010):
if (403 == e.args[0].error_code):
self.fail("ACL should allow exchange delete request for myEx");
+ def test_create_and_delete_exchange_via_qmf(self):
+ """
+ Test acl is enforced when creating/deleting via QMF
+ methods. Note that in order to be able to send the QMF methods
+ and receive the responses a significant amount of permissions
+ need to be enabled (TODO: can the set below be narrowed down
+ at all?)
+ """
+ aclf = self.get_acl_file()
+ aclf.write('acl allow bob@QPID create exchange\n')
+ aclf.write('acl allow admin@QPID delete exchange\n')
+ aclf.write('acl allow all access exchange\n')
+ aclf.write('acl allow all bind exchange\n')
+ aclf.write('acl allow all create queue\n')
+ aclf.write('acl allow all access queue\n')
+ aclf.write('acl allow all delete queue\n')
+ aclf.write('acl allow all consume queue\n')
+ aclf.write('acl allow all access method\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ bob = BrokerAdmin(self.config.broker, "bob", "bob")
+ bob.create_exchange("my-exchange") #should pass
+ #cleanup by deleting exchange
+ try:
+ bob.delete_exchange("my-exchange") #should fail
+ self.fail("ACL should deny exchange delete request for my-exchange");
+ except Exception, e:
+ self.assertEqual(7,e.args[0]["error_code"])
+ assert e.args[0]["error_text"].find("unauthorized-access") == 0
+ admin = BrokerAdmin(self.config.broker, "admin", "admin")
+ admin.delete_exchange("my-exchange") #should pass
+
+ anonymous = BrokerAdmin(self.config.broker)
+ try:
+ anonymous.create_exchange("another-exchange") #should fail
+ self.fail("ACL should deny exchange create request for another-exchange");
+ except Exception, e:
+ self.assertEqual(7,e.args[0]["error_code"])
+ assert e.args[0]["error_text"].find("unauthorized-access") == 0
+
+
#=====================================
# ACL consume tests
#=====================================
@@ -780,7 +837,7 @@ class ACLTests(TestBase010):
"""
Test cases for consume in allow mode
"""
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('acl deny bob@QPID consume queue name=q1\n')
aclf.write('acl deny bob@QPID consume queue name=q2\n')
aclf.write('acl allow all all')
@@ -826,7 +883,7 @@ class ACLTests(TestBase010):
"""
Test cases for consume in allow mode
"""
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('acl allow bob@QPID consume queue name=q1\n')
aclf.write('acl allow bob@QPID consume queue name=q2\n')
aclf.write('acl allow bob@QPID create queue\n')
@@ -872,7 +929,7 @@ class ACLTests(TestBase010):
"""
Test various publish acl
"""
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('acl deny bob@QPID publish exchange name=amq.direct routingkey=rk1\n')
aclf.write('acl deny bob@QPID publish exchange name=amq.topic\n')
aclf.write('acl deny bob@QPID publish exchange name=myEx routingkey=rk2\n')
@@ -921,7 +978,7 @@ class ACLTests(TestBase010):
"""
Test various publish acl
"""
- aclf = ACLFile()
+ aclf = self.get_acl_file()
aclf.write('acl allow bob@QPID publish exchange name=amq.direct routingkey=rk1\n')
aclf.write('acl allow bob@QPID publish exchange name=amq.topic\n')
aclf.write('acl allow bob@QPID publish exchange name=myEx routingkey=rk2\n')
@@ -972,3 +1029,113 @@ class ACLTests(TestBase010):
except qpid.session.SessionException, e:
if (403 == e.args[0].error_code):
self.fail("ACL should allow message transfer to exchange amq.direct with routing key rk1");
+
+ #=====================================
+ # ACL broker configuration tests
+ #=====================================
+
+ def test_broker_timestamp_config(self):
+ """
+ Test ACL control of the broker timestamp configuration
+ """
+ aclf = self.get_acl_file()
+ # enable lots of stuff to allow QMF to work
+ aclf.write('acl allow all create exchange\n')
+ aclf.write('acl allow all access exchange\n')
+ aclf.write('acl allow all bind exchange\n')
+ aclf.write('acl allow all publish exchange\n')
+ aclf.write('acl allow all create queue\n')
+ aclf.write('acl allow all access queue\n')
+ aclf.write('acl allow all delete queue\n')
+ aclf.write('acl allow all consume queue\n')
+ aclf.write('acl allow all access method\n')
+ # this should let bob access the timestamp configuration
+ aclf.write('acl allow bob@QPID access broker\n')
+ aclf.write('acl allow admin@QPID all all\n')
+ aclf.write('acl deny all all')
+ aclf.close()
+
+ result = self.reload_acl()
+ if (result.text.find("format error",0,len(result.text)) != -1):
+ self.fail(result)
+
+ ts = None
+ bob = BrokerAdmin(self.config.broker, "bob", "bob")
+ ts = bob.get_timestamp_cfg() #should work
+ bob.set_timestamp_cfg(ts); #should work
+
+ obo = BrokerAdmin(self.config.broker, "obo", "obo")
+ try:
+ ts = obo.get_timestamp_cfg() #should fail
+ failed = False
+ except Exception, e:
+ failed = True
+ self.assertEqual(7,e.args[0]["error_code"])
+ assert e.args[0]["error_text"].find("unauthorized-access") == 0
+ assert(failed)
+
+ try:
+ obo.set_timestamp_cfg(ts) #should fail
+ failed = False
+ except Exception, e:
+ failed = True
+ self.assertEqual(7,e.args[0]["error_code"])
+ assert e.args[0]["error_text"].find("unauthorized-access") == 0
+ assert(failed)
+
+ admin = BrokerAdmin(self.config.broker, "admin", "admin")
+ ts = admin.get_timestamp_cfg() #should pass
+ admin.set_timestamp_cfg(ts) #should pass
+
+
+class BrokerAdmin:
+ def __init__(self, broker, username=None, password=None):
+ self.connection = qpid.messaging.Connection(broker)
+ if username:
+ self.connection.username = username
+ self.connection.password = password
+ self.connection.sasl_mechanisms = "PLAIN"
+ self.connection.open()
+ self.session = self.connection.session()
+ self.sender = self.session.sender("qmf.default.direct/broker")
+ self.reply_to = "responses-#; {create:always}"
+ self.receiver = self.session.receiver(self.reply_to)
+
+ def invoke(self, method, arguments):
+ content = {
+ "_object_id": {"_object_name": "org.apache.qpid.broker:broker:amqp-broker"},
+ "_method_name": method,
+ "_arguments": arguments
+ }
+ request = qpid.messaging.Message(reply_to=self.reply_to, content=content)
+ request.properties["x-amqp-0-10.app-id"] = "qmf2"
+ request.properties["qmf.opcode"] = "_method_request"
+ self.sender.send(request)
+ response = self.receiver.fetch()
+ self.session.acknowledge()
+ if response.properties['x-amqp-0-10.app-id'] == 'qmf2':
+ if response.properties['qmf.opcode'] == '_method_response':
+ return response.content['_arguments']
+ elif response.properties['qmf.opcode'] == '_exception':
+ raise Exception(response.content['_values'])
+ else: raise Exception("Invalid response received, unexpected opcode: %s" % response.properties['qmf.opcode'])
+ else: raise Exception("Invalid response received, not a qmfv2 method: %s" % response.properties['x-amqp-0-10.app-id'])
+ def create_exchange(self, name, exchange_type=None, options={}):
+ properties = options
+ if exchange_type: properties["exchange_type"] = exchange_type
+ self.invoke("create", {"type": "exchange", "name":name, "properties":properties})
+
+ def create_queue(self, name, properties={}):
+ self.invoke("create", {"type": "queue", "name":name, "properties":properties})
+
+ def delete_exchange(self, name):
+ self.invoke("delete", {"type": "exchange", "name":name})
+
+ def delete_queue(self, name):
+ self.invoke("delete", {"type": "queue", "name":name})
+
+ def get_timestamp_cfg(self):
+ return self.invoke("getTimestampConfig", {})
+
+ def set_timestamp_cfg(self, receive):
+ return self.invoke("getTimestampConfig", {"receive":receive})
diff --git a/cpp/src/tests/allhosts b/cpp/src/tests/allhosts
index e43571aed4..4b4b943156 100755
--- a/cpp/src/tests/allhosts
+++ b/cpp/src/tests/allhosts
@@ -29,11 +29,12 @@ Options:
-s SECONDS sleep between starting commands.
-q don't print banner lines for each host.
-o SUFFIX log output of each command to <host>.SUFFIX
+ -X passed to ssh - forward X connection.
"
exit 1
}
-while getopts "tl:bs:dqo:" opt; do
+while getopts "tl:bs:dqo:X" opt; do
case $opt in
l) SSHOPTS="-l$OPTARG $SSHOPTS" ;;
t) SSHOPTS="-t $SSHOPTS" ;;
@@ -42,6 +43,7 @@ while getopts "tl:bs:dqo:" opt; do
s) SLEEP="sleep $OPTARG" ;;
q) NOBANNER=1 ;;
o) SUFFIX=$OPTARG ;;
+ X) SSHOPTS="-X $SSHOPTS" ;;
*) usage;;
esac
done
diff --git a/cpp/src/tests/brokertest.py b/cpp/src/tests/brokertest.py
index 98f58ebfdd..16d7fb0b78 100644
--- a/cpp/src/tests/brokertest.py
+++ b/cpp/src/tests/brokertest.py
@@ -29,6 +29,7 @@ from unittest import TestCase
from copy import copy
from threading import Thread, Lock, Condition
from logging import getLogger
+import qmf.console
log = getLogger("qpid.brokertest")
@@ -61,24 +62,6 @@ def is_running(pid):
class BadProcessStatus(Exception):
pass
-class ExceptionWrapper:
- """Proxy object that adds a message to exceptions raised"""
- def __init__(self, obj, msg):
- self.obj = obj
- self.msg = msg
-
- def __getattr__(self, name):
- func = getattr(self.obj, name)
- if type(func) != callable:
- return func
- return lambda *args, **kwargs: self._wrap(func, args, kwargs)
-
- def _wrap(self, func, args, kwargs):
- try:
- return func(*args, **kwargs)
- except Exception, e:
- raise Exception("%s: %s" %(self.msg, str(e)))
-
def error_line(filename, n=1):
"""Get the last n line(s) of filename for error messages"""
result = []
@@ -88,7 +71,8 @@ def error_line(filename, n=1):
for l in f:
if len(result) == n: result.pop(0)
result.append(" "+l)
- finally: f.close()
+ finally:
+ f.close()
except: return ""
return ":\n" + "".join(result)
@@ -96,111 +80,90 @@ 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."""
+ deadline = time.time() + timeout
while not function():
- if delay > timeout: delay = timeout
+ remaining = deadline - time.time()
+ if remaining <= 0: return False
+ delay = min(delay, remaining)
time.sleep(delay)
- timeout -= delay
- if timeout <= 0: return False
delay *= 2
return True
+class AtomicCounter:
+ def __init__(self):
+ self.count = 0
+ self.lock = Lock()
+
+ def next(self):
+ self.lock.acquire();
+ ret = self.count
+ self.count += 1
+ self.lock.release();
+ return ret
+
+_popen_id = AtomicCounter() # Popen identifier for use in output file names.
+
+# Constants for file descriptor arguments to Popen
+FILE = "FILE" # Write to file named after process
+PIPE = subprocess.PIPE
+
class Popen(subprocess.Popen):
"""
Can set and verify expectation of process status at end of test.
Dumps command line, stdout, stderr to data dir for debugging.
"""
- class DrainThread(Thread):
- """Thread to drain a file object and write the data to a file."""
- def __init__(self, infile, outname):
- Thread.__init__(self)
- self.infile, self.outname = infile, outname
- self.outfile = None
-
- def run(self):
- try:
- for line in self.infile:
- if self.outfile is None:
- self.outfile = open(self.outname, "w")
- self.outfile.write(line)
- finally:
- self.infile.close()
- if self.outfile is not None: self.outfile.close()
-
- class OutStream(ExceptionWrapper):
- """Wrapper for output streams, handles exceptions & draining output"""
- def __init__(self, infile, outfile, msg):
- ExceptionWrapper.__init__(self, infile, msg)
- self.infile, self.outfile = infile, outfile
- self.thread = None
-
- def drain(self):
- if self.thread is None:
- self.thread = Popen.DrainThread(self.infile, self.outfile)
- self.thread.start()
-
- def outfile(self, ext): return "%s.%s" % (self.pname, ext)
-
- def __init__(self, cmd, expect=EXPECT_EXIT_OK, drain=True):
- """Run cmd (should be a list of arguments)
+ def __init__(self, cmd, expect=EXPECT_EXIT_OK, stdin=None, stdout=FILE, stderr=FILE):
+ """Run cmd (should be a list of program and arguments)
expect - if set verify expectation at end of test.
- drain - if true (default) drain stdout/stderr to files.
+ stdout, stderr - can have the same values as for subprocess.Popen as well as
+ FILE (the default) which means write to a file named after the process.
+ stdin - like subprocess.Popen but defauts to PIPE
"""
self._clean = False
self._clean_lock = Lock()
assert find_exe(cmd[0]), "executable not found: "+cmd[0]
if type(cmd) is type(""): cmd = [cmd] # Make it a list.
self.cmd = [ str(x) for x in cmd ]
- self.returncode = None
self.expect = expect
+ self.id = _popen_id.next()
+ self.pname = "%s-%d" % (os.path.split(self.cmd[0])[1], self.id)
+ if stdout == FILE: stdout = open(self.outfile("out"), "w")
+ if stderr == FILE: stderr = open(self.outfile("err"), "w")
try:
- subprocess.Popen.__init__(self, self.cmd, 0, None, subprocess.PIPE, subprocess.PIPE, subprocess.PIPE, close_fds=True)
- except ValueError: # Windows can't do close_fds
- subprocess.Popen.__init__(self, self.cmd, 0, None, subprocess.PIPE, subprocess.PIPE, subprocess.PIPE)
- self.pname = "%s-%d" % (os.path.split(self.cmd[0])[1], self.pid)
- msg = "Process %s" % self.pname
- self.stdin = ExceptionWrapper(self.stdin, msg)
- self.stdout = Popen.OutStream(self.stdout, self.outfile("out"), msg)
- self.stderr = Popen.OutStream(self.stderr, self.outfile("err"), msg)
+ subprocess.Popen.__init__(self, self.cmd, bufsize=0, executable=None,
+ stdin=stdin, stdout=stdout, stderr=stderr,
+ close_fds=True)
+ except ValueError: # Windows can't do close_fds
+ subprocess.Popen.__init__(self, self.cmd, bufsize=0, executable=None,
+ stdin=stdin, stdout=stdout, stderr=stderr)
+
f = open(self.outfile("cmd"), "w")
- try: f.write(self.cmd_str())
+ try: f.write("%s\n%d"%(self.cmd_str(), self.pid))
finally: f.close()
log.debug("Started process %s: %s" % (self.pname, " ".join(self.cmd)))
- if drain: self.drain()
- def __str__(self): return "Popen<%s>"%(self.pname)
+ def __str__(self): return "Popen<%s>"%(self.pname)
- def drain(self):
- """Start threads to drain stdout/err"""
- self.stdout.drain()
- self.stderr.drain()
-
- def _cleanup(self):
- """Close pipes to sub-process"""
- self._clean_lock.acquire()
- try:
- if self._clean: return
- self._clean = True
- self.stdin.close()
- self.drain() # Drain output pipes.
- self.stdout.thread.join() # Drain thread closes pipe.
- self.stderr.thread.join()
- finally: self._clean_lock.release()
+ def outfile(self, ext): return "%s.%s" % (self.pname, ext)
def unexpected(self,msg):
err = error_line(self.outfile("err")) or error_line(self.outfile("out"))
raise BadProcessStatus("%s %s%s" % (self.pname, msg, err))
-
+
def stop(self): # Clean up at end of test.
try:
if self.expect == EXPECT_UNKNOWN:
try: self.kill() # Just make sure its dead
except: pass
elif self.expect == EXPECT_RUNNING:
- try:
- self.kill()
- except:
- self.unexpected("expected running, exit code %d" % self.wait())
+ if self.poll() != None:
+ self.unexpected("expected running, exit code %d" % self.returncode)
+ else:
+ try:
+ self.kill()
+ except Exception,e:
+ self.unexpected("exception from kill: %s" % str(e))
else:
retry(lambda: self.poll() is not None)
if self.returncode is None: # Still haven't stopped
@@ -212,40 +175,21 @@ class Popen(subprocess.Popen):
self.unexpected("expected error")
finally:
self.wait() # Clean up the process.
-
+
def communicate(self, input=None):
- if input:
- self.stdin.write(input)
- self.stdin.close()
- outerr = (self.stdout.read(), self.stderr.read())
- self.wait()
- return outerr
+ ret = subprocess.Popen.communicate(self, input)
+ self.cleanup()
+ return ret
- def is_running(self):
- return self.poll() is None
+ def is_running(self): return self.poll() is None
def assert_running(self):
if not self.is_running(): self.unexpected("Exit code %d" % self.returncode)
- def poll(self, _deadstate=None): # _deadstate required by base class in python 2.4
- if self.returncode is None:
- # Pass _deadstate only if it has been set, there is no _deadstate
- # parameter in Python 2.6
- if _deadstate is None: ret = subprocess.Popen.poll(self)
- else: ret = subprocess.Popen.poll(self, _deadstate)
-
- if (ret != -1):
- self.returncode = ret
- self._cleanup()
- return self.returncode
-
def wait(self):
- if self.returncode is None:
- self.drain()
- try: self.returncode = subprocess.Popen.wait(self)
- except OSError,e: raise OSError("Wait failed %s: %s"%(self.pname, e))
- self._cleanup()
- return self.returncode
+ ret = subprocess.Popen.wait(self)
+ self._cleanup()
+ return ret
def terminate(self):
try: subprocess.Popen.terminate(self)
@@ -254,7 +198,8 @@ class Popen(subprocess.Popen):
os.kill( self.pid , signal.SIGTERM)
except AttributeError: # no os.kill, using taskkill.. (Windows only)
os.popen('TASKKILL /PID ' +str(self.pid) + ' /F')
-
+ self._cleanup()
+
def kill(self):
try: subprocess.Popen.kill(self)
except AttributeError: # No terminate method
@@ -262,6 +207,20 @@ class Popen(subprocess.Popen):
os.kill( self.pid , signal.SIGKILL)
except AttributeError: # no os.kill, using taskkill.. (Windows only)
os.popen('TASKKILL /PID ' +str(self.pid) + ' /F')
+ self._cleanup()
+
+ def _cleanup(self):
+ """Clean up after a dead process"""
+ self._clean_lock.acquire()
+ if not self._clean:
+ self._clean = True
+ try: self.stdin.close()
+ except: pass
+ try: self.stdout.close()
+ except: pass
+ try: self.stderr.close()
+ except: pass
+ self._clean_lock.release()
def cmd_str(self): return " ".join([str(s) for s in self.cmd])
@@ -288,11 +247,11 @@ class Broker(Popen):
while (os.path.exists(self.log)):
self.log = "%s-%d.log" % (self.name, i)
i += 1
-
+
def get_log(self):
return os.path.abspath(self.log)
- def __init__(self, test, args=[], name=None, expect=EXPECT_RUNNING, port=0, log_level=None, wait=None):
+ def __init__(self, test, args=[], name=None, expect=EXPECT_RUNNING, port=0, log_level=None, wait=None, show_cmd=False):
"""Start a broker daemon. name determines the data-dir and log
file names."""
@@ -318,15 +277,20 @@ class Broker(Popen):
cmd += ["--log-to-file", self.log]
cmd += ["--log-to-stderr=no"]
if log_level != None:
- cmd += ["--log-enable=%s" % log_level]
+ cmd += ["--log-enable=%s" % log_level]
self.datadir = self.name
cmd += ["--data-dir", self.datadir]
- Popen.__init__(self, cmd, expect, drain=False)
+ if show_cmd: print cmd
+ Popen.__init__(self, cmd, expect, stdout=PIPE)
test.cleanup_stop(self)
self._host = "127.0.0.1"
log.debug("Started broker %s (%s, %s)" % (self.name, self.pname, self.log))
self._log_ready = False
+ def startQmf(self, handler=None):
+ self.qmf_session = qmf.console.Session(handler)
+ self.qmf_broker = self.qmf_session.addBroker("%s:%s" % (self.host(), self.port()))
+
def host(self): return self._host
def port(self):
@@ -357,7 +321,7 @@ class Broker(Popen):
s = c.session(str(qpid.datatypes.uuid4()))
s.queue_declare(queue=queue)
c.close()
-
+
def _prep_sender(self, queue, durable, xprops):
s = queue + "; {create:always, node:{durable:" + str(durable)
if xprops != None: s += ", x-declare:{" + xprops + "}"
@@ -401,13 +365,14 @@ class Broker(Popen):
def log_ready(self):
"""Return true if the log file exists and contains a broker ready message"""
- if self._log_ready: return True
- self._log_ready = find_in_file("notice Broker running", self.log)
+ if not self._log_ready:
+ self._log_ready = find_in_file("notice Broker running", self.log)
+ return self._log_ready
def ready(self, **kwargs):
"""Wait till broker is ready to serve clients"""
# First make sure the broker is listening by checking the log.
- if not retry(self.log_ready, timeout=30):
+ if not retry(self.log_ready, timeout=60):
raise Exception(
"Timed out waiting for broker %s%s"%(self.name, error_line(self.log,5)))
# Create a connection and a session. For a cluster broker this will
@@ -416,23 +381,27 @@ class Broker(Popen):
c = self.connect(**kwargs)
try: c.session()
finally: c.close()
- except: raise RethrownException(
- "Broker %s failed ready test%s"%(self.name,error_line(self.log, 5)))
+ except Exception,e: raise RethrownException(
+ "Broker %s not responding: (%s)%s"%(self.name,e,error_line(self.log, 5)))
def store_state(self):
- uuids = open(os.path.join(self.datadir, "cluster", "store.status")).readlines()
+ f = open(os.path.join(self.datadir, "cluster", "store.status"))
+ try: uuids = f.readlines()
+ finally: f.close()
null_uuid="00000000-0000-0000-0000-000000000000\n"
if len(uuids) < 2: return "unknown" # we looked while the file was being updated.
if uuids[0] == null_uuid: return "empty"
if uuids[1] == null_uuid: return "dirty"
return "clean"
-
+
class Cluster:
"""A cluster of brokers in a test."""
+ # Client connection options for use in failover tests.
+ CONNECTION_OPTIONS = "reconnect:true,reconnect-timeout:10,reconnect-urls-replace:true"
_cluster_count = 0
- def __init__(self, test, count=0, args=[], expect=EXPECT_RUNNING, wait=True):
+ def __init__(self, test, count=0, args=[], expect=EXPECT_RUNNING, wait=True, show_cmd=False):
self.test = test
self._brokers=[]
self.name = "cluster%d" % Cluster._cluster_count
@@ -443,16 +412,19 @@ class Cluster:
self.args += [ "--log-enable=info+", "--log-enable=debug+:cluster"]
assert BrokerTest.cluster_lib, "Cannot locate cluster plug-in"
self.args += [ "--load-module", BrokerTest.cluster_lib ]
- self.start_n(count, expect=expect, wait=wait)
+ self.start_n(count, expect=expect, wait=wait, show_cmd=show_cmd)
- def start(self, name=None, expect=EXPECT_RUNNING, wait=True, args=[], port=0):
+ def start(self, name=None, expect=EXPECT_RUNNING, wait=True, args=[], port=0, show_cmd=False):
"""Add a broker to the cluster. Returns the index of the new broker."""
if not name: name="%s-%d" % (self.name, len(self._brokers))
- self._brokers.append(self.test.broker(self.args+args, name, expect, wait, port=port))
+ self._brokers.append(self.test.broker(self.args+args, name, expect, wait, port=port, show_cmd=show_cmd))
return self._brokers[-1]
- def start_n(self, count, expect=EXPECT_RUNNING, wait=True, args=[]):
- for i in range(count): self.start(expect=expect, wait=wait, args=args)
+ def ready(self):
+ for b in self: b.ready()
+
+ def start_n(self, count, expect=EXPECT_RUNNING, wait=True, args=[], show_cmd=False):
+ for i in range(count): self.start(expect=expect, wait=wait, args=args, show_cmd=show_cmd)
# Behave like a list of brokers.
def __len__(self): return len(self._brokers)
@@ -481,7 +453,7 @@ class BrokerTest(TestCase):
rootdir = os.getcwd()
def configure(self, config): self.config=config
-
+
def setUp(self):
outdir = self.config.defines.get("OUTDIR") or "brokertest.tmp"
self.dir = os.path.join(self.rootdir, outdir, self.id())
@@ -502,41 +474,50 @@ class BrokerTest(TestCase):
"""Call thing.stop at end of test"""
self.stopem.append(stopable)
- def popen(self, cmd, expect=EXPECT_EXIT_OK, drain=True):
+ def popen(self, cmd, expect=EXPECT_EXIT_OK, stdin=None, stdout=FILE, stderr=FILE):
"""Start a process that will be killed at end of test, in the test dir."""
os.chdir(self.dir)
- p = Popen(cmd, expect, drain)
+ p = Popen(cmd, expect, stdin=stdin, stdout=stdout, stderr=stderr)
self.cleanup_stop(p)
return p
- def broker(self, args=[], name=None, expect=EXPECT_RUNNING, wait=True, port=0, log_level=None):
+ def broker(self, args=[], name=None, expect=EXPECT_RUNNING, wait=True, port=0, log_level=None, show_cmd=False):
"""Create and return a broker ready for use"""
- b = Broker(self, args=args, name=name, expect=expect, port=port, log_level=log_level)
+ b = Broker(self, args=args, name=name, expect=expect, port=port, log_level=log_level, show_cmd=show_cmd)
if (wait):
try: b.ready()
except Exception, e:
raise RethrownException("Failed to start broker %s(%s): %s" % (b.name, b.log, e))
return b
- def cluster(self, count=0, args=[], expect=EXPECT_RUNNING, wait=True):
+ def cluster(self, count=0, args=[], expect=EXPECT_RUNNING, wait=True, show_cmd=False):
"""Create and return a cluster ready for use"""
- cluster = Cluster(self, count, args, expect=expect, wait=wait)
+ cluster = Cluster(self, count, args, expect=expect, wait=wait, show_cmd=show_cmd)
return cluster
+ def browse(self, session, queue, timeout=0):
+ """Return a list with the contents of each message on queue."""
+ r = session.receiver("%s;{mode:browse}"%(queue))
+ r.capacity = 100
+ try:
+ contents = []
+ try:
+ while True: contents.append(r.fetch(timeout=timeout).content)
+ except messaging.Empty: pass
+ finally: r.close()
+ return contents
+
def assert_browse(self, session, queue, expect_contents, timeout=0):
"""Assert that the contents of messages on queue (as retrieved
using session and timeout) exactly match the strings in
expect_contents"""
-
- r = session.receiver("%s;{mode:browse}"%(queue))
- actual_contents = []
- try:
- for c in expect_contents: actual_contents.append(r.fetch(timeout=timeout).content)
- while True: actual_contents.append(r.fetch(timeout=0).content) # Check for extra messages.
- except messaging.Empty: pass
- r.close()
+ actual_contents = self.browse(session, queue, timeout)
self.assertEqual(expect_contents, actual_contents)
+def join(thread, timeout=10):
+ thread.join(timeout)
+ if thread.isAlive(): raise Exception("Timed out joining thread %s"%thread)
+
class RethrownException(Exception):
"""Captures the stack trace of the current exception to be thrown later"""
def __init__(self, msg=""):
@@ -554,15 +535,16 @@ class StoppableThread(Thread):
def stop(self):
self.stopped = True
- self.join()
+ join(self)
if self.error: raise self.error
-
+
class NumberedSender(Thread):
"""
Thread to run a sender client and send numbered messages until stopped.
"""
- def __init__(self, broker, max_depth=None, queue="test-queue"):
+ def __init__(self, broker, max_depth=None, queue="test-queue",
+ connection_options=Cluster.CONNECTION_OPTIONS):
"""
max_depth: enable flow control, ensure sent - received <= max_depth.
Requires self.notify_received(n) to be called each time messages are received.
@@ -573,9 +555,11 @@ class NumberedSender(Thread):
"--broker", "localhost:%s"%broker.port(),
"--address", "%s;{create:always}"%queue,
"--failover-updates",
+ "--connection-options", "{%s}"%(connection_options),
"--content-stdin"
],
- expect=EXPECT_RUNNING)
+ expect=EXPECT_RUNNING,
+ stdin=PIPE)
self.condition = Condition()
self.max = max_depth
self.received = 0
@@ -590,6 +574,7 @@ class NumberedSender(Thread):
try:
self.sent = 0
while not self.stopped:
+ self.sender.assert_running()
if self.max:
self.condition.acquire()
while not self.stopped and self.sent - self.received > self.max:
@@ -612,16 +597,17 @@ class NumberedSender(Thread):
self.stopped = True
self.condition.notify()
finally: self.condition.release()
- self.join()
+ join(self)
self.write_message(-1) # end-of-messages marker.
if self.error: raise self.error
-
+
class NumberedReceiver(Thread):
"""
Thread to run a receiver client and verify it receives
sequentially numbered messages.
"""
- def __init__(self, broker, sender = None, queue="test-queue"):
+ def __init__(self, broker, sender = None, queue="test-queue",
+ connection_options=Cluster.CONNECTION_OPTIONS):
"""
sender: enable flow control. Call sender.received(n) for each message received.
"""
@@ -632,22 +618,24 @@ class NumberedReceiver(Thread):
"--broker", "localhost:%s"%broker.port(),
"--address", "%s;{create:always}"%queue,
"--failover-updates",
+ "--connection-options", "{%s}"%(connection_options),
"--forever"
],
expect=EXPECT_RUNNING,
- drain=False)
+ stdout=PIPE)
self.lock = Lock()
self.error = None
self.sender = sender
+ self.received = 0
def read_message(self):
return int(self.receiver.stdout.readline())
-
+
def run(self):
try:
- self.received = 0
m = self.read_message()
while m != -1:
+ self.receiver.assert_running()
assert(m <= self.received) # Check for missing messages
if (m == self.received): # Ignore duplicates
self.received += 1
@@ -659,7 +647,7 @@ class NumberedReceiver(Thread):
def stop(self):
"""Returns when termination message is received"""
- self.join()
+ join(self)
if self.error: raise self.error
class ErrorGenerator(StoppableThread):
@@ -674,7 +662,7 @@ class ErrorGenerator(StoppableThread):
self.broker=broker
broker.test.cleanup_stop(self)
self.start()
-
+
def run(self):
c = self.broker.connect_old()
try:
diff --git a/cpp/src/tests/cli_tests.py b/cpp/src/tests/cli_tests.py
index deef03279d..6c75927461 100755
--- a/cpp/src/tests/cli_tests.py
+++ b/cpp/src/tests/cli_tests.py
@@ -365,6 +365,26 @@ class CliTests(TestBase010):
self.assertEqual(queue._altExchange_.name, altName)
self.assertEqual(found, True)
+ def test_qpid_config_list_queues_arguments(self):
+ """
+ Test to verify that when the type of a policy limit is
+ actually a string (though still a valid value), it does not
+ upset qpid-config
+ """
+ self.startQmf();
+ qmf = self.qmf
+
+ names = ["queue_capacity%s" % (i) for i in range(1, 6)]
+ for name in names:
+ self.session.queue_declare(queue=name, exclusive=True,
+ arguments={'qpid.max_count' : str(i), 'qpid.max_size': '100'})
+
+ output = os.popen(self.qpid_config_command(" queues")).readlines()
+ queues = [line.split()[0] for line in output[2:len(output)]] #ignore first two lines (header)
+
+ for name in names:
+ assert name in queues, "%s not in %s" % (name, queues)
+
def test_qpid_route(self):
self.startQmf();
qmf = self.qmf
@@ -405,7 +425,7 @@ class CliTests(TestBase010):
qmf = self.qmf
ret = self.qpid_route_api("dynamic add "
- + " --sasl-mechanism PLAIN "
+ + " --client-sasl-mechanism PLAIN "
+ "guest/guest@localhost:"+str(self.broker.port) + " "
+ str(self.remote_host())+":"+str(self.remote_port()) + " "
+"amq.direct")
@@ -424,7 +444,7 @@ class CliTests(TestBase010):
qmf = self.qmf
ret = self.qpid_route_api("dynamic add "
- + " --sasl-mechanism PLAIN "
+ + " --client-sasl-mechanism PLAIN "
+ "localhost:"+str(self.broker.port) + " "
+ str(self.remote_host())+":"+str(self.remote_port()) + " "
+"amq.direct")
diff --git a/cpp/src/tests/cluster_python_tests_failing.txt b/cpp/src/tests/cluster_python_tests_failing.txt
index 7ba8089946..f8639d7b59 100644
--- a/cpp/src/tests/cluster_python_tests_failing.txt
+++ b/cpp/src/tests/cluster_python_tests_failing.txt
@@ -1,32 +1,4 @@
qpid_tests.broker_0_10.management.ManagementTest.test_purge_queue
qpid_tests.broker_0_10.management.ManagementTest.test_connection_close
-qpid_tests.broker_0_10.dtx.DtxTests.test_bad_resume
-qpid_tests.broker_0_10.dtx.DtxTests.test_commit_unknown
-qpid_tests.broker_0_10.dtx.DtxTests.test_end
-qpid_tests.broker_0_10.dtx.DtxTests.test_end_suspend_and_fail
-qpid_tests.broker_0_10.dtx.DtxTests.test_end_unknown_xid
-qpid_tests.broker_0_10.dtx.DtxTests.test_forget_xid_on_completion
-qpid_tests.broker_0_10.dtx.DtxTests.test_get_timeout
-qpid_tests.broker_0_10.dtx.DtxTests.test_get_timeout_unknown
-qpid_tests.broker_0_10.dtx.DtxTests.test_implicit_end
-qpid_tests.broker_0_10.dtx.DtxTests.test_invalid_commit_not_ended
-qpid_tests.broker_0_10.dtx.DtxTests.test_invalid_commit_one_phase_false
-qpid_tests.broker_0_10.dtx.DtxTests.test_invalid_commit_one_phase_true
-qpid_tests.broker_0_10.dtx.DtxTests.test_invalid_prepare_not_ended
-qpid_tests.broker_0_10.dtx.DtxTests.test_invalid_rollback_not_ended
-qpid_tests.broker_0_10.dtx.DtxTests.test_prepare_unknown
-qpid_tests.broker_0_10.dtx.DtxTests.test_recover
-qpid_tests.broker_0_10.dtx.DtxTests.test_rollback_unknown
-qpid_tests.broker_0_10.dtx.DtxTests.test_select_required
-qpid_tests.broker_0_10.dtx.DtxTests.test_set_timeout
-qpid_tests.broker_0_10.dtx.DtxTests.test_simple_commit
-qpid_tests.broker_0_10.dtx.DtxTests.test_simple_prepare_commit
-qpid_tests.broker_0_10.dtx.DtxTests.test_simple_prepare_rollback
-qpid_tests.broker_0_10.dtx.DtxTests.test_simple_rollback
-qpid_tests.broker_0_10.dtx.DtxTests.test_start_already_known
-qpid_tests.broker_0_10.dtx.DtxTests.test_start_join
-qpid_tests.broker_0_10.dtx.DtxTests.test_start_join_and_resume
-qpid_tests.broker_0_10.dtx.DtxTests.test_suspend_resume
-qpid_tests.broker_0_10.dtx.DtxTests.test_suspend_start_end_resume
qpid_tests.broker_0_10.message.MessageTests.test_ttl
qpid_tests.broker_0_10.management.ManagementTest.test_broker_connectivity_oldAPI
diff --git a/cpp/src/tests/cluster_test_logs.py b/cpp/src/tests/cluster_test_logs.py
index 1fa9014d11..3c7e8e8020 100755
--- a/cpp/src/tests/cluster_test_logs.py
+++ b/cpp/src/tests/cluster_test_logs.py
@@ -53,16 +53,19 @@ def filter_log(log):
'stall for update|unstall, ignore update|cancelled offer .* unstall',
'caught up',
'active for links|Passivating links|Activating links',
+ 'info Connecting: .*', # UpdateClient connection
'info Connection.* connected to', # UpdateClient connection
- 'warning Connection [\d+ [0-9.:]+] closed', # UpdateClient connection
+ 'warning Connection \\[[-0-9.: ]+\\] closed', # UpdateClient connection
'warning Broker closed connection: 200, OK',
'task late',
'task overran',
'warning CLOSING .* unsent data',
'Inter-broker link ',
- 'Running in a cluster, marking store'
+ 'Running in a cluster, marking store',
+ 'debug Sending keepalive signal to watchdog', # Watchdog timer thread
+ 'last broker standing joined by 1 replicas, updating queue policies.',
+ 'Connection .* timed out: closing' # heartbeat connection close
])
- skip_re = re.compile(skip)
# 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'
# Substitutions to remove expected differences
@@ -80,6 +83,13 @@ def filter_log(log):
(r' map={.*_object_name:([^,}]*)[,}].*', r' \1'), # V2 map - just keep name
(r'\d+-\d+-\d+--\d+', 'X-X-X--X'), # V1 Object IDs
]
+ # Substitutions to mask known issue: durable test shows inconsistent "changed stats for com.redhat.rhm.store:journal" messages.
+ skip += '|Changed V[12] statistics com.redhat.rhm.store:journal'
+ subs += [(r'to=console.obj.1.0.com.redhat.rhm.store.journal props=\d+ stats=\d+',
+ 'to=console.obj.1.0.com.redhat.rhm.store.journal props=NN stats=NN')]
+
+ skip_re = re.compile(skip)
+ subs = [(re.compile(pattern), subst) for pattern, subst in subs]
for l in open(log):
if skip_re.search(l): continue
for pattern,subst in subs: l = re.sub(pattern,subst,l)
diff --git a/cpp/src/tests/cluster_tests.py b/cpp/src/tests/cluster_tests.py
index cbad4010b4..0e80e06d34 100755
--- a/cpp/src/tests/cluster_tests.py
+++ b/cpp/src/tests/cluster_tests.py
@@ -18,12 +18,13 @@
# under the License.
#
-import os, signal, sys, time, imp, re, subprocess, glob, cluster_test_logs
+import os, signal, sys, time, imp, re, subprocess, glob, random, logging
+import cluster_test_logs
from qpid import datatypes, messaging
from brokertest import *
from qpid.harness import Skipped
-from qpid.messaging import Message, Empty
-from threading import Thread, Lock
+from qpid.messaging import Message, Empty, Disposition, REJECTED, util
+from threading import Thread, Lock, Condition
from logging import getLogger
from itertools import chain
from tempfile import NamedTemporaryFile
@@ -96,9 +97,15 @@ class ShortTests(BrokerTest):
destination="amq.direct",
message=qpid.datatypes.Message(props, "content"))
+ # Try message with TTL and differnet headers/properties
+ cluster[0].send_message("q", Message(durable=True, ttl=100000))
+ cluster[0].send_message("q", Message(durable=True, properties={}, ttl=100000))
+ cluster[0].send_message("q", Message(durable=True, properties={"x":10}, ttl=100000))
+
# Now update a new member and compare their dumps.
cluster.start(args=["--test-store-dump", "updatee.dump"])
assert readfile("direct.dump") == readfile("updatee.dump")
+
os.remove("direct.dump")
os.remove("updatee.dump")
@@ -108,19 +115,22 @@ class ShortTests(BrokerTest):
acl=os.path.join(os.getcwd(), "policy.acl")
aclf=file(acl,"w")
aclf.write("""
-acl deny zag@QPID create queue
-acl allow all all
+acl allow zig@QPID all all
+acl deny all all
""")
aclf.close()
- cluster = self.cluster(2, args=["--auth", "yes",
+ cluster = self.cluster(1, args=["--auth", "yes",
"--sasl-config", sasl_config,
"--load-module", os.getenv("ACL_LIB"),
"--acl-file", acl])
# Valid user/password, ensure queue is created.
c = cluster[0].connect(username="zig", password="zig")
- c.session().sender("ziggy;{create:always}")
+ c.session().sender("ziggy;{create:always,node:{x-declare:{exclusive:true}}}")
c.close()
+ cluster.start() # Start second node.
+
+ # Check queue is created on second node.
c = cluster[1].connect(username="zig", password="zig")
c.session().receiver("ziggy;{assert:always}")
c.close()
@@ -149,7 +159,7 @@ acl allow all all
self.fail("Expected exception")
except messaging.exceptions.UnauthorizedAccess: pass
# make sure the queue was not created at the other node.
- c = cluster[0].connect(username="zag", password="zag")
+ c = cluster[1].connect(username="zig", password="zig")
try:
s = c.session()
s.sender("zaggy;{assert:always}")
@@ -157,6 +167,35 @@ acl allow all all
self.fail("Expected exception")
except messaging.exceptions.NotFound: pass
+ def test_sasl_join(self):
+ """Verify SASL authentication between brokers when joining a cluster."""
+ sasl_config=os.path.join(self.rootdir, "sasl_config")
+ # Test with a valid username/password
+ cluster = self.cluster(1, args=["--auth", "yes",
+ "--sasl-config", sasl_config,
+ "--load-module", os.getenv("ACL_LIB"),
+ "--cluster-username=zig",
+ "--cluster-password=zig",
+ "--cluster-mechanism=PLAIN"
+ ])
+ cluster.start()
+ cluster.ready()
+ c = cluster[1].connect(username="zag", password="zag")
+
+ # Test with an invalid username/password
+ cluster = self.cluster(1, args=["--auth", "yes",
+ "--sasl-config", sasl_config,
+ "--load-module", os.getenv("ACL_LIB"),
+ "--cluster-username=x",
+ "--cluster-password=y",
+ "--cluster-mechanism=PLAIN"
+ ])
+ try:
+ cluster.start(expect=EXPECT_EXIT_OK)
+ cluster[1].ready()
+ self.fail("Expected exception")
+ except: pass
+
def test_user_id_update(self):
"""Ensure that user-id of an open session is updated to new cluster members"""
sasl_config=os.path.join(self.rootdir, "sasl_config")
@@ -246,25 +285,6 @@ acl allow all all
session1 = cluster[1].connect().session()
for q in queues: self.assert_browse(session1, "q1", ["foo"])
- def test_dr_no_message(self):
- """Regression test for https://bugzilla.redhat.com/show_bug.cgi?id=655141
- Joining broker crashes with 'error deliveryRecord no update message'
- """
-
- cluster = self.cluster(1)
- session0 = cluster[0].connect().session()
- s = session0.sender("q1;{create:always}")
- s.send(Message("a", ttl=0.05), sync=False)
- s.send(Message("b", ttl=0.05), sync=False)
- r1 = session0.receiver("q1")
- self.assertEqual("a", r1.fetch(timeout=0).content)
- r2 = session0.receiver("q1;{mode:browse}")
- self.assertEqual("b", r2.fetch(timeout=0).content)
- # Leave messages un-acknowledged, let the expire, then start new broker.
- time.sleep(.1)
- cluster.start()
- self.assertRaises(Empty, cluster[1].connect().session().receiver("q1").fetch,0)
-
def test_route_update(self):
"""Regression test for https://issues.apache.org/jira/browse/QPID-2982
Links and bridges associated with routes were not replicated on update.
@@ -272,6 +292,7 @@ acl allow all all
client was attached.
"""
args=["--mgmt-pub-interval=1","--log-enable=trace+:management"]
+ # First broker will be killed.
cluster0 = self.cluster(1, args=args)
cluster1 = self.cluster(1, args=args)
assert 0 == subprocess.call(
@@ -301,9 +322,695 @@ acl allow all all
qpid_tool.wait()
scanner.join()
assert scanner.found
+ # Regression test for https://issues.apache.org/jira/browse/QPID-3235
+ # Inconsistent stats when changing elder.
+
+ # Force a change of elder
+ cluster0.start()
+ cluster0[0].expect=EXPECT_EXIT_FAIL # About to die.
+ cluster0[0].kill()
+ time.sleep(2) # Allow a management interval to pass.
# Verify logs are consistent
cluster_test_logs.verify_logs()
+ def test_redelivered(self):
+ """Verify that redelivered flag is set correctly on replayed messages"""
+ cluster = self.cluster(2, expect=EXPECT_EXIT_FAIL)
+ url = "amqp:tcp:%s,tcp:%s" % (cluster[0].host_port(), cluster[1].host_port())
+ queue = "my-queue"
+ cluster[0].declare_queue(queue)
+ self.sender = self.popen(
+ ["qpid-send",
+ "--broker", url,
+ "--address", queue,
+ "--sequence=true",
+ "--send-eos=1",
+ "--messages=100000",
+ "--connection-options={%s}"%(Cluster.CONNECTION_OPTIONS)
+ ])
+ self.receiver = self.popen(
+ ["qpid-receive",
+ "--broker", url,
+ "--address", queue,
+ "--ignore-duplicates",
+ "--check-redelivered",
+ "--connection-options={%s}"%(Cluster.CONNECTION_OPTIONS),
+ "--forever"
+ ])
+ time.sleep(1)#give sender enough time to have some messages to replay
+ cluster[0].kill()
+ self.sender.wait()
+ self.receiver.wait()
+ cluster[1].kill()
+
+ class BlockedSend(Thread):
+ """Send a message, send is expected to block.
+ Verify that it does block (for a given timeout), then allow
+ waiting till it unblocks when it is expected to do so."""
+ def __init__(self, sender, msg):
+ self.sender, self.msg = sender, msg
+ self.blocked = True
+ self.condition = Condition()
+ self.timeout = 0.1 # Time to wait for expected results.
+ Thread.__init__(self)
+ def run(self):
+ try:
+ self.sender.send(self.msg, sync=True)
+ self.condition.acquire()
+ try:
+ self.blocked = False
+ self.condition.notify()
+ finally: self.condition.release()
+ except Exception,e: print "BlockedSend exception: %s"%e
+ def start(self):
+ Thread.start(self)
+ time.sleep(self.timeout)
+ assert self.blocked # Expected to block
+ def assert_blocked(self): assert self.blocked
+ def wait(self): # Now expecting to unblock
+ self.condition.acquire()
+ try:
+ while self.blocked:
+ self.condition.wait(self.timeout)
+ if self.blocked: raise Exception("Timed out waiting for send to unblock")
+ finally: self.condition.release()
+ self.join()
+
+ def queue_flowlimit_test(self, brokers):
+ """Verify that the queue's flowlimit configuration and state are
+ correctly replicated.
+ The brokers argument allows this test to run on single broker,
+ cluster of 2 pre-startd brokers or cluster where second broker
+ starts after queue is in flow control.
+ """
+ # configure a queue with a specific flow limit on first broker
+ ssn0 = brokers.first().connect().session()
+ s0 = ssn0.sender("flq; {create:always, node:{type:queue, x-declare:{arguments:{'qpid.flow_stop_count':5, 'qpid.flow_resume_count':3}}}}")
+ brokers.first().startQmf()
+ q1 = [q for q in brokers.first().qmf_session.getObjects(_class="queue") if q.name == "flq"][0]
+ oid = q1.getObjectId()
+ self.assertEqual(q1.name, "flq")
+ self.assertEqual(q1.arguments, {u'qpid.flow_stop_count': 5L, u'qpid.flow_resume_count': 3L})
+ assert not q1.flowStopped
+ self.assertEqual(q1.flowStoppedCount, 0)
+
+ # fill the queue on one broker until flow control is active
+ for x in range(5): s0.send(Message(str(x)))
+ sender = ShortTests.BlockedSend(s0, Message(str(6)))
+ sender.start() # Tests that sender does block
+ # Verify the broker queue goes into a flowStopped state
+ deadline = time.time() + 1
+ while not q1.flowStopped and time.time() < deadline: q1.update()
+ assert q1.flowStopped
+ self.assertEqual(q1.flowStoppedCount, 1)
+ sender.assert_blocked() # Still blocked
+
+ # Now verify the both brokers in cluster have same configuration
+ brokers.second().startQmf()
+ qs = brokers.second().qmf_session.getObjects(_objectId=oid)
+ self.assertEqual(len(qs), 1)
+ q2 = qs[0]
+ self.assertEqual(q2.name, "flq")
+ self.assertEqual(q2.arguments, {u'qpid.flow_stop_count': 5L, u'qpid.flow_resume_count': 3L})
+ assert q2.flowStopped
+ self.assertEqual(q2.flowStoppedCount, 1)
+
+ # now drain the queue using a session to the other broker
+ ssn1 = brokers.second().connect().session()
+ r1 = ssn1.receiver("flq", capacity=6)
+ for x in range(4):
+ r1.fetch(timeout=0)
+ ssn1.acknowledge()
+ sender.wait() # Verify no longer blocked.
+
+ # and re-verify state of queue on both brokers
+ q1.update()
+ assert not q1.flowStopped
+ q2.update()
+ assert not q2.flowStopped
+
+ ssn0.connection.close()
+ ssn1.connection.close()
+ cluster_test_logs.verify_logs()
+
+ def test_queue_flowlimit(self):
+ """Test flow limits on a standalone broker"""
+ broker = self.broker()
+ class Brokers:
+ def first(self): return broker
+ def second(self): return broker
+ self.queue_flowlimit_test(Brokers())
+
+ def test_queue_flowlimit_cluster(self):
+ cluster = self.cluster(2)
+ class Brokers:
+ def first(self): return cluster[0]
+ def second(self): return cluster[1]
+ self.queue_flowlimit_test(Brokers())
+
+ def test_queue_flowlimit_cluster_join(self):
+ cluster = self.cluster(1)
+ class Brokers:
+ def first(self): return cluster[0]
+ def second(self):
+ if len(cluster) == 1: cluster.start()
+ return cluster[1]
+ self.queue_flowlimit_test(Brokers())
+
+ def test_queue_flowlimit_replicate(self):
+ """ Verify that a queue which is in flow control BUT has drained BELOW
+ the flow control 'stop' threshold, is correctly replicated when a new
+ broker is added to the cluster.
+ """
+
+ class AsyncSender(Thread):
+ """Send a fixed number of msgs from a sender in a separate thread
+ so it may block without blocking the test.
+ """
+ def __init__(self, broker, address, count=1, size=4):
+ Thread.__init__(self)
+ self.daemon = True
+ self.broker = broker
+ self.queue = address
+ self.count = count
+ self.size = size
+ self.done = False
+
+ def run(self):
+ self.sender = subprocess.Popen(["qpid-send",
+ "--capacity=1",
+ "--content-size=%s" % self.size,
+ "--messages=%s" % self.count,
+ "--failover-updates",
+ "--connection-options={%s}"%(Cluster.CONNECTION_OPTIONS),
+ "--address=%s" % self.queue,
+ "--broker=%s" % self.broker.host_port()])
+ self.sender.wait()
+ self.done = True
+
+ cluster = self.cluster(2)
+ # create a queue with rather draconian flow control settings
+ ssn0 = cluster[0].connect().session()
+ s0 = ssn0.sender("flq; {create:always, node:{type:queue, x-declare:{arguments:{'qpid.flow_stop_count':100, 'qpid.flow_resume_count':20}}}}")
+
+ # fire off the sending thread to broker[0], and wait until the queue
+ # hits flow control on broker[1]
+ sender = AsyncSender(cluster[0], "flq", count=110);
+ sender.start();
+
+ cluster[1].startQmf()
+ q_obj = [q for q in cluster[1].qmf_session.getObjects(_class="queue") if q.name == "flq"][0]
+ deadline = time.time() + 10
+ while not q_obj.flowStopped and time.time() < deadline:
+ q_obj.update()
+ assert q_obj.flowStopped
+ assert not sender.done
+ assert q_obj.msgDepth < 110
+
+ # Now drain enough messages on broker[1] to drop below the flow stop
+ # threshold, but not relieve flow control...
+ receiver = subprocess.Popen(["qpid-receive",
+ "--messages=15",
+ "--timeout=1",
+ "--print-content=no",
+ "--failover-updates",
+ "--connection-options={%s}"%(Cluster.CONNECTION_OPTIONS),
+ "--ack-frequency=1",
+ "--address=flq",
+ "--broker=%s" % cluster[1].host_port()])
+ receiver.wait()
+ q_obj.update()
+ assert q_obj.flowStopped
+ assert not sender.done
+ current_depth = q_obj.msgDepth
+
+ # add a new broker to the cluster, and verify that the queue is in flow
+ # control on that broker
+ cluster.start()
+ cluster[2].startQmf()
+ q_obj = [q for q in cluster[2].qmf_session.getObjects(_class="queue") if q.name == "flq"][0]
+ assert q_obj.flowStopped
+ assert q_obj.msgDepth == current_depth
+
+ # now drain the queue on broker[2], and verify that the sender becomes
+ # unblocked
+ receiver = subprocess.Popen(["qpid-receive",
+ "--messages=95",
+ "--timeout=1",
+ "--print-content=no",
+ "--failover-updates",
+ "--connection-options={%s}"%(Cluster.CONNECTION_OPTIONS),
+ "--ack-frequency=1",
+ "--address=flq",
+ "--broker=%s" % cluster[2].host_port()])
+ receiver.wait()
+ q_obj.update()
+ assert not q_obj.flowStopped
+ self.assertEqual(q_obj.msgDepth, 0)
+
+ # verify that the sender has become unblocked
+ sender.join(timeout=5)
+ assert not sender.isAlive()
+ assert sender.done
+
+ def test_blocked_queue_delete(self):
+ """Verify that producers which are blocked on a queue due to flow
+ control are unblocked when that queue is deleted.
+ """
+
+ cluster = self.cluster(2)
+ cluster[0].startQmf()
+ cluster[1].startQmf()
+
+ # configure a queue with a specific flow limit on first broker
+ ssn0 = cluster[0].connect().session()
+ s0 = ssn0.sender("flq; {create:always, node:{type:queue, x-declare:{arguments:{'qpid.flow_stop_count':5, 'qpid.flow_resume_count':3}}}}")
+ q1 = [q for q in cluster[0].qmf_session.getObjects(_class="queue") if q.name == "flq"][0]
+ oid = q1.getObjectId()
+ self.assertEqual(q1.name, "flq")
+ self.assertEqual(q1.arguments, {u'qpid.flow_stop_count': 5L, u'qpid.flow_resume_count': 3L})
+ assert not q1.flowStopped
+ self.assertEqual(q1.flowStoppedCount, 0)
+
+ # fill the queue on one broker until flow control is active
+ for x in range(5): s0.send(Message(str(x)))
+ sender = ShortTests.BlockedSend(s0, Message(str(6)))
+ sender.start() # Tests that sender does block
+ # Verify the broker queue goes into a flowStopped state
+ deadline = time.time() + 1
+ while not q1.flowStopped and time.time() < deadline: q1.update()
+ assert q1.flowStopped
+ self.assertEqual(q1.flowStoppedCount, 1)
+ sender.assert_blocked() # Still blocked
+
+ # Now verify the both brokers in cluster have same configuration
+ qs = cluster[1].qmf_session.getObjects(_objectId=oid)
+ self.assertEqual(len(qs), 1)
+ q2 = qs[0]
+ self.assertEqual(q2.name, "flq")
+ self.assertEqual(q2.arguments, {u'qpid.flow_stop_count': 5L, u'qpid.flow_resume_count': 3L})
+ assert q2.flowStopped
+ self.assertEqual(q2.flowStoppedCount, 1)
+
+ # now delete the blocked queue from other broker
+ ssn1 = cluster[1].connect().session()
+ self.evaluate_address(ssn1, "flq;{delete:always}")
+ sender.wait() # Verify no longer blocked.
+
+ ssn0.connection.close()
+ ssn1.connection.close()
+ cluster_test_logs.verify_logs()
+
+
+ def test_alternate_exchange_update(self):
+ """Verify that alternate-exchange on exchanges and queues is propagated to new members of a cluster. """
+ cluster = self.cluster(1)
+ s0 = cluster[0].connect().session()
+ # create alt queue bound to amq.fanout exchange, will be destination for alternate exchanges
+ self.evaluate_address(s0, "alt;{create:always,node:{x-bindings:[{exchange:'amq.fanout',queue:alt}]}}")
+ # create direct exchange ex with alternate-exchange amq.fanout and no queues bound
+ self.evaluate_address(s0, "ex;{create:always,node:{type:topic, x-declare:{type:'direct', alternate-exchange:'amq.fanout'}}}")
+ # create queue q with alternate-exchange amq.fanout
+ self.evaluate_address(s0, "q;{create:always,node:{type:queue, x-declare:{alternate-exchange:'amq.fanout'}}}")
+
+ def verify(broker):
+ s = broker.connect().session()
+ # Verify unmatched message goes to ex's alternate.
+ s.sender("ex").send("foo")
+ self.assertEqual("foo", s.receiver("alt").fetch(timeout=0).content)
+ # 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", s.receiver("alt").fetch(timeout=0).content)
+
+ verify(cluster[0])
+ cluster.start()
+ verify(cluster[1])
+
+ def test_binding_order(self):
+ """Regression test for binding order inconsistency in cluster"""
+ cluster = self.cluster(1)
+ c0 = cluster[0].connect()
+ s0 = c0.session()
+ # Declare multiple queues bound to same key on amq.topic
+ def declare(q,max=0):
+ if max: declare = 'x-declare:{arguments:{"qpid.max_count":%d, "qpid.flow_stop_count":0}}'%max
+ else: declare = 'x-declare:{}'
+ bind='x-bindings:[{queue:%s,key:key,exchange:"amq.topic"}]'%(q)
+ s0.sender("%s;{create:always,node:{%s,%s}}" % (q,declare,bind))
+ declare('d',max=4) # Only one with a limit
+ for q in ['c', 'b','a']: declare(q)
+ # Add a cluster member, send enough messages to exceed the max count
+ cluster.start()
+ try:
+ s = s0.sender('amq.topic/key')
+ for m in xrange(1,6): s.send(Message(str(m)))
+ self.fail("Expected capacity exceeded exception")
+ except messaging.exceptions.TargetCapacityExceeded: pass
+ c1 = cluster[1].connect()
+ s1 = c1.session()
+ s0 = c0.session() # Old session s0 is broken by exception.
+ # Verify queue contents are consistent.
+ for q in ['a','b','c','d']:
+ self.assertEqual(self.browse(s0, q), self.browse(s1, q))
+ # Verify queue contents are "best effort"
+ for q in ['a','b','c']: self.assert_browse(s1,q,[str(n) for n in xrange(1,6)])
+ self.assert_browse(s1,'d',[str(n) for n in xrange(1,5)])
+
+ def test_deleted_exchange(self):
+ """QPID-3215: cached exchange reference can cause cluster inconsistencies
+ if exchange is deleted/recreated
+ Verify stand-alone case
+ """
+ cluster = self.cluster()
+ # Verify we do not route message via an exchange that has been destroyed.
+ cluster.start()
+ s0 = cluster[0].connect().session()
+ self.evaluate_address(s0, "ex;{create:always,node:{type:topic}}")
+ self.evaluate_address(s0, "q;{create:always,node:{x-bindings:[{exchange:'ex',queue:q,key:foo}]}}")
+ send0 = s0.sender("ex/foo")
+ send0.send("foo")
+ self.assert_browse(s0, "q", ["foo"])
+ self.evaluate_address(s0, "ex;{delete:always}")
+ try:
+ send0.send("bar") # Should fail, exchange is deleted.
+ self.fail("Expected not-found exception")
+ except qpid.messaging.NotFound: pass
+ self.assert_browse(cluster[0].connect().session(), "q", ["foo"])
+
+ def test_deleted_exchange_inconsistent(self):
+ """QPID-3215: cached exchange reference can cause cluster inconsistencies
+ if exchange is deleted/recreated
+
+ Verify cluster inconsistency.
+ """
+ cluster = self.cluster()
+ cluster.start()
+ s0 = cluster[0].connect().session()
+ self.evaluate_address(s0, "ex;{create:always,node:{type:topic}}")
+ self.evaluate_address(s0, "q;{create:always,node:{x-bindings:[{exchange:'ex',queue:q,key:foo}]}}")
+ send0 = s0.sender("ex/foo")
+ send0.send("foo")
+ self.assert_browse(s0, "q", ["foo"])
+
+ cluster.start()
+ s1 = cluster[1].connect().session()
+ self.evaluate_address(s0, "ex;{delete:always}")
+ try:
+ send0.send("bar")
+ self.fail("Expected not-found exception")
+ except qpid.messaging.NotFound: pass
+
+ self.assert_browse(s1, "q", ["foo"])
+
+
+ def test_ttl_consistent(self):
+ """Ensure we don't get inconsistent errors with message that have TTL very close together"""
+ messages = [ Message(str(i), ttl=i/1000.0) for i in xrange(0,1000)]
+ messages.append(Message("x"))
+ cluster = self.cluster(2)
+ sender = cluster[0].connect().session().sender("q;{create:always}")
+
+ def fetch(b):
+ receiver = b.connect().session().receiver("q;{create:always}")
+ while receiver.fetch().content != "x": pass
+
+ for m in messages: sender.send(m, sync=False)
+ for m in messages: sender.send(m, sync=False)
+ fetch(cluster[0])
+ fetch(cluster[1])
+ for m in messages: sender.send(m, sync=False)
+ cluster.start()
+ fetch(cluster[2])
+
+# Some utility code for transaction tests
+XA_RBROLLBACK = 1
+XA_RBTIMEOUT = 2
+XA_OK = 0
+dtx_branch_counter = 0
+
+class DtxStatusException(Exception):
+ def __init__(self, expect, actual):
+ self.expect = expect
+ self.actual = actual
+
+ def str(self):
+ return "DtxStatusException(expect=%s, actual=%s)"%(self.expect, self.actual)
+
+class DtxTestFixture:
+ """Bundle together some common requirements for dtx tests."""
+ def __init__(self, test, broker, name, exclusive=False):
+ self.test = test
+ self.broker = broker
+ self.name = name
+ # Use old API. DTX is not supported in messaging API.
+ self.connection = broker.connect_old()
+ self.session = self.connection.session(name, 1) # 1 second timeout
+ self.queue = self.session.queue_declare(name, exclusive=exclusive)
+ self.session.dtx_select()
+ self.consumer = None
+
+ def xid(self, id=None):
+ if id is None: id = self.name
+ return self.session.xid(format=0, global_id=id)
+
+ def check_status(self, expect, actual):
+ if expect != actual: raise DtxStatusException(expect, actual)
+
+ def start(self, id=None, resume=False):
+ self.check_status(XA_OK, self.session.dtx_start(xid=self.xid(id), resume=resume).status)
+
+ def end(self, id=None, suspend=False):
+ self.check_status(XA_OK, self.session.dtx_end(xid=self.xid(id), suspend=suspend).status)
+
+ def prepare(self, id=None):
+ self.check_status(XA_OK, self.session.dtx_prepare(xid=self.xid(id)).status)
+
+ def commit(self, id=None, one_phase=True):
+ self.check_status(
+ XA_OK, self.session.dtx_commit(xid=self.xid(id), one_phase=one_phase).status)
+
+ def rollback(self, id=None):
+ self.check_status(XA_OK, self.session.dtx_rollback(xid=self.xid(id)).status)
+
+ def set_timeout(self, timeout, id=None):
+ self.session.dtx_set_timeout(xid=self.xid(id),timeout=timeout)
+
+ def send(self, messages):
+ for m in messages:
+ dp=self.session.delivery_properties(routing_key=self.name)
+ mp=self.session.message_properties()
+ self.session.message_transfer(message=qpid.datatypes.Message(dp, mp, m))
+
+ def accept(self):
+ """Accept 1 message from queue"""
+ consumer_tag="%s-consumer"%(self.name)
+ self.session.message_subscribe(queue=self.name, destination=consumer_tag)
+ self.session.message_flow(unit = self.session.credit_unit.message, value = 1, destination = consumer_tag)
+ self.session.message_flow(unit = self.session.credit_unit.byte, value = 0xFFFFFFFFL, destination = consumer_tag)
+ msg = self.session.incoming(consumer_tag).get(timeout=1)
+ self.session.message_cancel(destination=consumer_tag)
+ self.session.message_accept(qpid.datatypes.RangedSet(msg.id))
+ return msg
+
+
+ def verify(self, sessions, messages):
+ for s in sessions:
+ self.test.assert_browse(s, self.name, messages)
+
+class DtxTests(BrokerTest):
+
+ def test_dtx_update(self):
+ """Verify that DTX transaction state is updated to a new broker.
+ Start a collection of transactions, then add a new cluster member,
+ then verify they commit/rollback correctly on the new broker."""
+
+ # Note: multiple test have been bundled into one to avoid the need to start/stop
+ # multiple brokers per test.
+
+ cluster=self.cluster(1)
+ sessions = [cluster[0].connect().session()] # For verify
+
+ # Transaction that will be open when new member joins, then committed.
+ t1 = DtxTestFixture(self, cluster[0], "t1")
+ t1.start()
+ t1.send(["1", "2"])
+ t1.verify(sessions, []) # Not visible outside of transaction
+
+ # Transaction that will be open when new member joins, then rolled back.
+ t2 = DtxTestFixture(self, cluster[0], "t2")
+ t2.start()
+ t2.send(["1", "2"])
+
+ # Transaction that will be prepared when new member joins, then committed.
+ t3 = DtxTestFixture(self, cluster[0], "t3")
+ t3.start()
+ t3.send(["1", "2"])
+ t3.end()
+ t3.prepare()
+ t1.verify(sessions, []) # Not visible outside of transaction
+
+ # Transaction that will be prepared when new member joins, then rolled back.
+ t4 = DtxTestFixture(self, cluster[0], "t4")
+ t4.start()
+ t4.send(["1", "2"])
+ t4.end()
+ t4.prepare()
+
+ # Transaction using an exclusive queue
+ t5 = DtxTestFixture(self, cluster[0], "t5", exclusive=True)
+ t5.start()
+ t5.send(["1", "2"])
+
+ # Accept messages in a transaction before/after join then commit
+ t6 = DtxTestFixture(self, cluster[0], "t6")
+ t6.send(["a","b","c"])
+ t6.start()
+ self.assertEqual(t6.accept().body, "a");
+
+ # Accept messages in a transaction before/after join then roll back
+ t7 = DtxTestFixture(self, cluster[0], "t7")
+ t7.send(["a","b","c"])
+ t7.start()
+ self.assertEqual(t7.accept().body, "a");
+
+ # Ended, suspended transactions across join.
+ t8 = DtxTestFixture(self, cluster[0], "t8")
+ t8.start(id="1")
+ t8.send(["x"])
+ t8.end(id="1", suspend=True)
+ t8.start(id="2")
+ t8.send(["y"])
+ t8.end(id="2")
+ t8.start()
+ t8.send("z")
+
+
+ # Start new cluster member
+ cluster.start()
+ sessions.append(cluster[1].connect().session())
+
+ # Commit t1
+ t1.send(["3","4"])
+ t1.verify(sessions, [])
+ t1.end()
+ t1.commit(one_phase=True)
+ t1.verify(sessions, ["1","2","3","4"])
+
+ # Rollback t2
+ t2.send(["3","4"])
+ t2.end()
+ t2.rollback()
+ t2.verify(sessions, [])
+
+ # Commit t3
+ t3.commit(one_phase=False)
+ t3.verify(sessions, ["1","2"])
+
+ # Rollback t4
+ t4.rollback()
+ t4.verify(sessions, [])
+
+ # Commit t5
+ t5.send(["3","4"])
+ t5.verify(sessions, [])
+ t5.end()
+ t5.commit(one_phase=True)
+ t5.verify(sessions, ["1","2","3","4"])
+
+ # Commit t6
+ self.assertEqual(t6.accept().body, "b");
+ t6.verify(sessions, ["c"])
+ t6.end()
+ t6.commit(one_phase=True)
+ t6.session.close() # Make sure they're not requeued by the session.
+ t6.verify(sessions, ["c"])
+
+ # Rollback t7
+ self.assertEqual(t7.accept().body, "b");
+ t7.end()
+ t7.rollback()
+ t7.verify(sessions, ["a", "b", "c"])
+
+ # Resume t8
+ t8.end()
+ t8.commit(one_phase=True)
+ t8.start("1", resume=True)
+ t8.end("1")
+ t8.commit("1", one_phase=True)
+ t8.commit("2", one_phase=True)
+ t8.verify(sessions, ["z", "x","y"])
+
+
+ def test_dtx_failover_rollback(self):
+ """Kill a broker during a transaction, verify we roll back correctly"""
+ cluster=self.cluster(1, expect=EXPECT_EXIT_FAIL)
+ cluster.start(expect=EXPECT_RUNNING)
+
+ # Test unprepared at crash
+ t1 = DtxTestFixture(self, cluster[0], "t1")
+ t1.send(["a"]) # Not in transaction
+ t1.start()
+ t1.send(["b"]) # In transaction
+
+ # Test prepared at crash
+ t2 = DtxTestFixture(self, cluster[0], "t2")
+ t2.send(["a"]) # Not in transaction
+ t2.start()
+ t2.send(["b"]) # In transaction
+ t2.end()
+ t2.prepare()
+
+ # Crash the broker
+ cluster[0].kill()
+
+ # Transactional changes should not appear
+ s = cluster[1].connect().session();
+ self.assert_browse(s, "t1", ["a"])
+ self.assert_browse(s, "t2", ["a"])
+
+ def test_dtx_timeout(self):
+ """Verify that dtx timeout works"""
+ cluster = self.cluster(1)
+ t1 = DtxTestFixture(self, cluster[0], "t1")
+ t1.start()
+ t1.set_timeout(1)
+ time.sleep(1.1)
+ try:
+ t1.end()
+ self.fail("Expected rollback timeout.")
+ except DtxStatusException, e:
+ self.assertEqual(e.actual, XA_RBTIMEOUT)
+
+class TxTests(BrokerTest):
+
+ def test_tx_update(self):
+ """Verify that transaction state is updated to a new broker"""
+
+ def make_message(session, body=None, key=None, id=None):
+ dp=session.delivery_properties(routing_key=key)
+ mp=session.message_properties(correlation_id=id)
+ return qpid.datatypes.Message(dp, mp, body)
+
+ cluster=self.cluster(1)
+ # Use old API. TX is not supported in messaging API.
+ c = cluster[0].connect_old()
+ s = c.session("tx-session", 1)
+ s.queue_declare(queue="q")
+ # Start transaction
+ s.tx_select()
+ s.message_transfer(message=make_message(s, "1", "q"))
+ # Start new member mid-transaction
+ cluster.start()
+ # Do more work
+ s.message_transfer(message=make_message(s, "2", "q"))
+ # Commit the transaction and verify the results.
+ s.tx_commit()
+ for b in cluster: self.assert_browse(b.connect().session(), "q", ["1","2"])
+
+
class LongTests(BrokerTest):
"""Tests that can run for a long time if -DDURATION=<minutes> is set"""
def duration(self):
@@ -316,22 +1023,28 @@ class LongTests(BrokerTest):
# Original cluster will all be killed so expect exit with failure
cluster = self.cluster(3, expect=EXPECT_EXIT_FAIL)
+ for b in cluster: b.ready() # Wait for brokers to be ready
for b in cluster: ErrorGenerator(b)
# Start sender and receiver threads
cluster[0].declare_queue("test-queue")
- sender = NumberedSender(cluster[1], 1000) # Max queue depth
- receiver = NumberedReceiver(cluster[2], sender)
+ sender = NumberedSender(cluster[0], 1000) # Max queue depth
+ receiver = NumberedReceiver(cluster[0], sender)
receiver.start()
sender.start()
+ # Wait for sender & receiver to get up and running
+ retry(lambda: receiver.received > 0)
# Kill original brokers, start new ones for the duration.
endtime = time.time() + self.duration()
i = 0
while time.time() < endtime:
+ sender.sender.assert_running()
+ receiver.receiver.assert_running()
cluster[i].kill()
i += 1
b = cluster.start(expect=EXPECT_EXIT_FAIL)
+ for b in cluster[i:]: b.ready()
ErrorGenerator(b)
time.sleep(5)
sender.stop()
@@ -362,24 +1075,24 @@ class LongTests(BrokerTest):
if self.stopped: break
self.process = self.broker.test.popen(
self.cmd, expect=EXPECT_UNKNOWN)
- finally: self.lock.release()
- try: exit = self.process.wait()
+ finally:
+ self.lock.release()
+ try:
+ exit = self.process.wait()
except OSError, e:
- # Seems to be a race in wait(), it throws
- # "no such process" during test shutdown.
- # Doesn't indicate a test error, ignore.
- return
+ # Process may already have been killed by self.stop()
+ break
except Exception, e:
self.process.unexpected(
"client of %s: %s"%(self.broker.name, e))
self.lock.acquire()
try:
- # Quit and ignore errors if stopped or expecting failure.
if self.stopped: break
if exit != 0:
self.process.unexpected(
"client of %s exit code %s"%(self.broker.name, exit))
- finally: self.lock.release()
+ finally:
+ self.lock.release()
except Exception, e:
self.error = RethrownException("Error in ClientLoop.run")
@@ -401,7 +1114,7 @@ class LongTests(BrokerTest):
args += ["--log-enable=trace+:management"]
# Use store if present.
if BrokerTest.store_lib: args +=["--load-module", BrokerTest.store_lib]
- cluster = self.cluster(3, args)
+ cluster = self.cluster(3, args, expect=EXPECT_EXIT_FAIL) # brokers will be killed
clients = [] # Per-broker list of clients that only connect to one broker.
mclients = [] # Management clients that connect to every broker in the cluster.
@@ -410,10 +1123,12 @@ class LongTests(BrokerTest):
"""Start ordinary clients for a broker."""
cmds=[
["qpid-tool", "localhost:%s"%(broker.port())],
- ["qpid-perftest", "--count", 50000,
+ ["qpid-perftest", "--count=5000", "--durable=yes",
"--base-name", str(qpid.datatypes.uuid4()), "--port", broker.port()],
- ["qpid-queue-stats", "-a", "localhost:%s" %(broker.port())],
- ["testagent", "localhost", str(broker.port())] ]
+ ["qpid-txtest", "--queue-base-name", "tx-%s"%str(qpid.datatypes.uuid4()),
+ "--port", broker.port()],
+ ["qpid-queue-stats", "-a", "localhost:%s" %(broker.port())]
+ ]
clients.append([ClientLoop(broker, cmd) for cmd in cmds])
def start_mclients(broker):
@@ -422,7 +1137,8 @@ class LongTests(BrokerTest):
mclients.append(ClientLoop(broker, cmd))
endtime = time.time() + self.duration()
- runtime = self.duration() / 4 # First run is longer, use quarter of duration.
+ # For long duration, first run is a quarter of the duration.
+ runtime = min(5.0, self.duration() / 3.0)
alive = 0 # First live cluster member
for i in range(len(cluster)): start_clients(cluster[i])
start_mclients(cluster[alive])
@@ -433,7 +1149,7 @@ class LongTests(BrokerTest):
for b in cluster[alive:]: b.ready() # Check if a broker crashed.
# Kill the first broker, expect the clients to fail.
b = cluster[alive]
- b.expect = EXPECT_EXIT_FAIL
+ b.ready()
b.kill()
# Stop the brokers clients and all the mclients.
for c in clients[alive] + mclients:
@@ -443,26 +1159,251 @@ class LongTests(BrokerTest):
mclients = []
# Start another broker and clients
alive += 1
- cluster.start()
+ cluster.start(expect=EXPECT_EXIT_FAIL)
+ cluster[-1].ready() # Wait till its ready
start_clients(cluster[-1])
start_mclients(cluster[alive])
for c in chain(mclients, *clients):
c.stop()
-
+ for b in cluster[alive:]:
+ b.ready() # Verify still alive
+ b.kill()
# Verify that logs are consistent
cluster_test_logs.verify_logs()
def test_management_qmf2(self):
self.test_management(args=["--mgmt-qmf2=yes"])
- def test_connect_consistent(self): # FIXME aconway 2011-01-18:
+ def test_connect_consistent(self):
args=["--mgmt-pub-interval=1","--log-enable=trace+:management"]
cluster = self.cluster(2, args=args)
end = time.time() + self.duration()
while (time.time() < end): # Get a management interval
for i in xrange(1000): cluster[0].connect().close()
+ cluster_test_logs.verify_logs()
+
+ def test_flowlimit_failover(self):
+ """Test fail-over during continuous send-receive with flow control
+ active.
+ """
+
+ # Original cluster will all be killed so expect exit with failure
+ cluster = self.cluster(3, expect=EXPECT_EXIT_FAIL)
+ for b in cluster: b.ready() # Wait for brokers to be ready
+
+ # create a queue with rather draconian flow control settings
+ ssn0 = cluster[0].connect().session()
+ s0 = ssn0.sender("test-queue; {create:always, node:{type:queue, x-declare:{arguments:{'qpid.flow_stop_count':2000, 'qpid.flow_resume_count':100}}}}")
+
+ receiver = NumberedReceiver(cluster[0])
+ receiver.start()
+ senders = [NumberedSender(cluster[0]) for i in range(1,3)]
+ for s in senders:
+ s.start()
+ # Wait for senders & receiver to get up and running
+ retry(lambda: receiver.received > 2*senders)
+
+ # Kill original brokers, start new ones for the duration.
+ endtime = time.time() + self.duration();
+ i = 0
+ while time.time() < endtime:
+ for s in senders: s.sender.assert_running()
+ receiver.receiver.assert_running()
+ for b in cluster[i:]: b.ready() # Check if any broker crashed.
+ cluster[i].kill()
+ i += 1
+ b = cluster.start(expect=EXPECT_EXIT_FAIL)
+ time.sleep(5)
+ for s in senders:
+ s.stop()
+ receiver.stop()
+ for i in range(i, len(cluster)): cluster[i].kill()
+
+ def test_ttl_failover(self):
+ """Test that messages with TTL don't cause problems in a cluster with failover"""
+
+ class Client(StoppableThread):
+
+ def __init__(self, broker):
+ StoppableThread.__init__(self)
+ self.connection = broker.connect(reconnect=True)
+ self.auto_fetch_reconnect_urls(self.connection)
+ self.session = self.connection.session()
+
+ def auto_fetch_reconnect_urls(self, conn):
+ """Replacment for qpid.messaging.util version which is noisy"""
+ ssn = conn.session("auto-fetch-reconnect-urls")
+ rcv = ssn.receiver("amq.failover")
+ rcv.capacity = 10
+
+ def main():
+ while True:
+ try:
+ msg = rcv.fetch()
+ qpid.messaging.util.set_reconnect_urls(conn, msg)
+ ssn.acknowledge(msg, sync=False)
+ except messaging.exceptions.LinkClosed: return
+ except messaging.exceptions.ConnectionError: return
+
+ thread = Thread(name="auto-fetch-reconnect-urls", target=main)
+ thread.setDaemon(True)
+ thread.start()
+
+ def stop(self):
+ StoppableThread.stop(self)
+ self.connection.detach()
+
+ class Sender(Client):
+ def __init__(self, broker, address):
+ Client.__init__(self, broker)
+ self.sent = 0 # Number of messages _reliably_ sent.
+ self.sender = self.session.sender(address, capacity=1000)
+
+ def send_counted(self, ttl):
+ self.sender.send(Message(str(self.sent), ttl=ttl))
+ self.sent += 1
+
+ def run(self):
+ while not self.stopped:
+ choice = random.randint(0,4)
+ if choice == 0: self.send_counted(None) # No ttl
+ elif choice == 1: self.send_counted(100000) # Large ttl
+ else: # Small ttl, might expire
+ self.sender.send(Message("", ttl=random.random()/10))
+ self.sender.send(Message("z"), sync=True) # Chaser.
+
+ class Receiver(Client):
+
+ def __init__(self, broker, address):
+ Client.__init__(self, broker)
+ self.received = 0 # Number of non-empty (reliable) messages received.
+ self.receiver = self.session.receiver(address, capacity=1000)
+ def run(self):
+ try:
+ while True:
+ m = self.receiver.fetch(1)
+ if m.content == "z": break
+ if m.content: # Ignore unreliable messages
+ # Ignore duplicates
+ if int(m.content) == self.received: self.received += 1
+ except Exception,e: self.error = e
+
+ # def test_ttl_failover
+
+ # Original cluster will all be killed so expect exit with failure
+ # Set small purge interval.
+ cluster = self.cluster(3, expect=EXPECT_EXIT_FAIL, args=["--queue-purge-interval=1"])
+ for b in cluster: b.ready() # Wait for brokers to be ready
+
+ # Python client failover produces noisy WARN logs, disable temporarily
+ logger = logging.getLogger()
+ log_level = logger.getEffectiveLevel()
+ logger.setLevel(logging.ERROR)
+ sender = None
+ receiver = None
+ try:
+ # Start sender and receiver threads
+ receiver = Receiver(cluster[0], "q;{create:always}")
+ receiver.start()
+ sender = Sender(cluster[0], "q;{create:always}")
+ sender.start()
+ # Wait for sender & receiver to get up and running
+ retry(lambda: receiver.received > 0)
+
+ # Kill brokers in a cycle.
+ endtime = time.time() + self.duration()
+ runtime = min(5.0, self.duration() / 4.0)
+ i = 0
+ while time.time() < endtime:
+ for b in cluster[i:]: b.ready() # Check if any broker crashed.
+ cluster[i].kill()
+ i += 1
+ b = cluster.start(expect=EXPECT_EXIT_FAIL)
+ b.ready()
+ time.sleep(runtime)
+ sender.stop()
+ receiver.stop()
+ for b in cluster[i:]:
+ b.ready() # Check it didn't crash
+ b.kill()
+ self.assertEqual(sender.sent, receiver.received)
cluster_test_logs.verify_logs()
+ finally:
+ # Detach to avoid slow reconnect attempts during shut-down if test fails.
+ if sender: sender.connection.detach()
+ if receiver: receiver.connection.detach()
+ logger.setLevel(log_level)
+
+ def test_msg_group_failover(self):
+ """Test fail-over during continuous send-receive of grouped messages.
+ """
+
+ class GroupedTrafficGenerator(Thread):
+ def __init__(self, url, queue, group_key):
+ Thread.__init__(self)
+ self.url = url
+ self.queue = queue
+ self.group_key = group_key
+ self.status = -1
+
+ def run(self):
+ # generate traffic for approx 10 seconds (2011msgs / 200 per-sec)
+ cmd = ["msg_group_test",
+ "--broker=%s" % self.url,
+ "--address=%s" % self.queue,
+ "--connection-options={%s}" % (Cluster.CONNECTION_OPTIONS),
+ "--group-key=%s" % self.group_key,
+ "--receivers=2",
+ "--senders=3",
+ "--messages=2011",
+ "--send-rate=200",
+ "--capacity=11",
+ "--ack-frequency=23",
+ "--allow-duplicates",
+ "--group-size=37",
+ "--randomize-group-size",
+ "--interleave=13"]
+ # "--trace"]
+ self.generator = Popen( cmd );
+ self.status = self.generator.wait()
+ return self.status
+
+ def results(self):
+ self.join(timeout=30) # 3x assumed duration
+ if self.isAlive(): return -1
+ return self.status
+
+ # Original cluster will all be killed so expect exit with failure
+ cluster = self.cluster(3, expect=EXPECT_EXIT_FAIL, args=["-t"])
+ for b in cluster: b.ready() # Wait for brokers to be ready
+
+ # create a queue with rather draconian flow control settings
+ ssn0 = cluster[0].connect().session()
+ q_args = "{'qpid.group_header_key':'group-id', 'qpid.shared_msg_group':1}"
+ s0 = ssn0.sender("test-group-q; {create:always, node:{type:queue, x-declare:{arguments:%s}}}" % q_args)
+
+ # Kill original brokers, start new ones for the duration.
+ endtime = time.time() + self.duration();
+ i = 0
+ while time.time() < endtime:
+ traffic = GroupedTrafficGenerator( cluster[i].host_port(),
+ "test-group-q", "group-id" )
+ traffic.start()
+ time.sleep(1)
+
+ for x in range(2):
+ for b in cluster[i:]: b.ready() # Check if any broker crashed.
+ cluster[i].kill()
+ i += 1
+ b = cluster.start(expect=EXPECT_EXIT_FAIL)
+ time.sleep(1)
+
+ # wait for traffic to finish, verify success
+ self.assertEqual(0, traffic.results())
+
+ for i in range(i, len(cluster)): cluster[i].kill()
+
class StoreTests(BrokerTest):
"""
diff --git a/cpp/src/tests/exception_test.cpp b/cpp/src/tests/exception_test.cpp
index 3536ffddbe..3e844b4e58 100644
--- a/cpp/src/tests/exception_test.cpp
+++ b/cpp/src/tests/exception_test.cpp
@@ -92,32 +92,30 @@ QPID_AUTO_TEST_CASE(TestSessionBusy) {
}
QPID_AUTO_TEST_CASE(DisconnectedPop) {
- ProxySessionFixture fix;
- ProxyConnection c(fix.broker->getPort(Broker::TCP_TRANSPORT));
+ SessionFixture fix;
fix.session.queueDeclare(arg::queue="q");
fix.subs.subscribe(fix.lq, "q");
Catcher<TransportFailure> pop(bind(&LocalQueue::pop, &fix.lq, sys::TIME_SEC));
- fix.connection.proxy.close();
+ fix.shutdownBroker();
BOOST_CHECK(pop.join());
}
QPID_AUTO_TEST_CASE(DisconnectedListen) {
- ProxySessionFixture fix;
+ SessionFixture fix;
struct NullListener : public MessageListener {
void received(Message&) { BOOST_FAIL("Unexpected message"); }
} l;
- ProxyConnection c(fix.broker->getPort(Broker::TCP_TRANSPORT));
fix.session.queueDeclare(arg::queue="q");
fix.subs.subscribe(l, "q");
Catcher<TransportFailure> runner(bind(&SubscriptionManager::run, boost::ref(fix.subs)));
- fix.connection.proxy.close();
- runner.join();
+ fix.shutdownBroker();
+ runner.join();
BOOST_CHECK_THROW(fix.session.queueDeclare(arg::queue="x"), TransportFailure);
}
QPID_AUTO_TEST_CASE(NoSuchQueueTest) {
- ProxySessionFixture fix;
+ SessionFixture fix;
ScopedSuppressLogging sl; // Suppress messages for expected errors.
BOOST_CHECK_THROW(fix.subs.subscribe(fix.lq, "no such queue"), NotFoundException);
}
diff --git a/cpp/src/tests/federated_topic_test b/cpp/src/tests/federated_topic_test
index b1063c7e8c..2e55ddcfaa 100755
--- a/cpp/src/tests/federated_topic_test
+++ b/cpp/src/tests/federated_topic_test
@@ -42,13 +42,12 @@ while getopts "s:m:b:" opt ; do
esac
done
-MY_DIR=$(dirname $(which $0))
source ./test_env.sh
trap stop_brokers EXIT
start_broker() {
- ${MY_DIR}/../qpidd --daemon --port 0 --no-module-dir --no-data-dir --auth no > qpidd.port
+ $QPIDD_EXEC --daemon --port 0 --no-module-dir --no-data-dir --auth no > qpidd.port
}
start_brokers() {
@@ -76,39 +75,39 @@ subscribe() {
echo Subscriber $1 connecting on $MY_PORT
LOG="subscriber_$1.log"
- ${MY_DIR}/topic_listener -p $MY_PORT > $LOG 2>&1 && rm -f $LOG
+ ./qpid-topic-listener -p $MY_PORT > $LOG 2>&1 && rm -f $LOG
}
publish() {
- ${MY_DIR}/topic_publisher --messages $MESSAGES --batches $BATCHES --subscribers $SUBSCRIBERS -p $PORT_A
+ ./qpid-topic-publisher --messages $MESSAGES --batches $BATCHES --subscribers $SUBSCRIBERS -p $PORT_A
}
setup_routes() {
- BROKER_A="localhost:$PORT_A"
- BROKER_B="localhost:$PORT_B"
- BROKER_C="localhost:$PORT_C"
+ BROKER_A="daffodil:$PORT_A"
+ BROKER_B="daffodil:$PORT_B"
+ BROKER_C="daffodil:$PORT_C"
if (($VERBOSE)); then
echo "Establishing routes for topic..."
fi
- $PYTHON_COMMANDS/qpid-route route add $BROKER_B $BROKER_A amq.topic topic_control B B
- $PYTHON_COMMANDS/qpid-route route add $BROKER_C $BROKER_B amq.topic topic_control C C
+ $QPID_ROUTE_EXEC route add $BROKER_B $BROKER_A amq.topic topic_control B B
+ $QPID_ROUTE_EXEC route add $BROKER_C $BROKER_B amq.topic topic_control C C
if (($VERBOSE)); then
echo "linked A->B->C"
fi
- $PYTHON_COMMANDS/qpid-route route add $BROKER_B $BROKER_C amq.topic topic_control B B
- $PYTHON_COMMANDS/qpid-route route add $BROKER_A $BROKER_B amq.topic topic_control A A
+ $QPID_ROUTE_EXEC route add $BROKER_B $BROKER_C amq.topic topic_control B B
+ $QPID_ROUTE_EXEC route add $BROKER_A $BROKER_B amq.topic topic_control A A
if (($VERBOSE)); then
echo "linked C->B->A"
echo "Establishing routes for response queue..."
fi
- $PYTHON_COMMANDS/qpid-route route add $BROKER_B $BROKER_C amq.direct response B B
- $PYTHON_COMMANDS/qpid-route route add $BROKER_A $BROKER_B amq.direct response A A
+ $QPID_ROUTE_EXEC route add $BROKER_B $BROKER_C amq.direct response B B
+ $QPID_ROUTE_EXEC route add $BROKER_A $BROKER_B amq.direct response A A
if (($VERBOSE)); then
echo "linked C->B->A"
for b in $BROKER_A $BROKER_B $BROKER_C; do
echo "Routes for $b"
- $PYTHON_COMMANDS/qpid-route route list $b
+ $QPID_ROUTE_EXEC route list $b
done
fi
}
diff --git a/cpp/src/tests/federation.py b/cpp/src/tests/federation.py
index 973a1d366c..7d613b98ce 100755
--- a/cpp/src/tests/federation.py
+++ b/cpp/src/tests/federation.py
@@ -23,7 +23,7 @@ from qpid.testlib import TestBase010
from qpid.datatypes import Message
from qpid.queue import Empty
from qpid.util import URL
-from time import sleep
+from time import sleep, time
class _FedBroker(object):
@@ -111,18 +111,18 @@ class FederationTests(TestBase010):
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)
+ self.assertEqual(result.status, 0, result)
link = qmf.getObjects(_class="link")[0]
result = link.bridge(False, "amq.direct", "amq.direct", "my-key", "", "", False, False, False, 0)
- self.assertEqual(result.status, 0)
+ self.assertEqual(result.status, 0, result)
bridge = qmf.getObjects(_class="bridge")[0]
result = bridge.close()
- self.assertEqual(result.status, 0)
+ self.assertEqual(result.status, 0, result)
result = link.close()
- self.assertEqual(result.status, 0)
+ self.assertEqual(result.status, 0, result)
self.verify_cleanup()
@@ -133,11 +133,11 @@ class FederationTests(TestBase010):
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)
+ 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)
- self.assertEqual(result.status, 0)
+ self.assertEqual(result.status, 0, result)
bridge = qmf.getObjects(_class="bridge")[0]
@@ -165,9 +165,9 @@ class FederationTests(TestBase010):
except Empty: None
result = bridge.close()
- self.assertEqual(result.status, 0)
+ self.assertEqual(result.status, 0, result)
result = link.close()
- self.assertEqual(result.status, 0)
+ self.assertEqual(result.status, 0, result)
self.verify_cleanup()
@@ -178,11 +178,11 @@ class FederationTests(TestBase010):
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)
+ self.assertEqual(result.status, 0, result)
link = qmf.getObjects(_class="link")[0]
result = link.bridge(False, "amq.direct", "amq.fanout", "my-key", "", "", False, True, False, 0)
- self.assertEqual(result.status, 0)
+ self.assertEqual(result.status, 0, result)
bridge = qmf.getObjects(_class="bridge")[0]
@@ -209,9 +209,9 @@ class FederationTests(TestBase010):
except Empty: None
result = bridge.close()
- self.assertEqual(result.status, 0)
+ self.assertEqual(result.status, 0, result)
result = link.close()
- self.assertEqual(result.status, 0)
+ self.assertEqual(result.status, 0, result)
self.verify_cleanup()
@@ -236,11 +236,11 @@ class FederationTests(TestBase010):
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)
+ self.assertEqual(result.status, 0, result)
link = qmf.getObjects(_class="link")[0]
result = link.bridge(False, "my-bridge-queue", "amq.fanout", "my-key", "", "", True, False, False, 1)
- self.assertEqual(result.status, 0)
+ self.assertEqual(result.status, 0, result)
bridge = qmf.getObjects(_class="bridge")[0]
sleep(3)
@@ -262,6 +262,63 @@ class FederationTests(TestBase010):
except Empty: None
result = bridge.close()
+ self.assertEqual(result.status, 0, result)
+ result = link.close()
+ self.assertEqual(result.status, 0, result)
+
+ self.verify_cleanup()
+
+ def test_pull_from_queue_recovery(self):
+ session = self.session
+
+ #setup queue on remote broker and add some messages
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_pull_from_queue_recovery")
+ r_session.queue_declare(queue="my-bridge-queue", auto_delete=True)
+ for i in range(1, 6):
+ dp = r_session.delivery_properties(routing_key="my-bridge-queue")
+ r_session.message_transfer(message=Message(dp, "Message %d" % i))
+
+ #setup queue to receive messages from local broker
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="amq.fanout")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ 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, "my-bridge-queue", "amq.fanout", "my-key", "", "", True, False, False, 1)
+ self.assertEqual(result.status, 0, result)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+ sleep(5)
+
+ #recreate the remote bridge queue to invalidate the bridge session
+ r_session.queue_delete (queue="my-bridge-queue", if_empty=False, if_unused=False)
+ r_session.queue_declare(queue="my-bridge-queue", auto_delete=True)
+
+ #add some more messages (i.e. after bridge was created)
+ for i in range(6, 11):
+ dp = r_session.delivery_properties(routing_key="my-bridge-queue")
+ r_session.message_transfer(message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ try:
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ except Empty:
+ self.fail("Failed to find expected message containing 'Message %d'" % i)
+ try:
+ extra = queue.get(timeout=1)
+ self.fail("Got unexpected message in queue: " + extra.body)
+ except Empty: None
+
+ result = bridge.close()
self.assertEqual(result.status, 0)
result = link.close()
self.assertEqual(result.status, 0)
@@ -649,10 +706,17 @@ class FederationTests(TestBase010):
self.verify_cleanup()
- def test_dynamic_headers(self):
+ def test_dynamic_headers_any(self):
+ self.do_test_dynamic_headers('any')
+
+ def test_dynamic_headers_all(self):
+ self.do_test_dynamic_headers('all')
+
+
+ def do_test_dynamic_headers(self, match_mode):
session = self.session
r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
- r_session = r_conn.session("test_dynamic_headers")
+ r_session = r_conn.session("test_dynamic_headers_%s" % match_mode)
session.exchange_declare(exchange="fed.headers", type="headers")
r_session.exchange_declare(exchange="fed.headers", type="headers")
@@ -671,7 +735,7 @@ class FederationTests(TestBase010):
sleep(5)
session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
- session.exchange_bind(queue="fed1", exchange="fed.headers", binding_key="key1", arguments={'x-match':'any', 'class':'first'})
+ session.exchange_bind(queue="fed1", exchange="fed.headers", binding_key="key1", arguments={'x-match':match_mode, 'class':'first'})
self.subscribe(queue="fed1", destination="f1")
queue = session.incoming("f1")
@@ -1791,3 +1855,301 @@ class FederationTests(TestBase010):
if headers:
return headers[name]
return None
+
+ def test_dynamic_topic_bounce(self):
+ """ Bounce the connection between federated Topic Exchanges.
+ """
+ class Params:
+ def exchange_type(self): return "topic"
+ def bind_queue(self, ssn, qname, ename):
+ ssn.exchange_bind(queue=qname, exchange=ename,
+ binding_key="spud.*")
+ def unbind_queue(self, ssn, qname, ename):
+ ssn.exchange_unbind(queue=qname, exchange=ename, binding_key="spud.*")
+ def delivery_properties(self, ssn):
+ return ssn.delivery_properties(routing_key="spud.boy")
+
+ self.generic_dynamic_bounce_test(Params())
+
+ def test_dynamic_direct_bounce(self):
+ """ Bounce the connection between federated Direct Exchanges.
+ """
+ class Params:
+ def exchange_type(self): return "direct"
+ def bind_queue(self, ssn, qname, ename):
+ ssn.exchange_bind(queue=qname, exchange=ename, binding_key="spud")
+ def unbind_queue(self, ssn, qname, ename):
+ ssn.exchange_unbind(queue=qname, exchange=ename, binding_key="spud")
+ def delivery_properties(self, ssn):
+ return ssn.delivery_properties(routing_key="spud")
+ self.generic_dynamic_bounce_test(Params())
+
+ def test_dynamic_fanout_bounce(self):
+ """ Bounce the connection between federated Fanout Exchanges.
+ """
+ class Params:
+ def exchange_type(self): return "fanout"
+ def bind_queue(self, ssn, qname, ename):
+ ssn.exchange_bind(queue=qname, exchange=ename)
+ def unbind_queue(self, ssn, qname, ename):
+ ssn.exchange_unbind(queue=qname, exchange=ename)
+ def delivery_properties(self, ssn):
+ return ssn.delivery_properties(routing_key="spud")
+ self.generic_dynamic_bounce_test(Params())
+
+ def test_dynamic_headers_bounce(self):
+ """ Bounce the connection between federated Headers Exchanges.
+ """
+ class Params:
+ def exchange_type(self): return "headers"
+ def bind_queue(self, ssn, qname, ename):
+ ssn.exchange_bind(queue=qname, exchange=ename,
+ binding_key="spud", arguments={'x-match':'any', 'class':'first'})
+ def unbind_queue(self, ssn, qname, ename):
+ ssn.exchange_unbind(queue=qname, exchange=ename, binding_key="spud")
+ def delivery_properties(self, ssn):
+ return ssn.message_properties(application_headers={'class':'first'})
+ ## @todo KAG - re-enable once federation bugs with headers exchanges
+ ## are fixed.
+ #self.generic_dynamic_bounce_test(Params())
+ return
+
+
+ def generic_dynamic_bounce_test(self, params):
+ """ Verify that a federated broker can maintain a binding to a local
+ queue using the same key as a remote binding. Destroy and reconnect
+ the federation link, and verify routes are restored correctly.
+ See QPID-3170.
+ Topology:
+
+ Queue1 <---"Key"---B0<==[Federated Exchange]==>B1---"Key"--->Queue2
+ """
+ session = self.session
+
+ # create the federation
+
+ self.startQmf()
+ qmf = self.qmf
+
+ self._setup_brokers()
+
+ # create exchange on each broker, and retrieve the corresponding
+ # management object for that exchange
+
+ exchanges=[]
+ for _b in self._brokers[0:2]:
+ _b.client_session.exchange_declare(exchange="fedX", type=params.exchange_type())
+ self.assertEqual(_b.client_session.exchange_query(name="fedX").type,
+ params.exchange_type(), "exchange_declare failed!")
+ # pull the exchange out of qmf...
+ retries = 0
+ my_exchange = None
+ timeout = time() + 10
+ while my_exchange is None and time() <= timeout:
+ objs = qmf.getObjects(_broker=_b.qmf_broker, _class="exchange")
+ for ooo in objs:
+ if ooo.name == "fedX":
+ my_exchange = ooo
+ break
+ if my_exchange is None:
+ self.fail("QMF failed to find new exchange!")
+ exchanges.append(my_exchange)
+
+ #
+ # on each broker, create a local queue bound to the exchange with the
+ # same key value.
+ #
+
+ self._brokers[0].client_session.queue_declare(queue="fedX1", exclusive=True, auto_delete=True)
+ params.bind_queue(self._brokers[0].client_session, "fedX1", "fedX")
+ self.subscribe(self._brokers[0].client_session, queue="fedX1", destination="f1")
+ queue_0 = self._brokers[0].client_session.incoming("f1")
+
+ self._brokers[1].client_session.queue_declare(queue="fedX1", exclusive=True, auto_delete=True)
+ params.bind_queue(self._brokers[1].client_session, "fedX1", "fedX")
+ self.subscribe(self._brokers[1].client_session, queue="fedX1", destination="f1")
+ queue_1 = self._brokers[1].client_session.incoming("f1")
+
+ # now federate the two brokers
+
+ # connect B0 --> B1
+ result = self._brokers[1].qmf_object.connect(self._brokers[0].host,
+ self._brokers[0].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # connect B1 --> B0
+ result = self._brokers[0].qmf_object.connect(self._brokers[1].host,
+ self._brokers[1].port,
+ False, "PLAIN", "guest", "guest", "tcp")
+ self.assertEqual(result.status, 0)
+
+ # for each link, bridge the "fedX" exchanges:
+
+ for _l in qmf.getObjects(_class="link"):
+ # print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.getBroker())))
+ result = _l.bridge(False, # durable
+ "fedX", # src
+ "fedX", # dst
+ "", # key
+ "", # tag
+ "", # excludes
+ False, # srcIsQueue
+ False, # srcIsLocal
+ True, # dynamic
+ 0) # sync
+ self.assertEqual(result.status, 0)
+
+ # wait for all the inter-broker links to become operational
+ operational = False
+ timeout = time() + 10
+ while not operational and time() <= timeout:
+ 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
+ self.failUnless(operational, "inter-broker links failed to become operational.")
+
+ # @todo - There is no way to determine when the bridge objects become
+ # active.
+
+ # wait until the binding key has propagated to each broker - each
+ # broker should see 2 bindings (1 local, 1 remote)
+
+ binding_counts = [2, 2]
+ self.assertEqual(len(binding_counts), len(exchanges), "Update Test!")
+ for i in range(2):
+ exchanges[i].update()
+ timeout = time() + 10
+ while exchanges[i].bindingCount < binding_counts[i] and time() <= timeout:
+ exchanges[i].update()
+ self.failUnless(exchanges[i].bindingCount == binding_counts[i])
+
+ # send 10 msgs to B0
+ for i in range(1, 11):
+ # dp = self._brokers[0].client_session.delivery_properties(routing_key=params.routing_key())
+ dp = params.delivery_properties(self._brokers[0].client_session)
+ self._brokers[0].client_session.message_transfer(destination="fedX", message=Message(dp, "Message_trp %d" % i))
+
+ # get exactly 10 msgs on B0's local queue and B1's queue
+ for i in range(1, 11):
+ try:
+ msg = queue_0.get(timeout=5)
+ self.assertEqual("Message_trp %d" % i, msg.body)
+ msg = queue_1.get(timeout=5)
+ self.assertEqual("Message_trp %d" % i, msg.body)
+ except Empty:
+ self.fail("Only got %d msgs - expected 10" % i)
+ try:
+ extra = queue_0.get(timeout=1)
+ self.fail("Got unexpected message in queue_0: " + extra.body)
+ except Empty: None
+
+ try:
+ extra = queue_1.get(timeout=1)
+ self.fail("Got unexpected message in queue_1: " + extra.body)
+ except Empty: None
+
+ #
+ # Tear down the bridges between the two exchanges, then wait
+ # for the bindings to be cleaned up
+ #
+
+ for _b in qmf.getObjects(_class="bridge"):
+ result = _b.close()
+ self.assertEqual(result.status, 0)
+
+ binding_counts = [1, 1]
+ self.assertEqual(len(binding_counts), len(exchanges), "Update Test!")
+ for i in range(2):
+ exchanges[i].update()
+ timeout = time() + 10
+ while exchanges[i].bindingCount != binding_counts[i] and time() <= timeout:
+ exchanges[i].update()
+ self.failUnless(exchanges[i].bindingCount == binding_counts[i])
+
+ #
+ # restore the bridges between the two exchanges, and wait for the
+ # bindings to propagate.
+ #
+
+ for _l in qmf.getObjects(_class="link"):
+ # print("Link=%s:%s %s" % (_l.host, _l.port, str(_l.getBroker())))
+ result = _l.bridge(False, # durable
+ "fedX", # src
+ "fedX", # dst
+ "", # key
+ "", # tag
+ "", # excludes
+ False, # srcIsQueue
+ False, # srcIsLocal
+ True, # dynamic
+ 0) # sync
+ self.assertEqual(result.status, 0)
+
+ binding_counts = [2, 2]
+ self.assertEqual(len(binding_counts), len(exchanges), "Update Test!")
+ for i in range(2):
+ exchanges[i].update()
+ timeout = time() + 10
+ while exchanges[i].bindingCount != binding_counts[i] and time() <= timeout:
+ exchanges[i].update()
+ self.failUnless(exchanges[i].bindingCount == binding_counts[i])
+
+ #
+ # verify traffic flows correctly
+ #
+
+ for i in range(1, 11):
+ #dp = self._brokers[1].client_session.delivery_properties(routing_key=params.routing_key())
+ dp = params.delivery_properties(self._brokers[1].client_session)
+ self._brokers[1].client_session.message_transfer(destination="fedX", message=Message(dp, "Message_trp %d" % i))
+
+ # get exactly 10 msgs on B0's queue and B1's queue
+ for i in range(1, 11):
+ try:
+ msg = queue_0.get(timeout=5)
+ self.assertEqual("Message_trp %d" % i, msg.body)
+ msg = queue_1.get(timeout=5)
+ self.assertEqual("Message_trp %d" % i, msg.body)
+ except Empty:
+ self.fail("Only got %d msgs - expected 10" % i)
+ try:
+ extra = queue_0.get(timeout=1)
+ self.fail("Got unexpected message in queue_0: " + extra.body)
+ except Empty: None
+
+ try:
+ extra = queue_1.get(timeout=1)
+ self.fail("Got unexpected message in queue_1: " + extra.body)
+ except Empty: None
+
+
+ #
+ # cleanup
+ #
+ params.unbind_queue(self._brokers[0].client_session, "fedX1", "fedX")
+ self._brokers[0].client_session.message_cancel(destination="f1")
+ self._brokers[0].client_session.queue_delete(queue="fedX1")
+
+ params.unbind_queue(self._brokers[1].client_session, "fedX1", "fedX")
+ self._brokers[1].client_session.message_cancel(destination="f1")
+ self._brokers[1].client_session.queue_delete(queue="fedX1")
+
+ 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[0:2]:
+ _b.client_session.exchange_delete(exchange="fedX")
+
+ self._teardown_brokers()
+
+ self.verify_cleanup()
+
+
diff --git a/cpp/src/tests/federation_sys.py b/cpp/src/tests/federation_sys.py
new file mode 100755
index 0000000000..11590f684e
--- /dev/null
+++ b/cpp/src/tests/federation_sys.py
@@ -0,0 +1,1900 @@
+#!/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.
+#
+
+from inspect import stack
+from qpid import messaging
+from qpid.messaging import Message
+from qpid.messaging.exceptions import Empty
+from qpid.testlib import TestBase010
+from random import randint
+from sys import stdout
+from time import sleep
+
+
+class Enum(object):
+ def __init__(self, **entries):
+ self.__dict__.update(entries)
+ def __repr__(self):
+ args = ['%s=%s' % (k, repr(v)) for (k,v) in vars(self).items()]
+ return 'Enum(%s)' % ', '.join(args)
+
+
+class QmfTestBase010(TestBase010):
+
+ _brokers = []
+ _links = []
+ _bridges = []
+ _alt_exch_ops = Enum(none=0, create=1, delete=2)
+
+ class _Broker(object):
+ """
+ This broker proxy object holds the Qmf proxy to a broker of known address as well as the QMF broker
+ object, connection and sessions to the broker.
+ """
+ def __init__(self, url):
+ self.url = url # format: "host:port"
+ url_parts = url.split(':')
+ self.host = url_parts[0]
+ self.port = int(url_parts[1])
+ self.qmf_broker = None
+ self.connection = messaging.Connection.establish(self.url)
+ self.sessions = []
+ def __str__(self):
+ return "_Broker %s:%s (%d open sessions)" % (self.host, self.port, len(self.sessions))
+ def destroy(self, qmf = None):
+ if qmf is not None:
+ qmf.delBroker(self.qmf_broker.getBroker())
+ for session in self.sessions:
+ try: # Session may have been closed by broker error
+ session.close()
+ except Exception, e: print "WARNING: %s: Unable to close session %s (%s): %s %s" % (self, session, hex(id(session)), type(e), e)
+ try: # Connection may have been closed by broker error
+ self.connection.close()
+ except Exception, e: print "WARNING: %s: Unable to close connection %s (%s): %s %s" % (self, self.connection, hex(id(self.connection)), type(e), e)
+ def session(self, name, transactional_flag = False):
+ session = self.connection.session(name, transactional_flag)
+ self.sessions.append(session)
+ return session
+
+ def setUp(self):
+ """
+ Called one before each test starts
+ """
+ TestBase010.setUp(self)
+ self.startQmf();
+
+ def tearDown(self):
+ """
+ Called once after each test competes. Close all Qmf objects (bridges, links and brokers)
+ """
+ while len(self._bridges):
+ self._bridges.pop().close()
+ while len(self._links):
+ self._links.pop().close()
+ while len(self._brokers):
+ b = self._brokers.pop()
+ if len(self._brokers) <= 1:
+ b.destroy(None)
+ else:
+ b.destroy(self.qmf)
+ TestBase010.tearDown(self)
+ self.qmf.close()
+
+ #--- General test utility functions
+
+ def _get_name(self):
+ """
+ Return the name of method which called this method stripped of "test_" prefix. Used for naming
+ queues and exchanges on a per-test basis.
+ """
+ return stack()[1][3][5:]
+
+ def _get_broker_port(self, key):
+ """
+ Get the port of a broker defined in the environment using -D<key>=portno
+ """
+ return int(self.defines[key])
+
+ def _get_cluster_ports(self, key):
+ """
+ Get the cluster ports from the parameters of the test which place it in the environment using
+ -D<key>="port0 port1 ... portN" (space-separated)
+ """
+ ports = []
+ ports_str = self.defines[key]
+ if ports_str:
+ for p in ports_str.split():
+ ports.append(int(p))
+ return ports
+
+ def _get_send_address(self, exch_name, queue_name):
+ """
+ Get an address to which to send messages based on the exchange name and queue name, but taking into account
+ that the exchange name may be "" (the default exchange), in whcih case the format changes slightly.
+ """
+ if len(exch_name) == 0: # Default exchange
+ return queue_name
+ return "%s/%s" % (exch_name, queue_name)
+
+ def _get_broker(self, cluster_flag, broker_port_key, cluster_ports_key):
+ """
+ Read the port numbers for pre-started brokers from the environment using keys, then find or create and return
+ the Qmf broker proxy for the appropriate broker
+ """
+ if cluster_flag:
+ port = self._get_cluster_ports(cluster_ports_key)[0] # Always use the first node in the cluster
+ else:
+ port = self._get_broker_port(broker_port_key)
+ return self._find_create_broker("localhost:%s" % port)
+
+ def _get_msg_subject(self, topic_key):
+ """
+ Return an appropriate subject for sending a message to a known topic. Return None if there is no topic.
+ """
+ if len(topic_key) == 0: return None
+ if "*" in topic_key: return topic_key.replace("*", "test")
+ if "#" in topic_key: return topic_key.replace("#", "multipart.test")
+ return topic_key
+
+ def _send_msgs(self, session_name, broker, addr, msg_count, msg_content = "Message_%03d", topic_key = "",
+ msg_durable_flag = False, enq_txn_size = 0):
+ """
+ Send messages to a broker using address addr
+ """
+ send_session = broker.session(session_name, transactional_flag = enq_txn_size > 0)
+ sender = send_session.sender(addr)
+ txn_cnt = 0
+ for i in range(0, msg_count):
+ sender.send(Message(msg_content % (i + 1), subject = self._get_msg_subject(topic_key), durable = msg_durable_flag))
+ if enq_txn_size > 0:
+ txn_cnt += 1
+ if txn_cnt >= enq_txn_size:
+ send_session.commit()
+ txn_cnt = 0
+ if enq_txn_size > 0 and txn_cnt > 0:
+ send_session.commit()
+ sender.close()
+ send_session.close()
+
+ def _receive_msgs(self, session_name, broker, addr, msg_count, msg_content = "Message_%03d", deq_txn_size = 0,
+ timeout = 0):
+ """
+ Receive messages from a broker
+ """
+ receive_session = broker.session(session_name, transactional_flag = deq_txn_size > 0)
+ receiver = receive_session.receiver(addr)
+ txn_cnt = 0
+ for i in range(0, msg_count):
+ try:
+ msg = receiver.fetch(timeout = timeout)
+ if deq_txn_size > 0:
+ txn_cnt += 1
+ if txn_cnt >= deq_txn_size:
+ receive_session.commit()
+ txn_cnt = 0
+ receive_session.acknowledge()
+ except Empty:
+ if deq_txn_size > 0: receive_session.rollback()
+ receiver.close()
+ receive_session.close()
+ if i == 0:
+ self.fail("Broker %s queue \"%s\" is empty" % (broker.qmf_broker.getBroker().getUrl(), addr))
+ else:
+ self.fail("Unable to receive message %d from broker %s queue \"%s\"" % (i, broker.qmf_broker.getBroker().getUrl(), addr))
+ if msg.content != msg_content % (i + 1):
+ receiver.close()
+ receive_session.close()
+ self.fail("Unexpected message \"%s\", was expecting \"%s\"." % (msg.content, msg_content % (i + 1)))
+ try:
+ msg = receiver.fetch(timeout = 0)
+ if deq_txn_size > 0: receive_session.rollback()
+ receiver.close()
+ receive_session.close()
+ self.fail("Extra message \"%s\" found on broker %s address \"%s\"" % (msg.content, broker.qmf_broker.getBroker().getUrl(), addr))
+ except Empty:
+ pass
+ if deq_txn_size > 0 and txn_cnt > 0:
+ receive_session.commit()
+ receiver.close()
+ receive_session.close()
+
+ #--- QMF-specific utility functions
+
+ def _get_qmf_property(self, props, key):
+ """
+ Get the value of a named property key kj from a property list [(k0, v0), (k1, v1), ... (kn, vn)].
+ """
+ for k,v in props:
+ if k.name == key:
+ return v
+ return None
+
+ def _check_qmf_return(self, method_result):
+ """
+ Check the result of a Qmf-defined method call
+ """
+ self.assertTrue(method_result.status == 0, method_result.text)
+
+ def _check_optional_qmf_property(self, qmf_broker, type, qmf_object, key, expected_val, obj_ref_flag):
+ """
+ Optional Qmf properties don't show up in the properties list when they are not specified. Checks for
+ these property types involve searching the properties list and making sure it is present or not as
+ expected.
+ """
+ val = self._get_qmf_property(qmf_object.getProperties(), key)
+ if val is None:
+ if len(expected_val) > 0:
+ self.fail("%s %s exists, but has does not have %s property. Expected value: \"%s\"" %
+ (type, qmf_object.name, key, expected_val))
+ else:
+ if len(expected_val) > 0:
+ if obj_ref_flag:
+ obj = self.qmf.getObjects(_objectId = val, _broker = qmf_broker.getBroker())
+ self.assertEqual(len(obj), 1, "More than one object with the same objectId: %s" % obj)
+ val = obj[0].name
+ self.assertEqual(val, expected_val, "%s %s exists, but has incorrect %s property. Found \"%s\", expected \"%s\"" %
+ (type, qmf_object.name, key, val, expected_val))
+ else:
+ self.fail("%s %s exists, but has an unexpected %s property \"%s\" set." % (type, qmf_object.name, key, val))
+
+ #--- Find/create Qmf broker objects
+
+ def _find_qmf_broker(self, url):
+ """
+ Find the Qmf broker object for the given broker URL. The broker must have been previously added to Qmf through
+ addBroker()
+ """
+ for b in self.qmf.getObjects(_class="broker"):
+ if b.getBroker().getUrl() == url:
+ return b
+ return None
+
+ def _find_create_broker(self, url):
+ """
+ Find a running broker through Qmf. If it does not exist, add it (assuming the broker is already running).
+ """
+ broker = self._Broker(url)
+ self._brokers.append(broker)
+ if self.qmf is not None:
+ qmf_broker = self._find_qmf_broker(broker.url)
+ if qmf_broker is None:
+ self.qmf.addBroker(broker.url)
+ broker.qmf_broker = self._find_qmf_broker(broker.url)
+ else:
+ broker.qmf_broker = qmf_broker
+ return broker
+
+ #--- Find/create/delete exchanges
+
+ def _find_qmf_exchange(self, qmf_broker, name, type, alternate, durable, auto_delete):
+ """
+ Find Qmf exchange object
+ """
+ for e in self.qmf.getObjects(_class="exchange", _broker = qmf_broker.getBroker()):
+ if e.name == name:
+ if len(name) == 0 or (len(name) >= 4 and name[:4] == "amq."): return e # skip checks for special exchanges
+ self.assertEqual(e.type, type,
+ "Exchange \"%s\" exists, but is of unexpected type %s; expected type %s." %
+ (name, e.type, type))
+ self._check_optional_qmf_property(qmf_broker, "Exchange", e, "altExchange", alternate, True)
+ self.assertEqual(e.durable, durable,
+ "Exchange \"%s\" exists, but has incorrect durability. Found durable=%s, expected durable=%s" %
+ (name, e.durable, durable))
+ self.assertEqual(e.autoDelete, auto_delete,
+ "Exchange \"%s\" exists, but has incorrect auto-delete property. Found %s, expected %s" %
+ (name, e.autoDelete, auto_delete))
+ return e
+ return None
+
+ def _find_create_qmf_exchange(self, qmf_broker, name, type, alternate, durable, auto_delete, args):
+ """
+ Find Qmf exchange object if exchange exists, create exchange and return its Qmf object if not
+ """
+ e = self._find_qmf_exchange(qmf_broker, name, type, alternate, durable, auto_delete)
+ if e is not None: return e
+ # Does not exist, so create it
+ props = dict({"exchange-type": type, "type": type, "durable": durable, "auto-delete": auto_delete, "alternate-exchange": alternate}, **args)
+ self._check_qmf_return(qmf_broker.create(type="exchange", name=name, properties=props, strict=True))
+ e = self._find_qmf_exchange(qmf_broker, name, type, alternate, durable, auto_delete)
+ self.assertNotEqual(e, None, "Creation of exchange %s on broker %s failed" % (name, qmf_broker.getBroker().getUrl()))
+ return e
+
+ def _find_delete_qmf_exchange(self, qmf_broker, name, type, alternate, durable, auto_delete):
+ """
+ Find and delete Qmf exchange object if it exists
+ """
+ e = self._find_qmf_exchange(qmf_broker, name, type, alternate, durable, auto_delete)
+ if e is not None and not auto_delete:
+ self._check_qmf_return(qmf_broker.delete(type="exchange", name=name, options={}))
+
+ #--- Find/create/delete queues
+
+ def _find_qmf_queue(self, qmf_broker, name, alternate_exchange, durable, exclusive, auto_delete):
+ """
+ Find a Qmf queue object
+ """
+ for q in self.qmf.getObjects(_class="queue", _broker = qmf_broker.getBroker()):
+ if q.name == name:
+ self._check_optional_qmf_property(qmf_broker, "Queue", q, "altExchange", alternate_exchange, True)
+ self.assertEqual(q.durable, durable,
+ "Queue \"%s\" exists, but has incorrect durable property. Found %s, expected %s" %
+ (name, q.durable, durable))
+ self.assertEqual(q.exclusive, exclusive,
+ "Queue \"%s\" exists, but has incorrect exclusive property. Found %s, expected %s" %
+ (name, q.exclusive, exclusive))
+ self.assertEqual(q.autoDelete, auto_delete,
+ "Queue \"%s\" exists, but has incorrect auto-delete property. Found %s, expected %s" %
+ (name, q.autoDelete, auto_delete))
+ return q
+ return None
+
+ def _find_create_qmf_queue(self, qmf_broker, name, alternate_exchange, durable, exclusive, auto_delete, args):
+ """
+ Find Qmf queue object if queue exists, create queue and return its Qmf object if not
+ """
+ q = self._find_qmf_queue(qmf_broker, name, alternate_exchange, durable, exclusive, auto_delete)
+ if q is not None: return q
+ # Queue does not exist, so create it
+ props = dict({"durable": durable, "auto-delete": auto_delete, "exclusive": exclusive, "alternate-exchange": alternate_exchange}, **args)
+ self._check_qmf_return(qmf_broker.create(type="queue", name=name, properties=props, strict=True))
+ q = self._find_qmf_queue(qmf_broker, name, alternate_exchange, durable, exclusive, auto_delete)
+ self.assertNotEqual(q, None, "Creation of queue %s on broker %s failed" % (name, qmf_broker.getBroker().getUrl()))
+ return q
+
+ def _find_delete_qmf_queue(self, qmf_broker, name, alternate_exchange, durable, exclusive, auto_delete, args):
+ """
+ Find and delete Qmf queue object if it exists
+ """
+ q = self._find_qmf_queue(qmf_broker, name, alternate_exchange, durable, exclusive, auto_delete)
+ if q is not None and not auto_delete:
+ self._check_qmf_return(qmf_broker.delete(type="queue", name=name, options={}))
+
+ #--- Find/create/delete bindings (between an exchange and a queue)
+
+ def _find_qmf_binding(self, qmf_broker, qmf_exchange, qmf_queue, binding_key, binding_args):
+ """
+ Find a Qmf binding object
+ """
+ for b in self.qmf.getObjects(_class="binding", _broker = qmf_broker.getBroker()):
+ if b.exchangeRef == qmf_exchange.getObjectId() and b.queueRef == qmf_queue.getObjectId():
+ if qmf_exchange.type != "fanout": # Fanout ignores the binding key, and always returns "" as the key
+ self.assertEqual(b.bindingKey, binding_key,
+ "Binding between exchange %s and queue %s exists, but has mismatching binding key: Found %s, expected %s." %
+ (qmf_exchange.name, qmf_queue.name, b.bindingKey, binding_key))
+ self.assertEqual(b.arguments, binding_args,
+ "Binding between exchange %s and queue %s exists, but has mismatching arguments: Found %s, expected %s" %
+ (qmf_exchange.name, qmf_queue.name, b.arguments, binding_args))
+ return b
+ return None
+
+ def _find_create_qmf_binding(self, qmf_broker, qmf_exchange, qmf_queue, binding_key, binding_args):
+ """
+ Find Qmf binding object if it exists, create binding and return its Qmf object if not
+ """
+ b = self._find_qmf_binding(qmf_broker, qmf_exchange, qmf_queue, binding_key, binding_args)
+ if b is not None: return b
+ # Does not exist, so create it
+ self._check_qmf_return(qmf_broker.create(type="binding", name="%s/%s/%s" % (qmf_exchange.name, qmf_queue.name, binding_key), properties=binding_args, strict=True))
+ b = self._find_qmf_binding(qmf_broker, qmf_exchange, qmf_queue, binding_key, binding_args)
+ self.assertNotEqual(b, None, "Creation of binding between exchange %s and queue %s with key %s failed" %
+ (qmf_exchange.name, qmf_queue.name, binding_key))
+ return b
+
+ def _find_delete_qmf_binding(self, qmf_broker, qmf_exchange, qmf_queue, binding_key, binding_args):
+ """
+ Find and delete Qmf binding object if it exists
+ """
+ b = self._find_qmf_binding(qmf_broker, qmf_exchange, qmf_queue, binding_key, binding_args)
+ if b is not None:
+ if len(qmf_exchange.name) > 0: # not default exchange
+ self._check_qmf_return(qmf_broker.delete(type="binding", name="%s/%s/%s" % (qmf_exchange.name, qmf_queue.name, binding_key), options={}))
+
+ #--- Find/create a link
+
+ def _find_qmf_link(self, qmf_from_broker_proxy, host, port):
+ """
+ Find a Qmf link object
+ """
+ for l in self.qmf.getObjects(_class="link", _broker=qmf_from_broker_proxy):
+ if l.host == host and l.port == port:
+ return l
+ return None
+
+ def _find_create_qmf_link(self, qmf_from_broker, qmf_to_broker_proxy, link_durable_flag, auth_mechanism, user_id,
+ password, transport, pause_interval, link_ready_timeout):
+ """
+ Find a Qmf link object if it exists, create it and return its Qmf link object if not
+ """
+ to_broker_host = qmf_to_broker_proxy.host
+ to_broker_port = qmf_to_broker_proxy.port
+ l = self._find_qmf_link(qmf_from_broker.getBroker(), to_broker_host, to_broker_port)
+ if l is not None: return l
+ # Does not exist, so create it
+ self._check_qmf_return(qmf_from_broker.connect(to_broker_host, to_broker_port, link_durable_flag, auth_mechanism, user_id, password, transport))
+ l = self._find_qmf_link(qmf_from_broker.getBroker(), to_broker_host, to_broker_port)
+ self.assertNotEqual(l, None, "Creation of link from broker %s to broker %s failed" %
+ (qmf_from_broker.getBroker().getUrl(), qmf_to_broker_proxy.getUrl()))
+ self._wait_for_link(l, pause_interval, link_ready_timeout)
+ return l
+
+ def _wait_for_link(self, link, pause_interval, link_ready_timeout):
+ """
+ Wait for link to become active (state=Operational)
+ """
+ tot_time = 0
+ link.update()
+ if link.state == "":
+ # Link mgmt updates for the c++ link object are disabled when in a cluster because of inconsistent state:
+ # one is "Operational", the other "Passive". In this case, wait a bit and hope for the best...
+ sleep(2*pause_interval)
+ else:
+ while link.state != "Operational" and tot_time < link_ready_timeout:
+ sleep(pause_interval)
+ tot_time += pause_interval
+ link.update()
+ self.assertEqual(link.state, "Operational", "Timeout: Link not operational, state=%s" % link.state)
+
+ #--- Find/create a bridge
+
+ def _find_qmf_bridge(self, qmf_broker_proxy, qmf_link, source, destination, key):
+ """
+ Find a Qmf link object
+ """
+ for b in self.qmf.getObjects(_class="bridge", _broker=qmf_broker_proxy):
+ if b.linkRef == qmf_link.getObjectId() and b.src == source and b.dest == destination and b.key == key:
+ return b
+ return None
+
+ def _find_create_qmf_bridge(self, qmf_broker_proxy, qmf_link, queue_name, exch_name, topic_key,
+ queue_route_type_flag, bridge_durable_flag):
+ """
+ Find a Qmf bridge object if it exists, create it and return its Qmf object if not
+ """
+ if queue_route_type_flag:
+ src = queue_name
+ dest = exch_name
+ key = ""
+ else:
+ src = exch_name
+ dest = exch_name
+ if len(topic_key) > 0:
+ key = topic_key
+ else:
+ key = queue_name
+ b = self._find_qmf_bridge(qmf_broker_proxy, qmf_link, src, dest, key)
+ if b is not None:
+ return b
+ # Does not exist, so create it
+ self._check_qmf_return(qmf_link.bridge(bridge_durable_flag, src, dest, key, "", "", queue_route_type_flag, False, False, 1))
+ b = self._find_qmf_bridge(qmf_broker_proxy, qmf_link, src, dest, key)
+ self.assertNotEqual(b, None, "Bridge creation failed: src=%s dest=%s key=%s" % (src, dest, key))
+ return b
+
+ def _wait_for_bridge(self, bridge, src_broker, dest_broker, exch_name, queue_name, topic_key, pause_interval,
+ bridge_ready_timeout):
+ """
+ Wait for bridge to become active by sending messages over the bridge at 1 sec intervals until they are
+ observed at the destination.
+ """
+ tot_time = 0
+ active = False
+ send_session = src_broker.session("tx")
+ sender = send_session.sender(self._get_send_address(exch_name, queue_name))
+ src_receive_session = src_broker.session("src_rx")
+ src_receiver = src_receive_session.receiver(queue_name)
+ dest_receive_session = dest_broker.session("dest_rx")
+ dest_receiver = dest_receive_session.receiver(queue_name)
+ while not active and tot_time < bridge_ready_timeout:
+ sender.send(Message("xyz123", subject = self._get_msg_subject(topic_key)))
+ try:
+ src_receiver.fetch(timeout = 0)
+ src_receive_session.acknowledge()
+ # Keep receiving msgs, as several may have accumulated
+ while True:
+ dest_receiver.fetch(timeout = 0)
+ dest_receive_session.acknowledge()
+ sleep(1)
+ active = True
+ except Empty:
+ sleep(pause_interval)
+ tot_time += pause_interval
+ dest_receiver.close()
+ dest_receive_session.close()
+ src_receiver.close()
+ src_receive_session.close()
+ sender.close()
+ send_session.close()
+ self.assertTrue(active, "Bridge failed to become active after %ds: %s" % (bridge_ready_timeout, bridge))
+
+ #--- Find/create/delete utility functions
+
+ def _create_and_bind(self, qmf_broker, exchange_args, queue_args, binding_args):
+ """
+ Create a binding between a named exchange and queue on a broker
+ """
+ e = self._find_create_qmf_exchange(qmf_broker, **exchange_args)
+ q = self._find_create_qmf_queue(qmf_broker, **queue_args)
+ return self._find_create_qmf_binding(qmf_broker, e, q, **binding_args)
+
+ def _check_alt_exchange(self, qmf_broker, alt_exch_name, alt_exch_type, alt_exch_op):
+ """
+ Check for existence of alternate exchange. Return the Qmf exchange proxy object for the alternate exchange
+ """
+ if len(alt_exch_name) == 0: return None
+ if alt_exch_op == _alt_exch_ops.create:
+ return self._find_create_qmf_exchange(qmf_broker=qmf_broker, name=alt_exch_name, type=alt_exch_type,
+ alternate="", durable=False, auto_delete=False, args={})
+ if alt_exch_op == _alt_exch_ops.delete:
+ return self._find_delete_qmf_exchange(qmf_broker=qmf_broker, name=alt_exch_name, type=alt_exch_type,
+ alternate="", durable=False, auto_delete=False)
+ return self._find_qmf_exchange(qmf_broker=qmf_broker, name=alt_exchange_name, type=alt_exchange_type,
+ alternate="", durable=False, auto_delete=False)
+
+ def _delete_queue_binding(self, qmf_broker, exchange_args, queue_args, binding_args):
+ """
+ Delete a queue and the binding between it and the exchange
+ """
+ e = self._find_qmf_exchange(qmf_broker, exchange_args["name"], exchange_args["type"], exchange_args["alternate"], exchange_args["durable"], exchange_args["auto_delete"])
+ q = self._find_qmf_queue(qmf_broker, queue_args["name"], queue_args["alternate_exchange"], queue_args["durable"], queue_args["exclusive"], queue_args["auto_delete"])
+ self._find_delete_qmf_binding(qmf_broker, e, q, **binding_args)
+ self._find_delete_qmf_queue(qmf_broker, **queue_args)
+
+ def _create_route(self, queue_route_type_flag, src_broker, dest_broker, exch_name, queue_name, topic_key,
+ link_durable_flag, bridge_durable_flag, auth_mechanism, user_id, password, transport,
+ pause_interval = 1, link_ready_timeout = 20, bridge_ready_timeout = 20):
+ """
+ Create a route from a source broker to a destination broker
+ """
+ l = self._find_create_qmf_link(dest_broker.qmf_broker, src_broker.qmf_broker.getBroker(), link_durable_flag,
+ auth_mechanism, user_id, password, transport, pause_interval, link_ready_timeout)
+ self._links.append(l)
+ b = self._find_create_qmf_bridge(dest_broker.qmf_broker.getBroker(), l, queue_name, exch_name, topic_key,
+ queue_route_type_flag, bridge_durable_flag)
+ self._bridges.append(b)
+ self._wait_for_bridge(b, src_broker, dest_broker, exch_name, queue_name, topic_key, pause_interval, bridge_ready_timeout)
+
+ # Parameterized test - entry point for tests
+
+ def _do_test(self,
+ test_name, # Name of test
+ exch_name = "amq.direct", # Remote exchange name
+ exch_type = "direct", # Remote exchange type
+ exch_alt_exch = "", # Remote exchange alternate exchange
+ exch_alt_exch_type = "direct", # Remote exchange alternate exchange type
+ exch_durable_flag = False, # Remote exchange durability
+ exch_auto_delete_flag = False, # Remote exchange auto-delete property
+ exch_x_args = {}, # Remote exchange args
+ queue_alt_exch = "", # Remote queue alternate exchange
+ queue_alt_exch_type = "direct", # Remote queue alternate exchange type
+ queue_durable_flag = False, # Remote queue durability
+ queue_exclusive_flag = False, # Remote queue exclusive property
+ queue_auto_delete_flag = False, # Remote queue auto-delete property
+ queue_x_args = {}, # Remote queue args
+ binding_durable_flag = False, # Remote binding durability
+ binding_x_args = {}, # Remote binding args
+ topic_key = "", # Binding key For remote topic exchanges only
+ msg_count = 10, # Number of messages to send
+ msg_durable_flag = False, # Message durability
+ link_durable_flag = False, # Route link durability
+ bridge_durable_flag = False, # Route bridge durability
+ queue_route_type_flag = False, # Route type: false = bridge route, true = queue route
+ enq_txn_size = 0, # Enqueue transaction size, 0 = no transactions
+ deq_txn_size = 0, # Dequeue transaction size, 0 = no transactions
+ local_cluster_flag = False, # Use a node from the local cluster, otherwise use single local broker
+ remote_cluster_flag = False, # Use a node from the remote cluster, otherwise use single remote broker
+ alt_exch_op = _alt_exch_ops.create,# Op on alt exch [create (ensure present), delete (ensure not present), none (neither create nor delete)]
+ auth_mechanism = "", # Authorization mechanism for linked broker
+ user_id = "", # User ID for authorization on linked broker
+ password = "", # Password for authorization on linked broker
+ transport = "tcp" # Transport for route to linked broker
+ ):
+ """
+ Parameterized federation test. Sets up a federated link between a source broker and a destination broker and
+ checks that messages correctly pass over the link to the destination. Where appropriate (non-queue-routes), also
+ checks for the presence of messages on the source broker.
+
+ In these tests, the concept is to create a LOCAL broker, then create a link to a REMOTE broker using federation.
+ In other words, the messages sent to the LOCAL broker will be replicated on the REMOTE broker, and tests are
+ performed on the REMOTE broker to check that the required messages are present. In the case of regular routes,
+ the LOCAL broker will also retain the messages, and a similar test is performed on this broker.
+
+ TODO: There are several items to improve here:
+ 1. _do_test() is rather general. Rather create a version for each exchange type and test the exchange/queue
+ interaction in more detail based on the exchange type
+ 2. Add a headers and an xml exchange type
+ 3. Restructure the tests to start and stop brokers and clusters directly rather than relying on previously
+ started brokers. Then persistence can be checked by stopping and restarting the brokers/clusters. In particular,
+ test the persistence of links and bridges, both of which take a persistence flag.
+ 4. Test the behavior of the alternate exchanges when messages are sourced through a link. Also check behavior
+ when the alternate exchange is not present or is deleted after the reference is made.
+ 5. Test special queue types (eg LVQ)
+ """
+ local_broker = self._get_broker(local_cluster_flag, "local-port", "local-cluster-ports")
+ remote_broker = self._get_broker(remote_cluster_flag, "remote-port", "remote-cluster-ports")
+
+ # Check alternate exchanges exist (and create them if not) on both local and remote brokers
+ self._check_alt_exchange(local_broker.qmf_broker, exch_alt_exch, exch_alt_exch_type, alt_exch_op)
+ self._check_alt_exchange(local_broker.qmf_broker, queue_alt_exch, queue_alt_exch_type, alt_exch_op)
+ self._check_alt_exchange(remote_broker.qmf_broker, exch_alt_exch, exch_alt_exch_type, alt_exch_op)
+ self._check_alt_exchange(remote_broker.qmf_broker, queue_alt_exch, queue_alt_exch_type, alt_exch_op)
+
+ queue_name = "queue_%s" % test_name
+ exchange_args = {"name": exch_name, "type": exch_type, "alternate": exch_alt_exch,
+ "durable": exch_durable_flag, "auto_delete": exch_auto_delete_flag, "args": exch_x_args}
+ queue_args = {"name": queue_name, "alternate_exchange": queue_alt_exch, "durable": queue_durable_flag,
+ "exclusive": queue_exclusive_flag, "auto_delete": queue_auto_delete_flag, "args": queue_x_args}
+ binding_args = {"binding_args": binding_x_args}
+ if exch_type == "topic":
+ self.assertTrue(len(topic_key) > 0, "Topic exchange selected, but no topic key was set.")
+ binding_args["binding_key"] = topic_key
+ elif exch_type == "direct":
+ binding_args["binding_key"] = queue_name
+ else:
+ binding_args["binding_key"] = ""
+ self._create_and_bind(qmf_broker=local_broker.qmf_broker, exchange_args=exchange_args, queue_args=queue_args, binding_args=binding_args)
+ self._create_and_bind(qmf_broker=remote_broker.qmf_broker, exchange_args=exchange_args, queue_args=queue_args, binding_args=binding_args)
+ self._create_route(queue_route_type_flag, local_broker, remote_broker, exch_name, queue_name, topic_key,
+ link_durable_flag, bridge_durable_flag, auth_mechanism, user_id, password, transport)
+
+ self._send_msgs("send_session", local_broker, addr = self._get_send_address(exch_name, queue_name),
+ msg_count = msg_count, topic_key = topic_key, msg_durable_flag = msg_durable_flag, enq_txn_size = enq_txn_size)
+ if not queue_route_type_flag:
+ self._receive_msgs("local_receive_session", local_broker, addr = queue_name, msg_count = msg_count, deq_txn_size = deq_txn_size)
+ self._receive_msgs("remote_receive_session", remote_broker, addr = queue_name, msg_count = msg_count, deq_txn_size = deq_txn_size, timeout = 5)
+
+ # Clean up
+ self._delete_queue_binding(qmf_broker=local_broker.qmf_broker, exchange_args=exchange_args, queue_args=queue_args, binding_args=binding_args)
+ self._delete_queue_binding(qmf_broker=remote_broker.qmf_broker, exchange_args=exchange_args, queue_args=queue_args, binding_args=binding_args)
+
+class A_ShortTests(QmfTestBase010):
+
+ def test_route_defaultExch(self):
+ self._do_test(self._get_name())
+
+ def test_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True)
+
+
+class A_LongTests(QmfTestBase010):
+
+ def test_route_amqDirectExch(self):
+ self._do_test(self._get_name(), exch_name="amq.direct")
+
+ def test_queueRoute_amqDirectExch(self):
+ self._do_test(self._get_name(), exch_name="amq.direct", queue_route_type_flag=True)
+
+
+ def test_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange")
+
+ def test_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True)
+
+
+ def test_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout")
+
+ def test_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True)
+
+
+ def test_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#")
+
+ def test_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True)
+
+
+class B_ShortTransactionTests(QmfTestBase010):
+
+ def test_txEnq01_route_defaultExch(self):
+ self._do_test(self._get_name(), enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_txDeq01_route_defaultExch(self):
+ self._do_test(self._get_name(), enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+
+class B_LongTransactionTests(QmfTestBase010):
+
+ def test_txEnq10_route_defaultExch(self):
+ self._do_test(self._get_name(), enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+
+
+
+ def test_txEnq01_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq10_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq01_txDeq01_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+
+ def test_txEnq01_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq10_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq01_txDeq01_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+
+ def test_txEnq01_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq10_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq01_txDeq01_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+
+class C_ShortClusterTests(QmfTestBase010):
+
+ def test_locCluster_route_defaultExch(self):
+ self._do_test(self._get_name(), local_cluster_flag=True)
+
+ def test_locCluster_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True, local_cluster_flag=True)
+
+ def test_remCluster_route_defaultExch(self):
+ self._do_test(self._get_name(), remote_cluster_flag=True)
+
+ def test_remCluster_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_route_defaultExch(self):
+ self._do_test(self._get_name(), local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+
+class C_LongClusterTests(QmfTestBase010):
+
+ def test_locCluster_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", local_cluster_flag=True)
+
+ def test_locCluster_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, local_cluster_flag=True)
+
+ def test_remCluster_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", remote_cluster_flag=True)
+
+ def test_remCluster_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+
+ def test_locCluster_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", local_cluster_flag=True)
+
+ def test_locCluster_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, local_cluster_flag=True)
+
+ def test_remCluster_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", remote_cluster_flag=True)
+
+ def test_remCluster_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+
+ def test_locCluster_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", local_cluster_flag=True)
+
+ def test_locCluster_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, local_cluster_flag=True)
+
+ def test_remCluster_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", remote_cluster_flag=True)
+
+ def test_remCluster_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+
+class D_ShortClusterTransactionTests(QmfTestBase010):
+
+ def test_txEnq01_locCluster_route_defaultExch(self):
+ self._do_test(self._get_name(), enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_locCluster_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_route_defaultExch(self):
+ self._do_test(self._get_name(), enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_remCluster_route_defaultExch(self):
+ self._do_test(self._get_name(), enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_remCluster_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_route_defaultExch(self):
+ self._do_test(self._get_name(), enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_route_defaultExch(self):
+ self._do_test(self._get_name(), enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_route_defaultExch(self):
+ self._do_test(self._get_name(), enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+
+class D_LongClusterTransactionTests(QmfTestBase010):
+
+ def test_txEnq10_locCluster_route_defaultExch(self):
+ self._do_test(self._get_name(), enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq10_remCluster_route_defaultExch(self):
+ self._do_test(self._get_name(), enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_route_defaultExch(self):
+ self._do_test(self._get_name(), enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_queueRoute_defaultExch(self):
+ self._do_test(self._get_name(), queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+
+ def test_txEnq01_locCluster_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_locCluster_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_remCluster_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_remCluster_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_route_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+
+ def test_txEnq01_locCluster_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_locCluster_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_remCluster_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_remCluster_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_route_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+
+ def test_txEnq01_locCluster_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_locCluster_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_remCluster_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_remCluster_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_route_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+
+class E_ShortPersistenceTests(QmfTestBase010):
+
+ def test_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True)
+
+ def test_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True)
+
+ def test_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True)
+
+ def test_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True)
+
+
+class E_LongPersistenceTests(QmfTestBase010):
+
+
+ def test_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True)
+
+ def test_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True)
+
+ def test_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True)
+
+ def test_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True)
+
+
+ def test_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True)
+
+ def test_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True)
+
+ def test_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True)
+
+ def test_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True)
+
+
+ def test_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True)
+
+ def test_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True)
+
+ def test_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True)
+
+ def test_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True)
+
+
+class F_ShortPersistenceTransactionTests(QmfTestBase010):
+
+ def test_txEnq01_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_txDeq01_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+
+class F_LongPersistenceTransactionTests(QmfTestBase010):
+
+ def test_txEnq10_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+
+
+
+ def test_txEnq01_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq10_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq01_txDeq01_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+
+ def test_txEnq01_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq10_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq01_txDeq01_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+
+ def test_txEnq01_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq01_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1)
+
+ def test_txEnq10_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq10_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103)
+
+ def test_txEnq01_txDeq01_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+ def test_txEnq01_txDeq01_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1)
+
+
+class G_ShortPersistenceClusterTests(QmfTestBase010):
+
+ def test_locCluster_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, local_cluster_flag=True)
+
+ def test_locCluster_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, local_cluster_flag=True)
+
+ def test_locCluster_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True)
+
+ def test_locCluster_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True)
+
+ def test_remCluster_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, remote_cluster_flag=True)
+
+ def test_remCluster_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, remote_cluster_flag=True)
+
+ def test_remCluster_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, remote_cluster_flag=True)
+
+ def test_remCluster_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+
+class G_LongPersistenceClusterTests(QmfTestBase010):
+
+
+
+ def test_locCluster_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, local_cluster_flag=True)
+
+ def test_locCluster_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, local_cluster_flag=True)
+
+ def test_locCluster_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True)
+
+ def test_locCluster_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True)
+
+ def test_remCluster_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, remote_cluster_flag=True)
+
+ def test_remCluster_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, remote_cluster_flag=True)
+
+ def test_remCluster_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, remote_cluster_flag=True)
+
+ def test_remCluster_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+
+ def test_locCluster_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, local_cluster_flag=True)
+
+ def test_locCluster_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, local_cluster_flag=True)
+
+ def test_locCluster_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True)
+
+ def test_locCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True)
+
+ def test_remCluster_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, remote_cluster_flag=True)
+
+ def test_remCluster_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, remote_cluster_flag=True)
+
+ def test_remCluster_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, remote_cluster_flag=True)
+
+ def test_remCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+
+ def test_locCluster_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, local_cluster_flag=True)
+
+ def test_locCluster_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, local_cluster_flag=True)
+
+ def test_locCluster_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True)
+
+ def test_locCluster_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True)
+
+ def test_remCluster_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, remote_cluster_flag=True)
+
+ def test_remCluster_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, remote_cluster_flag=True)
+
+ def test_remCluster_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, remote_cluster_flag=True)
+
+ def test_remCluster_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_locCluster_remCluster_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, local_cluster_flag=True, remote_cluster_flag=True)
+
+
+class H_ShortPersistenceClusterTransactionTests(QmfTestBase010):
+
+ def test_txEnq01_locCluster_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_locCluster_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_locCluster_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_locCluster_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_remCluster_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_remCluster_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_remCluster_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_remCluster_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+
+class H_LongPersistenceClusterTransactionTests(QmfTestBase010):
+
+ def test_txEnq10_locCluster_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq10_remCluster_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_route_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_route_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_queueRoute_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_queueRoute_durMsg_durQueue_defaultExch(self):
+ self._do_test(self._get_name(), msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+
+
+
+ def test_txEnq01_locCluster_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_locCluster_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_locCluster_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_locCluster_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_remCluster_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_remCluster_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_remCluster_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_remCluster_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_route_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_route_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_durMsg_durQueue_directExch(self):
+ self._do_test(self._get_name(), exch_name="testDirectExchange", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+
+ def test_txEnq01_locCluster_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_locCluster_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_locCluster_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_locCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_remCluster_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_remCluster_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_remCluster_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_remCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_route_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_route_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_durMsg_durQueue_fanoutExch(self):
+ self._do_test(self._get_name(), exch_name="testFanoutExchange", exch_type="fanout", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+
+ def test_txEnq01_locCluster_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_locCluster_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_locCluster_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_locCluster_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq10_locCluster_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True)
+
+ def test_txEnq01_remCluster_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_remCluster_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_remCluster_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_remCluster_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq10_remCluster_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_remCluster_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_locCluster_remCluster_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq10_locCluster_remCluster_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=10, msg_count = 103, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_route_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_route_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
+ def test_txEnq01_txDeq01_locCluster_remCluster_queueRoute_durMsg_durQueue_topicExch(self):
+ self._do_test(self._get_name(), exch_name="testTopicExchange", exch_type="topic", topic_key=self._get_name()+".#", msg_durable_flag=True, queue_durable_flag=True, queue_route_type_flag=True, enq_txn_size=1, deq_txn_size=1, local_cluster_flag=True, remote_cluster_flag=True)
+
diff --git a/cpp/src/tests/ipv6_test b/cpp/src/tests/ipv6_test
new file mode 100755
index 0000000000..d75d50fd0a
--- /dev/null
+++ b/cpp/src/tests/ipv6_test
@@ -0,0 +1,150 @@
+#!/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.
+#
+
+# Run a simple test over IPv6
+source ./test_env.sh
+
+CONFIG=$(dirname $0)/config.null
+TEST_HOSTNAME=::1
+COUNT=10
+
+trap cleanup EXIT
+
+error() { echo $*; exit 1; }
+
+# Don't need --no-module-dir or --no-data-dir as they are set as env vars in test_env.sh
+COMMON_OPTS="--daemon --auth no --config $CONFIG"
+
+# Record all broker ports started
+unset PORTS
+declare -a PORTS
+
+# Start new brokers:
+# $1 must be integer
+# $2 = extra opts
+# Append used ports to PORTS variable
+start_brokers() {
+ local -a ports
+ for (( i=0; $i<$1; i++)) do
+ ports[$i]=$($QPIDD_EXEC --port 0 $COMMON_OPTS $2)
+ done
+ PORTS=( ${PORTS[@]} ${ports[@]} )
+}
+
+stop_brokers() {
+ for port in "${PORTS[@]}";
+ do
+ $QPIDD_EXEC -qp $port
+ done
+ PORTS=()
+}
+
+cleanup() {
+ stop_brokers
+}
+
+start_brokers 1
+PORT=${PORTS[0]}
+echo "Started IPv6 smoke perftest on broker port $PORT"
+
+## Test connection via connection settings
+./qpid-perftest --count ${COUNT} --port ${PORT} -b $TEST_HOSTNAME --summary
+
+## Test connection with a URL
+URL="amqp:[$TEST_HOSTNAME]:$PORT"
+
+./qpid-send -b $URL --content-string=hello -a "foo;{create:always}"
+MSG=`./qpid-receive -b $URL -a "foo;{create:always}" --messages 1`
+test "$MSG" = "hello" || { echo "receive failed '$MSG' != 'hello'"; exit 1; }
+
+stop_brokers
+
+# Federation smoke test follows
+
+# Start 2 brokers
+
+# In a distribution, the python tools will be absent.
+if [ ! -f $QPID_CONFIG_EXEC ] || [ ! -f $QPID_ROUTE_EXEC ] ; then
+ echo "python tools absent - skipping federation test."
+else
+
+ start_brokers 2
+ echo "Started Federated brokers on ports ${PORTS[*]}"
+ # Make broker urls
+ BROKER0="[::1]:${PORTS[0]}"
+ BROKER1="[::1]:${PORTS[1]}"
+ TEST_QUEUE=ipv6-fed-test
+
+ $QPID_CONFIG_EXEC -a $BROKER0 add queue $TEST_QUEUE
+ $QPID_CONFIG_EXEC -a $BROKER1 add queue $TEST_QUEUE
+ $QPID_ROUTE_EXEC dynamic add $BROKER1 $BROKER0 amq.direct
+ $QPID_CONFIG_EXEC -a $BROKER1 bind amq.direct $TEST_QUEUE $TEST_QUEUE
+ $QPID_ROUTE_EXEC route map $BROKER1
+
+ ./datagen --count 100 | tee rdata-in |
+ ./qpid-send -b amqp:$BROKER0 -a amq.direct/$TEST_QUEUE --content-stdin
+ ./qpid-receive -b amqp:$BROKER1 -a $TEST_QUEUE --print-content yes -m 0 > rdata-out
+
+ cmp rdata-in rdata-out || { echo "Federated data over IPv6 does not compare"; exit 1; }
+
+ stop_brokers
+ rm rdata-in rdata-out
+fi
+
+# Cluster smoke test follows
+test -z $CLUSTER_LIB && exit 0 # Exit if cluster not supported.
+
+## Test failover in a cluster using IPv6 only
+. $srcdir/ais_check # Will exit if clustering not enabled.
+
+pick_port() {
+ # We need a fixed port to set --cluster-url. Use qpidd to pick a free port.
+ # Note this method is racy
+ PICK=$($QPIDD_EXEC -dp0)
+ $QPIDD_EXEC -qp $PICK
+ echo $PICK
+}
+
+ssl_cluster_broker() { # $1 = port
+ $QPIDD_EXEC $COMMON_OPTS --load-module $CLUSTER_LIB --cluster-name ipv6_test.$HOSTNAME.$$ --cluster-url amqp:[$TEST_HOSTNAME]:$1 --port $1
+ # Wait for broker to be ready
+ ./qpid-ping -b $TEST_HOSTNAME -qp $1 || { echo "Cannot connect to broker on $1"; exit 1; }
+ echo "Running IPv6 cluster broker on port $1"
+}
+
+PORT1=`pick_port`; ssl_cluster_broker $PORT1
+PORT2=`pick_port`; ssl_cluster_broker $PORT2
+
+# Pipe receive output to uniq to remove duplicates
+./qpid-receive --connection-options "{reconnect:true, reconnect-timeout:5}" --failover-updates -b amqp:[$TEST_HOSTNAME]:$PORT1 -a "foo;{create:always}" -f | uniq > ssl_test_receive.tmp &
+
+./qpid-send -b amqp:[$TEST_HOSTNAME]:$PORT2 --content-string=one -a "foo;{create:always}"
+
+$QPIDD_EXEC -qp $PORT1 # Kill broker 1 receiver should fail-over.
+./qpid-send -b amqp:[$TEST_HOSTNAME]:$PORT2 --content-string=two -a "foo;{create:always}" --send-eos 1
+wait # Wait for qpid-receive
+{ echo one; echo two; } > ssl_test_receive.cmp
+diff ssl_test_receive.tmp ssl_test_receive.cmp || { echo "Failover failed"; exit 1; }
+
+$QPIDD_EXEC -qp $PORT2
+
+rm -f ssl_test_receive.*
+
diff --git a/cpp/src/tests/msg_group_test.cpp b/cpp/src/tests/msg_group_test.cpp
new file mode 100644
index 0000000000..6b9d09b89a
--- /dev/null
+++ b/cpp/src/tests/msg_group_test.cpp
@@ -0,0 +1,618 @@
+/*
+ *
+ * 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/messaging/Address.h>
+#include <qpid/messaging/Connection.h>
+#include <qpid/messaging/Receiver.h>
+#include <qpid/messaging/Sender.h>
+#include <qpid/messaging/Session.h>
+#include <qpid/messaging/Message.h>
+#include <qpid/messaging/FailoverUpdates.h>
+#include <qpid/Options.h>
+#include <qpid/log/Logger.h>
+#include <qpid/log/Options.h>
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Time.h"
+#include "qpid/sys/Runnable.h"
+#include "qpid/sys/Thread.h"
+#include "qpid/sys/SystemInfo.h"
+
+#include <iostream>
+#include <memory>
+#include <stdlib.h>
+
+using namespace qpid::messaging;
+using namespace qpid::types;
+using namespace std;
+
+namespace qpid {
+namespace tests {
+
+struct Options : public qpid::Options
+{
+ bool help;
+ std::string url;
+ std::string address;
+ std::string connectionOptions;
+ uint messages;
+ uint capacity;
+ uint ackFrequency;
+ bool failoverUpdates;
+ qpid::log::Options log;
+ uint senders;
+ uint receivers;
+ uint groupSize;
+ bool printReport;
+ std::string groupKey;
+ bool durable;
+ bool allowDuplicates;
+ bool randomizeSize;
+ bool stickyConsumer;
+ uint timeout;
+ uint interleave;
+ std::string prefix;
+ uint sendRate;
+
+ Options(const std::string& argv0=std::string())
+ : qpid::Options("Options"),
+ help(false),
+ url("amqp:tcp:127.0.0.1"),
+ messages(10000),
+ capacity(1000),
+ ackFrequency(100),
+ failoverUpdates(false),
+ log(argv0),
+ senders(2),
+ receivers(2),
+ groupSize(10),
+ printReport(false),
+ groupKey("qpid.no_group"),
+ durable(false),
+ allowDuplicates(false),
+ randomizeSize(false),
+ stickyConsumer(false),
+ timeout(10),
+ interleave(1),
+ sendRate(0)
+ {
+ addOptions()
+ ("ack-frequency", qpid::optValue(ackFrequency, "N"), "Ack frequency (0 implies none of the messages will get accepted)")
+ ("address,a", qpid::optValue(address, "ADDRESS"), "address to send and receive from")
+ ("allow-duplicates", qpid::optValue(allowDuplicates), "Ignore the delivery of duplicated messages")
+ ("broker,b", qpid::optValue(url, "URL"), "url of broker to connect to")
+ ("capacity", qpid::optValue(capacity, "N"), "Pre-fetch window (0 implies no pre-fetch)")
+ ("connection-options", qpid::optValue(connectionOptions, "OPTIONS"), "options for the connection")
+ ("durable", qpid::optValue(durable, "yes|no"), "Mark messages as durable.")
+ ("failover-updates", qpid::optValue(failoverUpdates), "Listen for membership updates distributed via amq.failover")
+ ("group-key", qpid::optValue(groupKey, "KEY"), "Key of the message header containing the group identifier.")
+ ("group-prefix", qpid::optValue(prefix, "STRING"), "Add 'prefix' to the start of all generated group identifiers.")
+ ("group-size", qpid::optValue(groupSize, "N"), "Number of messages per a group.")
+ ("interleave", qpid::optValue(interleave, "N"), "Simultaineously interleave messages from N different groups.")
+ ("messages,m", qpid::optValue(messages, "N"), "Number of messages to send per each sender.")
+ ("receivers,r", qpid::optValue(receivers, "N"), "Number of message consumers.")
+ ("randomize-group-size", qpid::optValue(randomizeSize), "Randomize the number of messages per group to [1...group-size].")
+ ("send-rate", qpid::optValue(sendRate,"N"), "Send at rate of N messages/second. 0 means send as fast as possible.")
+ ("senders,s", qpid::optValue(senders, "N"), "Number of message producers.")
+ ("sticky-consumers", qpid::optValue(stickyConsumer), "If set, verify that all messages in a group are consumed by the same client [TBD].")
+ ("timeout", qpid::optValue(timeout, "N"), "Fail with a stall error should all consumers remain idle for timeout seconds.")
+ ("print-report", qpid::optValue(printReport), "Dump message group statistics to stdout.")
+ ("help", qpid::optValue(help), "print this usage statement");
+ add(log);
+ //("check-redelivered", qpid::optValue(checkRedelivered), "Fails with exception if a duplicate is not marked as redelivered (only relevant when ignore-duplicates is selected)")
+ //("tx", qpid::optValue(tx, "N"), "batch size for transactions (0 implies transaction are not used)")
+ //("rollback-frequency", qpid::optValue(rollbackFrequency, "N"), "rollback frequency (0 implies no transaction will be rolledback)")
+ }
+
+ bool parse(int argc, char** argv)
+ {
+ try {
+ qpid::Options::parse(argc, argv);
+ if (address.empty()) throw qpid::Exception("Address must be specified!");
+ qpid::log::Logger::instance().configure(log);
+ if (help) {
+ std::ostringstream msg;
+ std::cout << msg << *this << std::endl << std::endl
+ << "Verifies the behavior of grouped messages." << std::endl;
+ return false;
+ } else {
+ return true;
+ }
+ } catch (const std::exception& e) {
+ std::cerr << *this << std::endl << std::endl << e.what() << std::endl;
+ return false;
+ }
+ }
+};
+
+const string EOS("eos");
+const string SN("sn");
+
+
+// class that monitors group state across all publishers and consumers. tracks the next
+// expected sequence for each group, and total messages consumed.
+class GroupChecker
+{
+ qpid::sys::Mutex lock;
+
+ const uint totalMsgs;
+ uint totalMsgsConsumed;
+ uint totalMsgsPublished;
+ bool allowDuplicates;
+ uint duplicateMsgs;
+
+ typedef std::map<std::string, uint> SequenceMap;
+ SequenceMap sequenceMap;
+
+ // Statistics - for each group, store the names of all clients that consumed messages
+ // from that group, and the number of messages consumed per client.
+ typedef std::map<std::string, uint> ClientCounter;
+ typedef std::map<std::string, ClientCounter> GroupStatistics;
+ GroupStatistics statistics;
+
+public:
+
+ GroupChecker( uint t, bool d ) :
+ totalMsgs(t), totalMsgsConsumed(0), totalMsgsPublished(0), allowDuplicates(d),
+ duplicateMsgs(0) {}
+
+ bool checkSequence( const std::string& groupId,
+ uint sequence, const std::string& client )
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+
+ QPID_LOG(debug, "Client " << client << " has received " << groupId << ":" << sequence);
+
+ GroupStatistics::iterator gs = statistics.find(groupId);
+ if (gs == statistics.end()) {
+ statistics[groupId][client] = 1;
+ } else {
+ gs->second[client]++;
+ }
+ // now verify
+ SequenceMap::iterator s = sequenceMap.find(groupId);
+ if (s == sequenceMap.end()) {
+ QPID_LOG(debug, "Client " << client << " thinks this is the first message from group " << groupId << ":" << sequence);
+ // if duplication allowed, it is possible that the last msg(s) of an old sequence are redelivered on reconnect.
+ // in this case, set the sequence from the first msg.
+ sequenceMap[groupId] = (allowDuplicates) ? sequence : 0;
+ s = sequenceMap.find(groupId);
+ } else if (sequence < s->second) {
+ duplicateMsgs++;
+ QPID_LOG(debug, "Client " << client << " thinks this message is a duplicate! " << groupId << ":" << sequence);
+ return allowDuplicates;
+ }
+ totalMsgsConsumed++;
+ return sequence == s->second++;
+ }
+
+ void sendingSequence( const std::string& groupId,
+ uint sequence, bool eos,
+ const std::string& client )
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ ++totalMsgsPublished;
+
+ QPID_LOG(debug, "Client " << client << " sending " << groupId << ":" << sequence <<
+ ((eos) ? " (last)" : ""));
+ }
+
+ bool eraseGroup( const std::string& groupId, const std::string& name )
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ QPID_LOG(debug, "Deleting group " << groupId << " (by client " << name << ")");
+ return sequenceMap.erase( groupId ) == 1;
+ }
+
+ uint getNextExpectedSequence( const std::string& groupId )
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return sequenceMap[groupId];
+ }
+
+ bool allMsgsConsumed() // true when done processing msgs
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return (totalMsgsPublished >= totalMsgs) &&
+ (totalMsgsConsumed >= totalMsgsPublished) &&
+ sequenceMap.size() == 0;
+ }
+
+ uint getConsumedTotal()
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return totalMsgsConsumed;
+ }
+
+ uint getPublishedTotal()
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return totalMsgsPublished;
+ }
+
+ ostream& print(ostream& out)
+ {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ out << "Total Published: " << totalMsgsPublished << ", Total Consumed: " << totalMsgsConsumed <<
+ ", Duplicates detected: " << duplicateMsgs << std::endl;
+ out << "Total Groups: " << statistics.size() << std::endl;
+ unsigned long consumers = 0;
+ for (GroupStatistics::iterator gs = statistics.begin(); gs != statistics.end(); ++gs) {
+ out << " GroupId: " << gs->first;
+ consumers += gs->second.size(); // # of consumers that processed this group
+ if (gs->second.size() == 1)
+ out << " completely consumed by a single client." << std::endl;
+ else
+ out << " consumed by " << gs->second.size() << " different clients." << std::endl;
+
+ for (ClientCounter::iterator cc = gs->second.begin(); cc != gs->second.end(); ++cc) {
+ out << " Client: " << cc->first << " consumed " << cc->second << " messages from the group." << std::endl;
+ }
+ }
+ out << "Average # of consumers per group: " << ((statistics.size() != 0) ? (double(consumers)/statistics.size()) : 0) << std::endl;
+ return out;
+ }
+};
+
+
+namespace {
+ // rand() is not thread safe. Create a singleton obj to hold a lock while calling
+ // rand() so it can be called safely by multiple concurrent clients.
+ class Randomizer {
+ qpid::sys::Mutex lock;
+ public:
+ uint operator()(uint max) {
+ qpid::sys::Mutex::ScopedLock l(lock);
+ return (rand() % max) + 1;
+ }
+ };
+
+ static Randomizer randomizer;
+}
+
+
+// tag each generated message with a group identifer
+//
+class GroupGenerator {
+
+ const std::string groupPrefix;
+ const uint groupSize;
+ const bool randomizeSize;
+ const uint interleave;
+
+ uint groupSuffix;
+ uint total;
+
+ struct GroupState {
+ std::string id;
+ const uint size;
+ uint count;
+ GroupState( const std::string& i, const uint s )
+ : id(i), size(s), count(0) {}
+ };
+ typedef std::list<GroupState> GroupList;
+ GroupList groups;
+ GroupList::iterator current;
+
+ // add a new group identifier to the list
+ void newGroup() {
+ std::ostringstream groupId(groupPrefix, ios_base::out|ios_base::ate);
+ groupId << std::string(":") << groupSuffix++;
+ uint size = (randomizeSize) ? randomizer(groupSize) : groupSize;
+ QPID_LOG(trace, "New group: GROUPID=[" << groupId.str() << "] size=" << size << " this=" << this);
+ GroupState group( groupId.str(), size );
+ groups.push_back( group );
+ }
+
+public:
+ GroupGenerator( const std::string& prefix,
+ const uint t,
+ const uint size,
+ const bool randomize,
+ const uint i)
+ : groupPrefix(prefix), groupSize(size),
+ randomizeSize(randomize), interleave(i), groupSuffix(0), total(t)
+ {
+ QPID_LOG(trace, "New group generator: PREFIX=[" << prefix << "] total=" << total << " size=" << size << " rand=" << randomize << " interleave=" << interleave << " this=" << this);
+ for (uint i = 0; i < 1 || i < interleave; ++i) {
+ newGroup();
+ }
+ current = groups.begin();
+ }
+
+ bool genGroup(std::string& groupId, uint& seq, bool& eos)
+ {
+ if (!total) return false;
+ --total;
+ if (current == groups.end())
+ current = groups.begin();
+ groupId = current->id;
+ seq = current->count++;
+ if (current->count == current->size) {
+ QPID_LOG(trace, "Last msg for " << current->id << ", " << current->count << " this=" << this);
+ eos = true;
+ if (total >= interleave) { // need a new group to replace this one
+ newGroup();
+ groups.erase(current++);
+ } else ++current;
+ } else {
+ ++current;
+ eos = total < interleave; // mark eos on the last message of each group
+ }
+ QPID_LOG(trace, "SENDING GROUPID=[" << groupId << "] seq=" << seq << " eos=" << eos << " this=" << this);
+ return true;
+ }
+};
+
+
+
+class Client : public qpid::sys::Runnable
+{
+public:
+ typedef boost::shared_ptr<Client> shared_ptr;
+ enum State {ACTIVE, DONE, FAILURE};
+ Client( const std::string& n, const Options& o ) : name(n), opts(o), state(ACTIVE), stopped(false) {}
+ virtual ~Client() {}
+ State getState() { return state; }
+ void testFailed( const std::string& reason ) { state = FAILURE; error << "Client '" << name << "' failed: " << reason; }
+ void clientDone() { if (state == ACTIVE) state = DONE; }
+ qpid::sys::Thread& getThread() { return thread; }
+ const std::string getErrorMsg() { return error.str(); }
+ void stop() {stopped = true;}
+ const std::string& getName() { return name; }
+
+protected:
+ const std::string name;
+ const Options& opts;
+ qpid::sys::Thread thread;
+ ostringstream error;
+ State state;
+ bool stopped;
+};
+
+
+class Consumer : public Client
+{
+ GroupChecker& checker;
+
+public:
+ Consumer(const std::string& n, const Options& o, GroupChecker& c ) : Client(n, o), checker(c) {};
+ virtual ~Consumer() {};
+
+ void run()
+ {
+ Connection connection;
+ try {
+ connection = Connection(opts.url, opts.connectionOptions);
+ connection.open();
+ std::auto_ptr<FailoverUpdates> updates(opts.failoverUpdates ? new FailoverUpdates(connection) : 0);
+ Session session = connection.createSession();
+ Receiver receiver = session.createReceiver(opts.address);
+ receiver.setCapacity(opts.capacity);
+ Message msg;
+ uint count = 0;
+
+ while (!stopped) {
+ if (receiver.fetch(msg, Duration::SECOND)) { // msg retrieved
+ qpid::types::Variant::Map& properties = msg.getProperties();
+ std::string groupId = properties[opts.groupKey];
+ uint groupSeq = properties[SN];
+ bool eof = properties[EOS];
+
+ QPID_LOG(trace, "RECVING GROUPID=[" << groupId << "] seq=" << groupSeq << " eos=" << eof << " name=" << name);
+
+ qpid::sys::usleep(10);
+
+ if (!checker.checkSequence( groupId, groupSeq, name )) {
+ ostringstream msg;
+ msg << "Check sequence failed. Group=" << groupId << " rcvd seq=" << groupSeq << " expected=" << checker.getNextExpectedSequence( groupId );
+ testFailed( msg.str() );
+ break;
+ } else if (eof) {
+ if (!checker.eraseGroup( groupId, name )) {
+ ostringstream msg;
+ msg << "Erase group failed. Group=" << groupId << " rcvd seq=" << groupSeq;
+ testFailed( msg.str() );
+ break;
+ }
+ }
+
+ ++count;
+ if (opts.ackFrequency && (count % opts.ackFrequency == 0)) {
+ session.acknowledge();
+ }
+ // Clear out message properties & content for next iteration.
+ msg = Message(); // TODO aconway 2010-12-01: should be done by fetch
+ } else if (checker.allMsgsConsumed()) // timed out, nothing else to do?
+ break;
+ }
+ session.acknowledge();
+ session.close();
+ connection.close();
+ } catch(const std::exception& error) {
+ ostringstream msg;
+ msg << "consumer error: " << error.what();
+ testFailed( msg.str() );
+ connection.close();
+ }
+ clientDone();
+ QPID_LOG(trace, "Consuming client " << name << " completed.");
+ }
+};
+
+
+
+class Producer : public Client
+{
+ GroupChecker& checker;
+ GroupGenerator generator;
+
+public:
+ Producer(const std::string& n, const Options& o, GroupChecker& c)
+ : Client(n, o), checker(c),
+ generator( n, o.messages, o.groupSize, o.randomizeSize, o.interleave )
+ {};
+ virtual ~Producer() {};
+
+ void run()
+ {
+ Connection connection;
+ try {
+ connection = Connection(opts.url, opts.connectionOptions);
+ connection.open();
+ std::auto_ptr<FailoverUpdates> updates(opts.failoverUpdates ? new FailoverUpdates(connection) : 0);
+ Session session = connection.createSession();
+ Sender sender = session.createSender(opts.address);
+ if (opts.capacity) sender.setCapacity(opts.capacity);
+ Message msg;
+ msg.setDurable(opts.durable);
+ std::string groupId;
+ uint seq;
+ bool eos;
+ uint sent = 0;
+
+ qpid::sys::AbsTime start = qpid::sys::now();
+ int64_t interval = 0;
+ if (opts.sendRate) interval = qpid::sys::TIME_SEC/opts.sendRate;
+
+ while (!stopped && generator.genGroup(groupId, seq, eos)) {
+ msg.getProperties()[opts.groupKey] = groupId;
+ msg.getProperties()[SN] = seq;
+ msg.getProperties()[EOS] = eos;
+ checker.sendingSequence( groupId, seq, eos, name );
+
+ sender.send(msg);
+ ++sent;
+
+ if (opts.sendRate) {
+ qpid::sys::AbsTime waitTill(start, sent*interval);
+ int64_t delay = qpid::sys::Duration(qpid::sys::now(), waitTill);
+ if (delay > 0) qpid::sys::usleep(delay/qpid::sys::TIME_USEC);
+ }
+ }
+ session.sync();
+ session.close();
+ connection.close();
+ } catch(const std::exception& error) {
+ ostringstream msg;
+ msg << "producer '" << name << "' error: " << error.what();
+ testFailed(msg.str());
+ connection.close();
+ }
+ clientDone();
+ QPID_LOG(trace, "Producing client " << name << " completed.");
+ }
+};
+
+
+}} // namespace qpid::tests
+
+using namespace qpid::tests;
+
+int main(int argc, char ** argv)
+{
+ int status = 0;
+ try {
+ Options opts;
+ if (opts.parse(argc, argv)) {
+
+ GroupChecker state( opts.senders * opts.messages,
+ opts.allowDuplicates);
+ std::vector<Client::shared_ptr> clients;
+
+ if (opts.randomizeSize) srand((unsigned int)qpid::sys::SystemInfo::getProcessId());
+
+ // fire off the producers && consumers
+ for (size_t j = 0; j < opts.senders; ++j) {
+ ostringstream name;
+ name << opts.prefix << "P_" << j;
+ clients.push_back(Client::shared_ptr(new Producer( name.str(), opts, state )));
+ clients.back()->getThread() = qpid::sys::Thread(*clients.back());
+ }
+ for (size_t j = 0; j < opts.receivers; ++j) {
+ ostringstream name;
+ name << opts.prefix << "C_" << j;
+ clients.push_back(Client::shared_ptr(new Consumer( name.str(), opts, state )));
+ clients.back()->getThread() = qpid::sys::Thread(*clients.back());
+ }
+
+ // wait for all pubs/subs to finish.... or for consumers to fail or stall.
+ uint stalledTime = 0;
+ bool done;
+ bool clientFailed = false;
+ do {
+ uint lastCount = state.getConsumedTotal();
+ qpid::sys::usleep( 1000000 );
+
+ // check each client for status
+ done = true;
+ for (std::vector<Client::shared_ptr>::iterator i = clients.begin();
+ i != clients.end(); ++i) {
+ QPID_LOG(debug, "Client " << (*i)->getName() << " state=" << (*i)->getState());
+ if ((*i)->getState() == Client::FAILURE) {
+ QPID_LOG(error, argv[0] << ": test failed with client error: " << (*i)->getErrorMsg());
+ clientFailed = true;
+ done = true;
+ break; // exit test.
+ } else if ((*i)->getState() != Client::DONE) {
+ done = false;
+ }
+ }
+
+ if (!done) {
+ // check that consumers are still receiving messages
+ if (lastCount == state.getConsumedTotal())
+ stalledTime++;
+ else {
+ lastCount = state.getConsumedTotal();
+ stalledTime = 0;
+ }
+ }
+
+ QPID_LOG(debug, "Consumed to date = " << state.getConsumedTotal() <<
+ " Published to date = " << state.getPublishedTotal() <<
+ " total=" << opts.senders * opts.messages );
+
+ } while (!done && stalledTime < opts.timeout);
+
+ if (clientFailed) {
+ status = 1;
+ } else if (stalledTime >= opts.timeout) {
+ QPID_LOG(error, argv[0] << ": test failed due to stalled consumer." );
+ status = 2;
+ }
+
+ // Wait for started threads.
+ for (std::vector<Client::shared_ptr>::iterator i = clients.begin();
+ i != clients.end(); ++i) {
+ (*i)->stop();
+ (*i)->getThread().join();
+ }
+
+ if (opts.printReport && !status) state.print(std::cout);
+ } else status = 4;
+ } catch(const std::exception& error) {
+ QPID_LOG(error, argv[0] << ": " << error.what());
+ status = 3;
+ }
+ QPID_LOG(trace, "TEST DONE [" << status << "]");
+
+ return status;
+}
diff --git a/cpp/src/tests/python_tests b/cpp/src/tests/python_tests
index e367004a71..0216b5ca7b 100755
--- a/cpp/src/tests/python_tests
+++ b/cpp/src/tests/python_tests
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
#
# Licensed to the Apache Software Foundation (ASF) under one
diff --git a/cpp/src/tests/qpid-cluster-benchmark b/cpp/src/tests/qpid-cluster-benchmark
index 4408e63866..ff787a46dd 100755
--- a/cpp/src/tests/qpid-cluster-benchmark
+++ b/cpp/src/tests/qpid-cluster-benchmark
@@ -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
@@ -19,21 +19,40 @@
#
# Benchmark script for comparing cluster performance.
-#PORT=":5555"
-BROKER=`echo $HOSTS | awk '{print $1}'` # Single broker
-BROKERS=`echo $HOSTS | sed "s/\>/$PORT/g;s/ /,/g"` # Broker URL list
-COUNT=100000
-RATE=20000 # Rate to throttle senders for latency results
-run_test() { echo $*; "$@"; echo; echo; echo; }
-# Thruput, unshared queue
-run_test qpid-cpp-benchmark --repeat 10 -b $BROKER --no-timestamp -m $COUNT
+# Default values
+PORT="5672"
+COUNT=10000
+FLOW=100 # Flow control limit on queue depth for latency.
+REPEAT=10
+QUEUES=4
+CLIENTS=3
-# Latency
-run_test qpid-cpp-benchmark --repeat 10 -b $BROKER --connection-options '{tcp-nodelay:true}' -m `expr $COUNT / 2` --send-rate $RATE
+while getopts "p:c:f:r:t:b:q:c" opt; do
+ case $opt in
+ p) PORT=$OPTARG;;
+ c) COUNT=$OPTARG;;
+ f) FLOW=$OPTARG;;
+ r) REPEAT=$OPTARG;;
+ s) SCALE=$OPTARG;;
+ b) BROKERS=$OPTARG;;
+ q) QUEUES=$OPTARG;;
+ c) CLIENTS=$OPTARG;;
+ *) echo "Unknown option"; exit 1;;
+ esac
+done
+
+BROKERS=${BROKERS:-$(echo $HOSTS | sed "s/\>/:$PORT/g;s/ /,/g")} # Broker URL list
+BROKER=`echo $BROKERS | awk -F, '{print $1}'` # First broker
+
+run_test() { echo $*; shift; "$@"; echo; echo; echo; }
# Multiple pubs/subs connect via multiple brokers (active-active)
-run_test qpid-cpp-benchmark --repeat 10 -b $BROKERS --no-timestamp --summarize -s10 -r10 -m `expr $COUNT / 10`
+run_test "multi-host-thruput" qpid-cpp-benchmark --repeat $REPEAT -b $BROKERS --no-timestamp --summarize -q$QUEUES -s$CLIENTS -r$CLIENTS -m $COUNT
# Multiple pubs/subs connect via single broker (active-passive)
-run_test qpid-cpp-benchmark --repeat 10 -b $BROKER --no-timestamp --summarize -s10 -r10 -m `expr $COUNT / 10`
+run_test "single-host-thruput" qpid-cpp-benchmark --repeat $REPEAT -b $BROKER --no-timestamp --summarize -q$QUEUES -s$CLIENTS -r$CLIENTS -m $COUNT
+
+# Latency
+run_test "latency" qpid-cpp-benchmark --repeat $REPEAT -b $BROKER --connection-options '{tcp-nodelay:true}' -m $COUNT --flow-control $FLOW
+
diff --git a/cpp/src/tests/qpid-cpp-benchmark b/cpp/src/tests/qpid-cpp-benchmark
index 1f77226b4d..300d34774f 100755
--- a/cpp/src/tests/qpid-cpp-benchmark
+++ b/cpp/src/tests/qpid-cpp-benchmark
@@ -77,6 +77,20 @@ def ssh_command(host, command):
"""Convert command into an ssh command on host with quoting"""
return ["ssh", host] + [posix_quote(arg) for arg in command]
+class Clients:
+ def __init__(self): self.clients=[]
+
+ def add(self, client):
+ self.clients.append(client)
+ return client
+
+ def kill(self):
+ for c in self.clients:
+ try: c.kill()
+ except: pass
+
+clients = Clients()
+
def start_receive(queue, index, opts, ready_queue, broker, host):
address_opts=["create:receiver"] + opts.receive_option
if opts.durable: address_opts += ["node:{durable:true}"]
@@ -101,7 +115,7 @@ def start_receive(queue, index, opts, ready_queue, broker, host):
if opts.connection_options:
command += ["--connection-options",opts.connection_options]
if host: command = ssh_command(host, command)
- return Popen(command, stdout=PIPE)
+ return clients.add(Popen(command, stdout=PIPE))
def start_send(queue, opts, broker, host):
address="%s;{%s}"%(queue,",".join(opts.send_option))
@@ -122,7 +136,7 @@ def start_send(queue, opts, broker, host):
if opts.connection_options:
command += ["--connection-options",opts.connection_options]
if host: command = ssh_command(host, command)
- return Popen(command, stdout=PIPE)
+ return clients.add(Popen(command, stdout=PIPE))
def first_line(p):
out,err=p.communicate()
@@ -133,7 +147,11 @@ def delete_queues(queues, broker):
c = qpid.messaging.Connection(broker)
c.open()
for q in queues:
- try: s = c.session().sender("%s;{delete:always}"%(q))
+ try:
+ s = c.session()
+ snd = s.sender("%s;{delete:always}"%(q))
+ snd.close()
+ s.sync()
except qpid.messaging.exceptions.NotFound: pass # Ignore "no such queue"
c.close()
@@ -145,7 +163,6 @@ def print_header(timestamp):
def parse(parser, lines): # Parse sender/receiver output
for l in lines:
fn_val = zip(parser, l)
-
return [map(lambda p: p[0](p[1]), zip(parser,line.split())) for line in lines]
def parse_senders(senders):
@@ -156,11 +173,12 @@ def parse_receivers(receivers):
def print_data(send_stats, recv_stats):
for send,recv in map(None, send_stats, recv_stats):
- if send: print send[0],
+ line=""
+ if send: line += "%d"%send[0]
if recv:
- print "\t\t%d"%recv[0],
- if len(recv) == 4: print "\t%.2f\t%.2f\t%.2f"%tuple(recv[1:]),
- print
+ line += "\t\t%d"%recv[0]
+ if len(recv) == 4: line += "\t%.2f\t%.2f\t%.2f"%tuple(recv[1:])
+ print line
def print_summary(send_stats, recv_stats):
def avg(s): sum(s) / len(s)
@@ -184,11 +202,11 @@ class ReadyReceiver:
self.receiver = self.connection.session().receiver(
"%s;{create:receiver,delete:receiver,node:{durable:false}}"%(queue))
self.receiver.session.sync()
- self.timeout=2
+ self.timeout=10
def wait(self, receivers):
try:
- for i in xrange(len(receivers)): self.receiver.fetch(self.timeout)
+ for i in receivers: self.receiver.fetch(self.timeout)
self.connection.close()
except qpid.messaging.Empty:
for r in receivers:
@@ -197,7 +215,8 @@ class ReadyReceiver:
raise Exception("Receiver error: %s"%(out))
raise Exception("Timed out waiting for receivers to be ready")
-def flatten(l): return sum(map(lambda s: s.split(","), l),[])
+def flatten(l):
+ return sum(map(lambda s: re.split(re.compile("\s*,\s*|\s+"), s), l), [])
class RoundRobin:
def __init__(self,items):
@@ -221,20 +240,22 @@ def main():
receive_out = ""
ready_queue="%s-ready"%(opts.queue_name)
queues = ["%s-%s"%(opts.queue_name, i) for i in xrange(opts.queues)]
- for i in xrange(opts.repeat):
- delete_queues(queues, opts.broker[0])
- ready_receiver = ReadyReceiver(ready_queue, opts.broker[0])
- receivers = [start_receive(q, j, opts, ready_queue, brokers.next(), client_hosts.next())
- for q in queues for j in xrange(opts.receivers)]
- ready_receiver.wait(filter(None, receivers)) # Wait for receivers to be ready.
- senders = [start_send(q, opts,brokers.next(), client_hosts.next())
- for q in queues for j in xrange(opts.senders)]
- if opts.report_header and i == 0: print_header(opts.timestamp)
- send_stats=parse_senders(senders)
- recv_stats=parse_receivers(receivers)
- if opts.summarize: print_summary(send_stats, recv_stats)
- else: print_data(send_stats, recv_stats)
- delete_queues(queues, opts.broker[0])
+ try:
+ for i in xrange(opts.repeat):
+ delete_queues(queues, opts.broker[0])
+ ready_receiver = ReadyReceiver(ready_queue, opts.broker[0])
+ receivers = [start_receive(q, j, opts, ready_queue, brokers.next(), client_hosts.next())
+ for q in queues for j in xrange(opts.receivers)]
+ ready_receiver.wait(filter(None, receivers)) # Wait for receivers to be ready.
+ senders = [start_send(q, opts,brokers.next(), client_hosts.next())
+ for q in queues for j in xrange(opts.senders)]
+ if opts.report_header and i == 0: print_header(opts.timestamp)
+ send_stats=parse_senders(senders)
+ recv_stats=parse_receivers(receivers)
+ if opts.summarize: print_summary(send_stats, recv_stats)
+ else: print_data(send_stats, recv_stats)
+ delete_queues(queues, opts.broker[0])
+ finally: clients.kill() # No strays
if __name__ == "__main__": main()
diff --git a/cpp/src/tests/qpid-ctrl b/cpp/src/tests/qpid-ctrl
index 7b46c190fb..4246c57898 100755
--- a/cpp/src/tests/qpid-ctrl
+++ b/cpp/src/tests/qpid-ctrl
@@ -92,7 +92,10 @@ try:
arguments = {}
for a in args:
name, val = nameval(a)
- arguments[name] = val
+ if val[0] == '{' or val[0] == '[':
+ arguments[name] = eval(val)
+ else:
+ arguments[name] = val
content = {
"_object_id": {"_object_name": object_name},
"_method_name": method_name,
diff --git a/cpp/src/tests/qpid-perftest.cpp b/cpp/src/tests/qpid-perftest.cpp
index 4d7b563c8c..664f0cf877 100644
--- a/cpp/src/tests/qpid-perftest.cpp
+++ b/cpp/src/tests/qpid-perftest.cpp
@@ -173,7 +173,7 @@ struct Opts : public TestOptions {
if (count % subs) {
count += subs - (count % subs);
cout << "WARNING: Adjusted --count to " << count
- << " the nearest multiple of --nsubs" << endl;
+ << " the next multiple of --nsubs" << endl;
}
totalPubs = pubs*qt;
totalSubs = subs*qt;
@@ -396,7 +396,7 @@ struct Controller : public Client {
void run() { // Controller
try {
// Wait for subscribers to be ready.
- process(opts.totalSubs, fqn("sub_ready"), bind(expect, _1, "ready"));
+ process(opts.totalSubs, fqn("sub_ready"), boost::bind(expect, _1, "ready"));
LocalQueue pubDone;
LocalQueue subDone;
@@ -413,7 +413,7 @@ struct Controller : public Client {
AbsTime start=now();
send(opts.totalPubs, fqn("pub_start"), "start"); // Start publishers
if (j) {
- send(opts.totalPubs, fqn("sub_iteration"), "next"); // Start subscribers on next iteration
+ send(opts.totalSubs, fqn("sub_iteration"), "next"); // Start subscribers on next iteration
}
Stats pubRates;
@@ -423,8 +423,10 @@ struct Controller : public Client {
process(opts.totalSubs, subDone, fqn("sub_done"), boost::ref(subRates));
AbsTime end=now();
-
double time=secs(start, end);
+ if (time <= 0.0) {
+ throw Exception("ERROR: Test completed in zero seconds. Try again with a larger message count.");
+ }
double txrate=opts.transfers/time;
double mbytes=(txrate*opts.size)/(1024*1024);
@@ -508,10 +510,11 @@ struct PublishThread : public Client {
}
SubscriptionManager subs(session);
LocalQueue lq;
- subs.setFlowControl(1, SubscriptionManager::UNLIMITED, true);
- subs.subscribe(lq, fqn("pub_start"));
+ subs.setFlowControl(0, SubscriptionManager::UNLIMITED, false);
+ Subscription cs = subs.subscribe(lq, fqn("pub_start"));
for (size_t j = 0; j < opts.iterations; ++j) {
+ cs.grantMessageCredit(1);
expect(lq.pop().getData(), "start");
AbsTime start=now();
for (size_t i=0; i<opts.count; i++) {
@@ -543,6 +546,9 @@ struct PublishThread : public Client {
if (opts.confirm) session.sync();
AbsTime end=now();
double time=secs(start,end);
+ if (time <= 0.0) {
+ throw Exception("ERROR: Test completed in zero seconds. Try again with a larger message count.");
+ }
// Send result to controller.
Message report(lexical_cast<string>(opts.count/time), fqn("pub_done"));
@@ -638,7 +644,9 @@ struct SubscribeThread : public Client {
//
// For now verify order only for a single publisher.
size_t offset = opts.uniqueData ? 5 /*marker is 'data:'*/ : 0;
- size_t n = *reinterpret_cast<const size_t*>(msg.getData().data() + offset);
+ size_t n;
+ memcpy (&n, reinterpret_cast<const char*>(msg.getData().data() + offset),
+ sizeof(n));
if (opts.pubs == 1) {
if (opts.subs == 1 || opts.mode == FANOUT) verify(n==expect, "==", expect, n);
else verify(n>=expect, ">=", expect, n);
diff --git a/cpp/src/tests/qpid-receive.cpp b/cpp/src/tests/qpid-receive.cpp
index 012d544a2e..9c713e872a 100644
--- a/cpp/src/tests/qpid-receive.cpp
+++ b/cpp/src/tests/qpid-receive.cpp
@@ -53,6 +53,7 @@ struct Options : public qpid::Options
bool forever;
uint messages;
bool ignoreDuplicates;
+ bool checkRedelivered;
uint capacity;
uint ackFrequency;
uint tx;
@@ -75,6 +76,7 @@ struct Options : public qpid::Options
forever(false),
messages(0),
ignoreDuplicates(false),
+ checkRedelivered(false),
capacity(1000),
ackFrequency(100),
tx(0),
@@ -92,10 +94,11 @@ struct Options : public qpid::Options
("broker,b", qpid::optValue(url, "URL"), "url of broker to connect to")
("address,a", qpid::optValue(address, "ADDRESS"), "address to receive from")
("connection-options", qpid::optValue(connectionOptions, "OPTIONS"), "options for the connection")
- ("timeout,t", qpid::optValue(timeout, "TIMEOUT"), "timeout in seconds to wait before exiting")
+ ("timeout", qpid::optValue(timeout, "TIMEOUT"), "timeout in seconds to wait before exiting")
("forever,f", qpid::optValue(forever), "ignore timeout and wait forever")
("messages,m", qpid::optValue(messages, "N"), "Number of messages to receive; 0 means receive indefinitely")
("ignore-duplicates", qpid::optValue(ignoreDuplicates), "Detect and ignore duplicates (by checking 'sn' header)")
+ ("check-redelivered", qpid::optValue(checkRedelivered), "Fails with exception if a duplicate is not marked as redelivered (only relevant when ignore-duplicates is selected)")
("capacity", qpid::optValue(capacity, "N"), "Pre-fetch window (0 implies no pre-fetch)")
("ack-frequency", qpid::optValue(ackFrequency, "N"), "Ack frequency (0 implies none of the messages will get accepted)")
("tx", qpid::optValue(tx, "N"), "batch size for transactions (0 implies transaction are not used)")
@@ -216,6 +219,8 @@ int main(int argc, char ** argv)
std::cout << msg.getContent() << std::endl;//TODO: handle map or list messages
if (opts.messages && count >= opts.messages) done = true;
}
+ } else if (opts.checkRedelivered && !msg.getRedelivered()) {
+ throw qpid::Exception("duplicate sequence number received, message not marked as redelivered!");
}
if (opts.tx && (count % opts.tx == 0)) {
if (opts.rollbackFrequency && (++txCount % opts.rollbackFrequency == 0)) {
@@ -257,7 +262,7 @@ int main(int argc, char ** argv)
return 0;
}
} catch(const std::exception& error) {
- std::cerr << "Failure: " << error.what() << std::endl;
+ std::cerr << "qpid-receive: " << error.what() << std::endl;
connection.close();
return 1;
}
diff --git a/cpp/src/tests/qpid-send.cpp b/cpp/src/tests/qpid-send.cpp
index 6a7e7838ce..b1213a484f 100644
--- a/cpp/src/tests/qpid-send.cpp
+++ b/cpp/src/tests/qpid-send.cpp
@@ -28,6 +28,7 @@
#include <qpid/messaging/FailoverUpdates.h>
#include <qpid/sys/Time.h>
#include <qpid/sys/Monitor.h>
+#include <qpid/sys/SystemInfo.h>
#include "TestOptions.h"
#include "Statistics.h"
@@ -76,6 +77,11 @@ struct Options : public qpid::Options
uint flowControl;
bool sequence;
bool timestamp;
+ std::string groupKey;
+ std::string groupPrefix;
+ uint groupSize;
+ bool groupRandSize;
+ uint groupInterleave;
Options(const std::string& argv0=std::string())
: qpid::Options("Options"),
@@ -100,19 +106,23 @@ struct Options : public qpid::Options
sendRate(0),
flowControl(0),
sequence(true),
- timestamp(true)
+ timestamp(true),
+ groupPrefix("GROUP-"),
+ groupSize(10),
+ groupRandSize(false),
+ groupInterleave(1)
{
addOptions()
("broker,b", qpid::optValue(url, "URL"), "url of broker to connect to")
- ("address,a", qpid::optValue(address, "ADDRESS"), "address to drain from")
+ ("address,a", qpid::optValue(address, "ADDRESS"), "address to send to")
("connection-options", qpid::optValue(connectionOptions, "OPTIONS"), "options for the connection")
("messages,m", qpid::optValue(messages, "N"), "stop after N messages have been sent, 0 means no limit")
("id,i", qpid::optValue(id, "ID"), "use the supplied id instead of generating one")
("reply-to", qpid::optValue(replyto, "REPLY-TO"), "specify reply-to address")
("send-eos", qpid::optValue(sendEos, "N"), "Send N EOS messages to mark end of input")
("durable", qpid::optValue(durable, "yes|no"), "Mark messages as durable.")
- ("ttl", qpid::optValue(ttl, "msecs"), "Time-to-live for messages, in milliseconds")
- ("priority", qpid::optValue(priority, "PRIORITY"), "Priority for messages (higher value implies higher priority)")
+ ("ttl", qpid::optValue(ttl, "msecs"), "Time-to-live for messages, in milliseconds")
+ ("priority", qpid::optValue(priority, "PRIORITY"), "Priority for messages (higher value implies higher priority)")
("property,P", qpid::optValue(properties, "NAME=VALUE"), "specify message property")
("correlation-id", qpid::optValue(correlationid, "ID"), "correlation-id for message")
("user-id", qpid::optValue(userid, "USERID"), "userid for message")
@@ -131,6 +141,11 @@ struct Options : public qpid::Options
("flow-control", qpid::optValue(flowControl,"N"), "Do end to end flow control to limit queue depth to 2*N. 0 means no flow control.")
("sequence", qpid::optValue(sequence, "yes|no"), "Add a sequence number messages property (required for duplicate/lost message detection)")
("timestamp", qpid::optValue(timestamp, "yes|no"), "Add a time stamp messages property (required for latency measurement)")
+ ("group-key", qpid::optValue(groupKey, "KEY"), "Generate groups of messages using message header 'KEY' to hold the group identifier")
+ ("group-prefix", qpid::optValue(groupPrefix, "STRING"), "Generate group identifers with 'STRING' prefix (if group-key specified)")
+ ("group-size", qpid::optValue(groupSize, "N"), "Number of messages per a group (if group-key specified)")
+ ("group-randomize-size", qpid::optValue(groupRandSize), "Randomize the number of messages per group to [1...group-size] (if group-key specified)")
+ ("group-interleave", qpid::optValue(groupInterleave, "N"), "Simultaineously interleave messages from N different groups (if group-key specified)")
("help", qpid::optValue(help), "print this usage statement");
add(log);
}
@@ -252,6 +267,68 @@ class MapContentGenerator : public ContentGenerator {
const Options& opts;
};
+// tag each generated message with a group identifer
+//
+class GroupGenerator {
+public:
+ GroupGenerator(const std::string& key,
+ const std::string& prefix,
+ const uint size,
+ const bool randomize,
+ const uint interleave)
+ : groupKey(key), groupPrefix(prefix), groupSize(size),
+ randomizeSize(randomize), groupSuffix(0)
+ {
+ if (randomize) srand((unsigned int)qpid::sys::SystemInfo::getProcessId());
+
+ for (uint i = 0; i < 1 || i < interleave; ++i) {
+ newGroup();
+ }
+ current = groups.begin();
+ }
+
+ void setGroupInfo(Message &msg)
+ {
+ if (current == groups.end())
+ current = groups.begin();
+ msg.getProperties()[groupKey] = current->id;
+ // std::cout << "SENDING GROUPID=[" << current->id << "]" << std::endl;
+ if (++(current->count) == current->size) {
+ newGroup();
+ groups.erase(current++);
+ } else
+ ++current;
+ }
+
+ private:
+ const std::string& groupKey;
+ const std::string& groupPrefix;
+ const uint groupSize;
+ const bool randomizeSize;
+
+ uint groupSuffix;
+
+ struct GroupState {
+ std::string id;
+ const uint size;
+ uint count;
+ GroupState( const std::string& i, const uint s )
+ : id(i), size(s), count(0) {}
+ };
+ typedef std::list<GroupState> GroupList;
+ GroupList groups;
+ GroupList::iterator current;
+
+ void newGroup() {
+ std::ostringstream groupId(groupPrefix, ios_base::out|ios_base::ate);
+ groupId << groupSuffix++;
+ uint size = (randomizeSize) ? (rand() % groupSize) + 1 : groupSize;
+ // std::cout << "New group: GROUPID=[" << groupId.str() << "] size=" << size << std::endl;
+ GroupState group( groupId.str(), size );
+ groups.push_back( group );
+ }
+};
+
int main(int argc, char ** argv)
{
Connection connection;
@@ -296,6 +373,14 @@ int main(int argc, char ** argv)
else
contentGen.reset(new FixedContentGenerator(opts.contentString));
+ std::auto_ptr<GroupGenerator> groupGen;
+ if (!opts.groupKey.empty())
+ groupGen.reset(new GroupGenerator(opts.groupKey,
+ opts.groupPrefix,
+ opts.groupSize,
+ opts.groupRandSize,
+ opts.groupInterleave));
+
qpid::sys::AbsTime start = qpid::sys::now();
int64_t interval = 0;
if (opts.sendRate) interval = qpid::sys::TIME_SEC/opts.sendRate;
@@ -312,9 +397,6 @@ int main(int argc, char ** argv)
++sent;
if (opts.sequence)
msg.getProperties()[SN] = sent;
- if (opts.timestamp)
- msg.getProperties()[TS] = int64_t(
- qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now()));
if (opts.flowControl) {
if ((sent % opts.flowControl) == 0) {
msg.setReplyTo(flowControlAddress);
@@ -323,6 +405,12 @@ int main(int argc, char ** argv)
else
msg.setReplyTo(Address()); // Clear the reply address.
}
+ if (groupGen.get())
+ groupGen->setGroupInfo(msg);
+
+ if (opts.timestamp)
+ msg.getProperties()[TS] = int64_t(
+ qpid::sys::Duration(qpid::sys::EPOCH, qpid::sys::now()));
sender.send(msg);
reporter.message(msg);
@@ -368,7 +456,7 @@ int main(int argc, char ** argv)
return 0;
}
} catch(const std::exception& error) {
- std::cout << "Failed: " << error.what() << std::endl;
+ std::cerr << "qpid-send: " << error.what() << std::endl;
connection.close();
return 1;
}
diff --git a/cpp/src/tests/qrsh.cpp b/cpp/src/tests/qrsh.cpp
deleted file mode 100644
index 0cb52b6b05..0000000000
--- a/cpp/src/tests/qrsh.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-#include <qpid/client/Connection.h>
-#include <qpid/client/Session.h>
-#include <qpid/client/AsyncSession.h>
-#include <qpid/client/Message.h>
-#include <qpid/client/MessageListener.h>
-#include <qpid/client/SubscriptionManager.h>
-
-#include <stdio.h>
-#include <cstdlib>
-#include <iostream>
-
-#include <sstream>
-
-using namespace qpid::client;
-using namespace qpid::framing;
-
-using namespace std;
-
-namespace qpid {
-namespace tests {
-
-class ResponseListener : public MessageListener
-{
- public :
-
- int exitCode;
-
- ResponseListener ( SubscriptionManager & subscriptions )
- : exitCode(-1),
- subscriptions ( subscriptions )
- {
- }
-
- virtual void
- received ( Message & message )
- {
- char first_word[1000];
- sscanf ( message.getData().c_str(), "%s", first_word );
-
- if ( ! strcmp ( first_word, "wait_response" ) )
- {
- // If we receive a message here, parse out the exit code.
- sscanf ( message.getData().c_str(), "%*s%d", & exitCode );
- subscriptions.cancel(message.getDestination());
- }
- else
- if ( ! strcmp ( first_word, "get_response" ) )
- {
- // The remainder of the message is the file we requested.
- fprintf ( stdout,
- "%s",
- message.getData().c_str() + strlen("get_response" )
- );
- subscriptions.cancel(message.getDestination());
- }
- }
-
-
- private :
-
- SubscriptionManager & subscriptions;
-};
-
-}} // namespace qpid::tests
-
-using namespace qpid::tests;
-
-/*
- * argv[1] host
- * argv[2] port
- * argv[3] server name
- * argv[4] command name
- * argv[5..N] args to the command
- */
-int
-main ( int argc, char ** argv )
-{
- const char* host = argv[1];
- int port = atoi(argv[2]);
-
-
- Connection connection;
-
- try
- {
- connection.open ( host, port );
- Session session = connection.newSession ( );
-
- // Make a queue and bind it to fanout.
- string myQueue = session.getId().getName();
-
- session.queueDeclare ( arg::queue=myQueue,
- arg::exclusive=true,
- arg::autoDelete=true
- );
-
- session.exchangeBind ( arg::exchange="amq.fanout",
- arg::queue=myQueue,
- arg::bindingKey="my-key"
- );
-
- // Get ready to listen for the wait-response.
- // or maybe a get-response.
- // ( Although this may not be one of those types
- // of command, get ready anyway.
- SubscriptionManager subscriptions ( session );
- ResponseListener responseListener ( subscriptions );
- subscriptions.subscribe ( responseListener, myQueue );
-
- bool response_command = false;
- if(! strcmp("exec_wait", argv[4] ))
- response_command = true;
- else
- if(! strcmp("exited", argv[4] ))
- response_command = true;
- else
- if(! strcmp("get", argv[4] ))
- response_command = true;
-
- // Send the payload message.
- // Skip "qrsh host_name port"
- Message message;
- stringstream ss;
- for ( int i = 3; i < argc; ++ i )
- ss << argv[i] << ' ';
-
- message.setData ( ss.str() );
-
- session.messageTransfer(arg::content=message,
- arg::destination="amq.fanout");
-
- if ( response_command )
- subscriptions.run();
-
- session.close();
- connection.close();
- return responseListener.exitCode;
- }
- catch ( exception const & e)
- {
- cerr << e.what() << endl;
- }
-
- return 1;
-}
-
-
-
diff --git a/cpp/src/tests/qrsh_run.cpp b/cpp/src/tests/qrsh_run.cpp
deleted file mode 100644
index cfdd0cef80..0000000000
--- a/cpp/src/tests/qrsh_run.cpp
+++ /dev/null
@@ -1,321 +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 <iostream>
-#include <sstream>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <fcntl.h>
-#include <errno.h>
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-
-using namespace std;
-
-
-
-int
-main ( int argc, char ** argv )
-{
- int exit_code = -1;
- int fd[2];
- int my_pid = getpid();
- int child_pid;
-
- pipe(fd);
-
- char const * root_dir = argv[1]; // This arg is prepended by qrsh_server.
- char const * child_name = argv[2]; // This arg comes from qrsh.
- char const * child_path = argv[3]; // This arg comes from qrsh.
-
- // This is the problem..
- fprintf ( stderr, "MDEBUG qrsh_run: root_dir: |%s|\n", root_dir );
- fprintf ( stderr, "MDEBUG qrsh_run: child_name: |%s|\n", child_name );
- fprintf ( stderr, "MDEBUG qrsh_run: child_path: |%s|\n", child_path );
-
- /*
- * A named child is one for whom we will create a directory and
- * store information. There are some magic names that are not
- * real symbolic names -- but are instead the names of actions.
- */
-
- bool named_child = true;
-
- if ( ! strcmp ( child_name, "exec" ) )
- named_child = false;
- else
- if ( ! strcmp ( child_name, "exec_wait" ) )
- named_child = false;
- else
- if ( ! strcmp ( child_name, "exited" ) )
- named_child = false;
- else
- named_child = true;
-
- stringstream child_dir_name;
-
- if ( named_child )
- {
- child_dir_name << root_dir
- << '/'
- << child_name;
-
- /*
- * Make the child directory before forking, or there is
- * a race in which the child might be trying to make its
- * stdout and stderr files while we are tring to make
- * the directory.
- */
- if ( -1 == mkdir ( child_dir_name.str().c_str(), 0777 ) )
- {
- fprintf ( stderr,
- "qrsh_run error: Can't mkdir |%s|\n",
- child_dir_name.str().c_str()
- );
- exit ( 1 );
- }
-
- }
- else
- /*
- * If this is an 'exited' command that means we are
- * waiting for a pre-existing child.
- */
- if ( ! strcmp ( child_name, "exited" ) )
- {
- int wait_pid = atoi(child_path);
-
- // Find the child's symbolic name.
- stringstream pid_to_name_file_name;
- pid_to_name_file_name << root_dir
- << '/'
- << wait_pid;
- FILE * fp = fopen ( pid_to_name_file_name.str().c_str(), "r" );
- if (! fp)
- {
- fprintf ( stderr,
- "qrsh_run %d error: Can't open pid2name file |%s|.\n",
- my_pid,
- pid_to_name_file_name.str().c_str()
- );
- exit(1);
- }
- char symbolic_name[1000];
- strcpy ( symbolic_name, "qrsh_no_name" );
- fscanf ( fp, "%s", symbolic_name );
- fclose ( fp );
-
- // Make the name of the child's exit code file.
- stringstream exit_code_file_name;
- exit_code_file_name << root_dir
- << '/'
- << symbolic_name
- << "/exit_code";
-
- struct stat stat_buf;
- int file_does_not_exist = stat ( exit_code_file_name.str().c_str(), & stat_buf );
-
- /*
- * If the result of stat is zero, the file exists, which means that
- * the command has exited. The question we are being asked here is
- * "has it exited yet?"
- */
- if ( ! file_does_not_exist )
- return 1;
- else
- if ( errno == ENOENT )
- return 0;
- else
- return 2 ;
- }
-
-
- // We are not waiting on a pre-wxiting child: we have a
- // new child to create.
-
- child_pid = fork();
-
- if ( child_pid == 0 )
- {
- // This code is executed in the child process.
-
- // If it's a *named* child, then redirect its stdout and stderr.
- if ( named_child )
- {
- stringstream stdout_path,
- stderr_path;
-
- // Redirect the child's stdout. -----------------
- stdout_path << root_dir
- << '/'
- << child_name
- << '/'
- << "stdout";
-
- int redirected_stdout = open ( stdout_path.str().c_str(),
- O_WRONLY|O_CREAT|O_TRUNC,
- S_IRWXU|S_IRWXG|S_IRWXO
- );
- if ( redirected_stdout < 0 )
- {
- perror ( "qrsh_run: error opening redirected_stdout: " );
- fprintf ( stderr, "stdout path: |%s|\n", stdout_path.str().c_str() );
- exit ( 1 );
- }
- if ( -1 == dup2 ( redirected_stdout, 1 ) )
- {
- perror ( "qrsh_run: dup2 (stdout) error: " );
- exit(1);
- }
-
- // Redirect the child's stderr. -----------------
- stderr_path << root_dir
- << '/'
- << child_name
- << '/'
- << "stderr";
-
- int redirected_stderr = open ( stderr_path.str().c_str(),
- O_WRONLY|O_CREAT|O_TRUNC,
- S_IRWXU|S_IRWXG|S_IRWXO
- );
- if ( redirected_stderr < 0 )
- {
- perror ( "qrsh_run: error opening redirected_stderr: " );
- fprintf ( stderr, "stderr path: |%s|\n", stderr_path.str().c_str() );
- exit ( 1 );
- }
- if(-1 == dup2 ( redirected_stderr, 2 ) )
- {
- perror ( "qrsh_run: dup2 (stderr) error: " );
- exit(1);
- }
- }
-
- fprintf ( stderr, "MDEBUG ------------- qrsh_run argv -------------\n" );
- for ( int i = 0; i < argc; ++ i )
- fprintf ( stderr, "MDEBUG argv[%d] : |%s|\n", i, argv[i] );
-
- execv ( child_path, argv + 2 );
- perror ( "qrsh_run: execv error: " );
- fprintf ( stderr, "on path |%s|\n", child_path );
- exit ( 1 );
- }
- else
- {
- // This code is executed in the parent process.
-
- if ( named_child )
- {
- // Write the name-to-pid mapping.
- stringstream pid_file_name;
- pid_file_name << child_dir_name.str()
- << "/pid";
-
- FILE * fp;
- if ( ! (fp = fopen ( pid_file_name.str().c_str(), "w") ) )
- {
- fprintf ( stderr,
- "qrsh_run %d error: Can't open file |%s|\n",
- my_pid,
- pid_file_name.str().c_str()
- );
- exit(1);
- }
- fprintf ( fp, "%d\n", child_pid );
- fclose ( fp );
-
-
- // Write the pid-to-name mapping.
- stringstream name_to_pid_file_name;
- name_to_pid_file_name << root_dir
- << '/'
- << child_pid;
- if(! (fp = fopen ( name_to_pid_file_name.str().c_str(), "w")))
- {
- fprintf ( stderr,
- "qrsh_run %d error: Can't open file |%s|\n",
- my_pid,
- name_to_pid_file_name.str().c_str()
- );
- exit(1);
- }
- fprintf ( fp, "%s\n", child_name );
- fclose(fp);
- }
-
- pid_t awaited_pid;
- while ( 0 == (awaited_pid = waitpid ( child_pid, & exit_code, WNOHANG)) )
- {
- fprintf ( stderr,
- "qrsh_run %d info: parent: waiting for child %d...\n",
- my_pid,
- child_pid
- );
- sleep(1);
- }
-
- if ( -1 == awaited_pid )
- {
- fprintf ( stderr, "qrsh_run error awaiting child!\n" );
- exit ( 1 );
- }
-
- /*
- * Write the exit code.
- */
- exit_code >>= 8;
-
- if ( named_child )
- {
- if ( child_pid == awaited_pid )
- {
- stringstream exit_code_file_name;
- exit_code_file_name << child_dir_name.str()
- << "/exit_code";
-
- FILE * fp;
- if ( ! (fp = fopen ( exit_code_file_name.str().c_str(), "w") ) )
- {
- fprintf ( stderr,
- "qrsh_run error: Can't open file |%s|\n",
- exit_code_file_name.str().c_str()
- );
- exit(1);
- }
- fprintf ( fp, "%d\n", exit_code );
- fclose ( fp );
- }
- }
- }
-
- fprintf ( stderr, "MDEBUG qrsh_run returning exit code %d\n", exit_code );
- return exit_code;
-}
-
-
-
-
diff --git a/cpp/src/tests/qrsh_server.cpp b/cpp/src/tests/qrsh_server.cpp
deleted file mode 100644
index 782f1e6c7c..0000000000
--- a/cpp/src/tests/qrsh_server.cpp
+++ /dev/null
@@ -1,1068 +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 <stdio.h>
-#include <set>
-#include <string>
-#include <sstream>
-#include <unistd.h>
-#include <cstdlib>
-#include <iostream>
-#include <map>
-#include <dirent.h>
-
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-
-#include <qpid/client/Connection.h>
-#include <qpid/client/Session.h>
-#include <qpid/client/AsyncSession.h>
-#include <qpid/client/Message.h>
-#include <qpid/client/MessageListener.h>
-#include <qpid/client/SubscriptionManager.h>
-
-
-using namespace qpid::client;
-using namespace qpid::framing;
-using namespace std;
-
-
-namespace qpid {
-namespace tests {
-
-int
-mrand ( int max_desired_val )
-{
- double zero_to_one = (double) rand() / (double) RAND_MAX;
- return (int) (zero_to_one * (double) max_desired_val);
-}
-
-
-
-char *
-file2str ( char const * file_name )
-{
- FILE * fp = fopen ( file_name, "r" );
- if(! fp)
- {
- fprintf ( stderr, "file2str error: can't open file |%s|.\n", file_name );
- return 0;
- }
-
- fseek ( fp, 0, SEEK_END );
- size_t file_len = (size_t) ftell ( fp );
- rewind ( fp );
- char * content = (char *) malloc ( file_len + 1 );
-
- if ( ! content )
- {
- fprintf ( stderr,
- "file2str error: can't malloc %d bytes.\n",
- (int)file_len
- );
- return 0;
- }
-
- size_t items_read = fread ( content, file_len, 1, fp );
-
- if ( 1 != items_read )
- {
- fprintf ( stderr, "file2str error: read failed.\n" );
- free ( content );
- return 0;
- }
-
- fclose ( fp );
- content[file_len] = 0;
-
- return content;
-}
-
-
-
-
-
-class QrshServer : public MessageListener
-{
- public:
-
- QrshServer ( SubscriptionManager & subscriptions,
- char const * name,
- char const * qrsh_run_path,
- char const * host,
- int port
- );
-
- virtual void received ( Message & message);
-
-
- private:
-
- set<string> all_server_names;
-
- stringstream data_dir;
-
- SubscriptionManager & subscriptions;
-
- // Is this message addressed to me?
- bool myMessage ( Message const & message );
-
- /* ----------------------------------------------
- * Special Commands
- * These are commands that the qrsh_server executes
- * directly, rather than through a child process
- * instance of qrsh_run.
- */
- void runCommand ( Message const & message );
- void execute ( Message const & message );
- void wait ( Message const & message );
- void exited ( Message const & message );
- void get ( Message const & message );
- void rememberIntroduction ( Message const & message );
- void getStraw ( Message const & message );
- void addAlias ( Message const & message );
-
- void start ( );
- void sayHello ( );
- void sayName ( );
- // end Special Commands ------------------------
-
-
- void saveCommand ( Message const & message );
-
- void send ( string const & content );
-
- void drawStraws ( );
- void getNames ( );
- void runSavedCommand ( );
-
- char ** getArgs ( char const * s );
- bool isProcessName ( char const * s );
- int string_countWords ( char const * s );
- char const * skipWord ( char const * s );
-
-
- void string_replaceAll ( string & str,
- string & target,
- string & replacement
- );
-
-
- string name,
- qrsh_run_path,
- host;
-
- vector<string *> aliases;
-
- int port;
-
- map < char *, int > abstract_name_map;
-
- set < string > myFellowBrokers;
-
- bool saidHello;
-
- Message savedCommand;
-
- vector < int > straws;
- int myStraw;
-
-};
-
-
-
-QrshServer::QrshServer ( SubscriptionManager & subs,
- char const * name,
- char const * qrsh_run_path,
- char const * host,
- int port
- )
- : subscriptions ( subs ),
- name ( name ),
- qrsh_run_path ( qrsh_run_path ),
- host ( host ),
- port ( port ),
- saidHello ( false ),
- myStraw ( 0 )
-{
- data_dir << "/tmp/qrsh_"
- << getpid();
-
- if(mkdir ( data_dir.str().c_str(), 0777 ) )
- {
- fprintf ( stderr,
- "QrshServer::QrshServer error: can't mkdir |%s|\n",
- data_dir.str().c_str()
- );
- exit ( 1 );
- }
-}
-
-
-
-void
-QrshServer::saveCommand ( Message const & message )
-{
- savedCommand = message;
-}
-
-
-
-void
-QrshServer::runSavedCommand ( )
-{
- runCommand ( savedCommand );
-}
-
-
-
-void
-QrshServer::start ( )
-{
- stringstream announcement_data;
- announcement_data << "hello_my_name_is "
- << name;
-
- send ( announcement_data.str() );
-
- saidHello = true;
-}
-
-
-
-
-void
-QrshServer::send ( string const & content )
-{
- try
- {
- Message message;
- message.setData ( content );
-
- Connection connection;
- connection.open ( host, port );
- Session session = connection.newSession ( );
- session.messageTransfer ( arg::content = message,
- arg::destination = "amq.fanout"
- );
- session.close();
- connection.close();
- }
- catch ( exception const & e )
- {
- fprintf ( stderr, "QrshServer::send error: |%s|\n", e.what() );
- }
-}
-
-
-
-
-void
-QrshServer::sayHello ( )
-{
- if ( saidHello )
- return;
-
- stringstream ss;
-
- ss << "hello_my_name_is "
- << name;
-
- send ( ss.str() );
- saidHello = true;
-}
-
-
-
-void
-QrshServer::sayName ( )
-{
- fprintf ( stderr, "My name is: |%s|\n", name.c_str() );
-}
-
-
-
-
-void
-QrshServer::drawStraws ( )
-{
- myStraw = mrand ( 1000000000 );
- stringstream ss;
- ss << "straw "
- << name
- << ' '
- << myStraw;
- send ( ss.str() );
-}
-
-
-
-void
-QrshServer::getStraw ( Message const & message )
-{
- int straw;
-
- char brokerName[1000];
- sscanf ( message.getData().c_str(), "%*s%s", brokerName );
-
- if ( ! strcmp ( brokerName, name.c_str() ) )
- return;
-
- sscanf ( message.getData().c_str(), "%*s%*s%d", & straw );
- straws.push_back ( straw );
-
- bool i_win = true;
- int ties = 0;
-
- if ( straws.size() >= myFellowBrokers.size() )
- {
- // All votes are in! Let's see if I win!
- for ( unsigned int i = 0; i < straws.size(); ++ i )
- {
- if ( straws[i] == myStraw )
- ++ ties;
- else
- if ( straws[i] > myStraw )
- {
- i_win = false;
- break;
- }
- }
-
- if ( i_win && (ties <= 0) )
- {
- myStraw = 0;
- straws.clear();
- runSavedCommand ( );
- }
- else
- if ( i_win && (ties > 0) )
- {
- fprintf ( stderr, "MDEBUG oh no! drawStraws error: server %s tied with straw %d!\n", name.c_str(), straw );
- }
- }
-}
-
-
-
-
-/*
- * "APB" command (all-points-bullitens (commands that are not addressed
- * specifically to any server)) are handled directly, here.
- * Because if I return simply "true", the normal command processing code
- * will misinterpret the command.
- */
-bool
-QrshServer::myMessage ( Message const & message )
-{
- int const maxlen = 100;
- char head[maxlen];
- char first_word [ maxlen + 1 ];
- strncpy ( head, message.getData().c_str(), maxlen );
- sscanf ( head, "%s", first_word );
-
- if ( ! strcmp ( name.c_str(), first_word ) )
- {
- return true;
- }
- else
- {
- // Is the given name one of my aliases?
- char possibleAlias[1000];
- if(1 == sscanf ( message.getData().c_str(), "%s", possibleAlias ))
- {
- for ( unsigned int i = 0; i < aliases.size(); ++ i )
- {
-
- if ( ! strcmp ( possibleAlias, aliases[i]->c_str() ))
- {
- return true;
- }
- }
- }
- }
-
- if ( ! strcmp ( first_word, "hello_my_name_is" ) )
- {
- rememberIntroduction ( message );
- sayHello ( );
- return false;
- }
- else
- if ( ! strcmp ( first_word, "straw" ) )
- {
- getStraw ( message );
- return false;
- }
- else
- if ( ! strcmp ( first_word, "all" ) )
- {
- return true;
- }
- else
- if ( ! strcmp ( first_word, "any" ) )
- {
- straws.clear();
- usleep ( 200000 );
- saveCommand ( message );
- drawStraws ( );
- return false;
- }
- else
- return false;
-}
-
-
-
-
-void
-QrshServer::rememberIntroduction ( Message const & message )
-{
- char brokerName [ 1000 ];
- sscanf ( message.getData().c_str(), "%*s%s", brokerName );
-
- if ( strcmp ( brokerName, name.c_str() ) )
- myFellowBrokers.insert ( string ( brokerName ) );
-}
-
-
-
-
-void
-QrshServer::addAlias ( Message const & message )
-{
- char alias[1000];
- sscanf ( message.getData().c_str(), "%*s%*s%s", alias );
- aliases.push_back ( new string(alias) );
-}
-
-
-
-
-void
-QrshServer::getNames ( )
-{
- abstract_name_map.clear();
-
- DIR * dir = opendir ( data_dir.str().c_str() );
-
- if ( ! dir )
- {
- fprintf ( stderr,
- "QrshServer::getNames error: could not open dir |%s|.\n",
- data_dir.str().c_str()
- );
- return;
- }
-
- struct dirent * file;
- while ( (file = readdir ( dir ) ) )
- {
- if ( '.' != file->d_name[0] )
- {
- stringstream pid_file_name;
- pid_file_name << data_dir.str()
- << '/'
- << file->d_name
- << "/pid";
-
- int pid = 0;
- FILE * fp;
- if ( (fp = fopen ( pid_file_name.str().c_str(), "r" ) ) )
- {
- fscanf ( fp, "%d", & pid );
- fclose ( fp );
- abstract_name_map.insert(pair<char*, int>(strdup(file->d_name), pid));
- }
- else
- {
- /*
- * Fail silently. The non-existence of this file
- * is not necessarily an error.
- */
- }
- }
- }
- closedir ( dir );
-}
-
-
-
-void
-QrshServer::string_replaceAll ( string & str,
- string & target,
- string & replacement
- )
-{
- int target_size = target.size();
- int found_pos = 0;
-
- while ( 0 <= (found_pos = str.find ( target ) ) )
- str.replace ( found_pos, target_size, replacement );
-}
-
-
-
-
-bool
-QrshServer::isProcessName ( char const * str )
-{
- getNames();
- map<char *, int>::iterator it;
- for ( it = abstract_name_map.begin(); it != abstract_name_map.end(); ++ it )
- {
- if ( ! strcmp ( str, it->first ) )
- return true;
- }
-
- return false;
-}
-
-
-
-
-
-int
-QrshServer::string_countWords ( char const * s1 )
-{
- int count = 0;
- char const * s2 = s1 + 1;
-
- if ( ! isspace(* s1) )
- {
- ++ count;
- }
-
- for ( ; * s2; ++ s1, ++ s2 )
- {
- // count space-to-word transitions.
- if ( isspace(*s1) && (! isspace(*s2)) )
- ++ count;
- }
-
- return count;
-}
-
-
-
-
-void
-QrshServer::execute ( Message const & message )
-{
- // First, gather all the symbolic names we know.
- getNames();
-
- // Now make a copy of the command, that I can alter.
- string command ( message.getData() );
-
-
- // Replace each occurrence of every abstract name with its pid.
- char pid_str[100];
- map<char *, int>::iterator it;
- for ( it = abstract_name_map.begin(); it != abstract_name_map.end(); ++ it )
- {
- sprintf ( pid_str, "%d", it->second );
- string target ( it->first ),
- replacement ( pid_str );
- string_replaceAll ( command, target, replacement );
- }
-
-
- char const * truncated_command = skipWord(skipWord(command.c_str()));
-
- if ( truncated_command )
- system ( truncated_command );
-}
-
-
-
-
-
-void
-QrshServer::get ( Message const & request_message )
-{
- char * file_content;
-
- /*
- * Get the contents of the requested file.
- */
- char file_or_process_name[1000];
- sscanf ( request_message.getData().c_str(), "%*s%*s%s", file_or_process_name );
-
- if ( isProcessName ( file_or_process_name ) )
- {
- stringstream desired_file_name;
- desired_file_name << data_dir.str()
- << '/'
- << file_or_process_name
- << '/';
- char requested_output_stream[1000];
- if(1 != sscanf ( request_message.getData().c_str(),
- "%*s%*s%*s%s",
- requested_output_stream
- )
- )
- {
- fprintf ( stderr,
- "QrshServer::get error: Can't read requested data file name from this message: |%s|\n",
- request_message.getData().c_str()
- );
- return;
- }
- desired_file_name << requested_output_stream;
- file_content = file2str ( desired_file_name.str().c_str() );
- }
- else
- {
- file_content = file2str ( file_or_process_name );
- }
-
- stringstream reply_data ;
- reply_data << "get_response "
- << file_content;
- /*
- * Send a response-message to the server who is waiting.
- */
- send ( reply_data.str() );
-}
-
-
-
-
-
-
-void
-QrshServer::exited ( Message const & message )
-{
- int exit_code = -1;
-
- // First, gather all the symbolic names we know.
- getNames();
-
- // Now make a copy of the command, that I can alter.
- string edited_command ( message.getData() );
-
- // Replace each occurrence of every abstract name with its pid.
- char pid_str[100];
- map<char *, int>::iterator it;
- for ( it = abstract_name_map.begin(); it != abstract_name_map.end(); ++ it )
- {
- sprintf ( pid_str, "%d", it->second );
- string target ( it->first ),
- replacement ( pid_str );
- string_replaceAll ( edited_command, target, replacement );
- }
-
- // Skip the service name. That is not used by the child.
- char const * truncated_command = skipWord(edited_command.c_str());
-
- if ( truncated_command )
- {
- stringstream ss;
- ss << qrsh_run_path
- << ' '
- << data_dir.str()
- << ' '
- << truncated_command;
-
- int child_pid;
- if ( ! (child_pid = fork() ) )
- {
- // This is the child.
-
- char ** argv = getArgs ( ss.str().c_str() );
- execv ( qrsh_run_path.c_str(), argv );
-
- perror ( "qrsh_server: execv error: " );
- exit ( 1 );
- }
- else
- {
- // This is the parent.
- pid_t awaited_pid;
- while ( 0 == (awaited_pid = waitpid ( child_pid, & exit_code, WNOHANG)) )
- {
- fprintf ( stderr, "qrsh_server info: parent: waiting for child...\n" );
- sleep(1);
- }
-
- if ( -1 == awaited_pid )
- {
- fprintf ( stderr, "qrsh_server error awaiting child!\n" );
- exit ( 1 );
- }
-
- exit_code >>= 8;
-
- stringstream data;
- data << "wait_response "
- << exit_code;
-
- send ( data.str() );
- }
- }
-}
-
-
-
-
-void
-QrshServer::wait ( Message const & message )
-{
- bool pre_existing = false;
- if ( 3 == string_countWords ( message.getData().c_str() ) )
- {
- // The first word is the name of this service.
- // The second word is "exec_wait".
- // The third word is the symbolic name of the command to wait for.
- // The fact that there are exactly three words means that this
- // must be a command that has already been named and started --
- // we just need to find its pid and wait on it.
- pre_existing = true;
- }
-
-
- int exit_code = -1;
-
- // First, gather all the symbolic names we know.
- getNames();
-
- // Now make a copy of the command, that I can alter.
- string edited_command ( message.getData() );
-
- // Replace each occurrence of every abstract name with its pid.
- char pid_str[100];
- map<char *, int>::iterator it;
- for ( it = abstract_name_map.begin(); it != abstract_name_map.end(); ++ it )
- {
- sprintf ( pid_str, "%d", it->second );
- string target ( it->first ),
- replacement ( pid_str );
- string_replaceAll ( edited_command, target, replacement );
- }
-
- // Skip the service name. That is not used by the child.
- char const * truncated_command = skipWord(edited_command.c_str());
-
- if ( truncated_command )
- {
- stringstream ss;
- ss << qrsh_run_path
- << ' '
- << data_dir.str()
- << ' '
- << truncated_command;
-
- int child_pid;
- if ( ! (child_pid = fork() ) )
- {
- // This is the child.
-
- char ** argv = getArgs ( ss.str().c_str() );
- execv ( qrsh_run_path.c_str(), argv );
-
- perror ( "qrsh_server: execv error: " );
- exit ( 1 );
- }
- else
- {
- // This is the parent.
- pid_t awaited_pid;
- while ( 0 == (awaited_pid = waitpid ( child_pid, & exit_code, WNOHANG)) )
- {
- fprintf ( stderr, "qrsh_server info: parent: waiting for child...\n" );
- sleep(1);
- }
-
- if ( -1 == awaited_pid )
- {
- fprintf ( stderr, "qrsh_server error awaiting child!\n" );
- exit ( 1 );
- }
- }
-
- exit_code >>= 8;
-
- stringstream data;
- data << "wait_response "
- << exit_code;
-
- send ( data.str() );
- }
-}
-
-
-
-
-
-char const *
-QrshServer::skipWord ( char const * s )
-{
- if(! (s && *s) )
- return 0;
-
- // skip past initial white space
- while ( isspace(*s) )
- {
- ++ s;
- if(! *s)
- return 0;
- }
-
- // skip past first word
- while ( ! isspace(*s) )
- {
- ++ s;
- if(! *s)
- return 0;
- }
-
- return s;
-}
-
-
-
-
-
-char **
-QrshServer::getArgs ( char const * str )
-{
- char const * s = str;
-
- char ** argv = 0;
- vector<int> start_positions,
- lengths;
-
- int pos = 0;
- int arg_len = 0;
-
- int n_args = 0;
- while ( 1 )
- {
- // advance over whitespace.
- while ( isspace ( *s ) )
- {
- ++ s; ++ pos;
- if(! *s)
- {
- goto done;
- }
- }
-
- ++ n_args;
- start_positions.push_back ( pos );
- arg_len = 0;
-
- // advance over non-whitespace.
- while ( ! isspace ( *s ) )
- {
- ++ s; ++ pos; ++ arg_len;
- if(! *s)
- {
- lengths.push_back ( arg_len );
- arg_len = 0;
- goto done;
- }
- }
-
- lengths.push_back ( arg_len );
- arg_len = 0;
- }
-
- done:
-
- if ( arg_len > 0 )
- lengths.push_back ( arg_len );
-
- // Alloc the array.
- argv = (char **) malloc ( sizeof(char *) * ( n_args + 1 ) );
- argv[n_args] = 0; // mull-term the array.
-
- for ( int i = 0; i < n_args; ++ i )
- {
- argv[i] = ( char *) malloc ( lengths[i] + 1 );
- strncpy ( argv[i],
- str + start_positions[i],
- lengths[i]
- );
- argv[i][lengths[i]] = 0;
- }
-
- return argv;
-}
-
-
-
-void
-QrshServer::runCommand ( Message const & message )
-{
- char const * s = message.getData().c_str();
-
- /*
- * Skip the first word, which is this server's name.
- */
- while ( isspace(*s) ) // go to start of first word.
- ++ s;
-
- while ( ! isspace(*s) ) // go to end of first word.
- ++ s;
-
- while ( isspace(*s) ) // go to start of second word.
- ++ s;
-
- char command_name[1000];
- sscanf ( s, "%s", command_name );
-
- if ( ! strcmp ( "get", command_name ) )
- {
- get ( message );
- }
- else
- if ( ! strcmp ( "exited", command_name ) )
- {
- exited ( message );
- }
- else
- if ( ! strcmp ( "exec_wait", command_name ) )
- {
- wait ( message );
- }
- else
- if ( ! strcmp ( "exec", command_name ) )
- {
- execute ( message );
- }
- else
- if ( ! strcmp ( "start", command_name ) )
- {
- start ( );
- }
- else
- if ( ! strcmp ( "alias", command_name ) )
- {
- addAlias ( message );
- }
- else
- if ( ! strcmp ( "sayName", command_name ) )
- {
- sayName ( );
- }
- else
- {
- /*
- * If the command is not any of the "special" commands
- * above, then it's a "normal" command.
- * That means we run it with a child process instance of
- * qrsh_run, which will save all its data in the qrsh dir.
- */
- stringstream ss;
- ss << qrsh_run_path
- << ' '
- << data_dir.str()
- << ' '
- << s;
-
- if ( ! fork() )
- {
- char ** argv = getArgs ( ss.str().c_str() );
- execv ( qrsh_run_path.c_str(), argv );
- perror ( "qrsh_server: execv error: " );
- }
- }
-}
-
-
-
-void
-QrshServer::received ( Message & message )
-{
- if ( myMessage ( message ) )
- runCommand ( message );
-}
-
-
-
-}} // namespace qpid::tests
-
-using namespace qpid::tests;
-
-/*
- * fixme mick Mon Aug 3 10:29:26 EDT 2009
- * argv[1] server name
- * argv[2] qrsh exe path
- * argv[3] host
- * argv[4] port
- */
-int
-main ( int /*argc*/, char** argv )
-{
- const char* host = argv[3];
- int port = atoi(argv[4]);
- Connection connection;
- Message msg;
-
- srand ( getpid() );
-
- try
- {
- connection.open ( host, port );
- Session session = connection.newSession();
-
-
- // Declare queues.
- string myQueue = session.getId().getName();
- session.queueDeclare ( arg::queue=myQueue,
- arg::exclusive=true,
- arg::autoDelete=true);
-
- session.exchangeBind ( arg::exchange="amq.fanout",
- arg::queue=myQueue,
- arg::bindingKey="my-key");
-
- // Create a server and subscribe it to my queue.
- SubscriptionManager subscriptions ( session );
- QrshServer server ( subscriptions,
- argv[1], // server name
- argv[2], // qrsh exe path
- host,
- port
- );
- subscriptions.subscribe ( server, myQueue );
-
- // Receive messages until the subscription is cancelled
- // by QrshServer::received()
- subscriptions.run();
-
- connection.close();
- }
- catch(const exception& error)
- {
- cout << error.what() << endl;
- return 1;
- }
-
- return 0;
-}
-
-
-
-
diff --git a/cpp/src/tests/qrsh_utils/10_all b/cpp/src/tests/qrsh_utils/10_all
deleted file mode 100755
index 7b486ea672..0000000000
--- a/cpp/src/tests/qrsh_utils/10_all
+++ /dev/null
@@ -1,30 +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.
- *
- */
-
-#! /bin/bash
-
-echo "Asking all servers to say their names... "
-qrsh 127.0.0.1 5813 \
- all sayName
-
-
-
-
diff --git a/cpp/src/tests/qrsh_utils/1_remote_run b/cpp/src/tests/qrsh_utils/1_remote_run
deleted file mode 100755
index 5b9b307bba..0000000000
--- a/cpp/src/tests/qrsh_utils/1_remote_run
+++ /dev/null
@@ -1,26 +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.
- *
- */
-
-#! /bin/bash
-
-
-./qrsh 127.0.0.1 5813 \
- mrg23 command_1 /home/mick/redhat/qrsh/qrsh_run/my_command foo bar baz
diff --git a/cpp/src/tests/qrsh_utils/2_forever b/cpp/src/tests/qrsh_utils/2_forever
deleted file mode 100755
index 5528b0e4d8..0000000000
--- a/cpp/src/tests/qrsh_utils/2_forever
+++ /dev/null
@@ -1,26 +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.
- *
- */
-
-#! /bin/bash
-
-
-./qrsh 127.0.0.1 5813 \
- mrg23 command_2 /home/mick/redhat/qrsh/qrsh_run/forever foo bar baz
diff --git a/cpp/src/tests/qrsh_utils/3_kill_it b/cpp/src/tests/qrsh_utils/3_kill_it
deleted file mode 100755
index afc7a03c9d..0000000000
--- a/cpp/src/tests/qrsh_utils/3_kill_it
+++ /dev/null
@@ -1,27 +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.
- *
- */
-
-#! /bin/bash
-
-echo "Killing command 2... "
-./qrsh 127.0.0.1 5813 \
- mrg23 exec kill -9 command_2
-
diff --git a/cpp/src/tests/qrsh_utils/4_wait_for_it b/cpp/src/tests/qrsh_utils/4_wait_for_it
deleted file mode 100755
index a4dc0da1ce..0000000000
--- a/cpp/src/tests/qrsh_utils/4_wait_for_it
+++ /dev/null
@@ -1,26 +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.
- *
- */
-
-#! /bin/bash
-
-./qrsh 127.0.0.1 5813 \
- mrg23 exec_wait /home/mick/redhat/qrsh/qrsh_run/my_command foo bar baz
-echo "my_command returned an exit code of $?"
diff --git a/cpp/src/tests/qrsh_utils/5_exited b/cpp/src/tests/qrsh_utils/5_exited
deleted file mode 100755
index 4fec1dcc79..0000000000
--- a/cpp/src/tests/qrsh_utils/5_exited
+++ /dev/null
@@ -1,64 +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.
- *
- */
-
-#! /bin/bash
-
-path=/home/mick/redhat/qrsh/qrsh_run
-
-echo "Running command_3 ..."
-./qrsh 127.0.0.1 5813 \
- mrg23 command_3 $path/my_command foo bar baz
-
-echo "Now I do some other stuff..."
-sleep 1
-echo "And then some more stuff..."
-sleep 1
-echo "and so on..."
-sleep 1
-
-echo "Now I'm waiting for command_3 ..."
-./qrsh 127.0.0.1 5813 \
- mrg23 exited command_3
-echo "has command_3 exited: $? ."
-sleep 5
-
-./qrsh 127.0.0.1 5813 \
- mrg23 exited command_3
-echo "has command_3 exited: $? ."
-sleep 5
-
-./qrsh 127.0.0.1 5813 \
- mrg23 exited command_3
-echo "has command_3 exited: $? ."
-sleep 5
-
-./qrsh 127.0.0.1 5813 \
- mrg23 exited command_3
-echo "has command_3 exited: $? ."
-sleep 5
-
-./qrsh 127.0.0.1 5813 \
- mrg23 exited command_3
-echo "has command_3 exited: $? ."
-sleep 5
-
-
-
diff --git a/cpp/src/tests/qrsh_utils/7_get_output b/cpp/src/tests/qrsh_utils/7_get_output
deleted file mode 100755
index 59911089ec..0000000000
--- a/cpp/src/tests/qrsh_utils/7_get_output
+++ /dev/null
@@ -1,44 +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.
- *
- */
-
-#! /bin/bash
-
-echo "Run a command..."
-./qrsh 127.0.0.1 5813 \
- mrg23 command_4 /home/mick/redhat/qrsh/qrsh_run/my_command foo bar baz
-
-echo "Wait for a while..."
-sleep 20
-
-echo "Get stderr output:"
-echo "------------- begin stderr ---------------"
-./qrsh 127.0.0.1 5813 \
- mrg23 get command_4 stderr
-echo "------------- end stderr ---------------"
-echo " "
-echo " "
-echo " "
-echo "Get stdout output:"
-echo "------------- begin stdout ---------------"
-./qrsh 127.0.0.1 5813 \
- mrg23 get command_4 stdout
-echo "------------- end stdout ---------------"
-
diff --git a/cpp/src/tests/qrsh_utils/8_any b/cpp/src/tests/qrsh_utils/8_any
deleted file mode 100755
index 2a922ea0e0..0000000000
--- a/cpp/src/tests/qrsh_utils/8_any
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-#! /bin/bash
-
-echo "asking any server to say his name ..."
-./qrsh 127.0.0.1 5813 \
- any sayName
-sleep 1
-echo "asking any server to say his name ..."
-./qrsh 127.0.0.1 5813 \
- any sayName
-sleep 1
-echo "asking any server to say his name ..."
-./qrsh 127.0.0.1 5813 \
- any sayName
-sleep 1
-echo "asking any server to say his name ..."
-./qrsh 127.0.0.1 5813 \
- any sayName
-sleep 1
-
-
-
-
diff --git a/cpp/src/tests/qrsh_utils/9_alias b/cpp/src/tests/qrsh_utils/9_alias
deleted file mode 100755
index a4cfdfdf9a..0000000000
--- a/cpp/src/tests/qrsh_utils/9_alias
+++ /dev/null
@@ -1,38 +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.
- *
- */
-
-#! /bin/bash
-
-# Make a group of two of the servers, using "alias",
-# and send the group a command.
-
-qrsh 127.0.0.1 5813 \
- mrg22 alias group_1
-qrsh 127.0.0.1 5813 \
- mrg23 alias group_1
-
-echo "Asking group_1 to say their names... "
-qrsh 127.0.0.1 5813 \
- group_1 sayName
-
-
-
-
diff --git a/cpp/src/tests/qrsh_utils/qrsh_forever.cpp b/cpp/src/tests/qrsh_utils/qrsh_forever.cpp
deleted file mode 100644
index 191a9bca11..0000000000
--- a/cpp/src/tests/qrsh_utils/qrsh_forever.cpp
+++ /dev/null
@@ -1,50 +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 <stdio.h>
-#include <unistd.h>
-
-
-
-main ( int argc, char ** argv )
-{
- fprintf ( stderr, "Hello, I am the Forever Example Child!\n");
- fprintf ( stderr, "my %d arguments are:\n", argc - 1 );
-
- int i;
- for ( i = 1; i < argc; ++ i )
- fprintf ( stderr, "arg %d: |%s|\n", i, argv[i] );
-
- for ( i = 0; i >= 0; ++ i )
- {
- fprintf ( stderr, "child sleeping forever %d ...\n" , i);
- sleep ( 1 );
- }
-
- fprintf ( stderr, "child exiting with code 12.\n" );
-
- return 12;
-}
-
-
-
-
diff --git a/cpp/src/tests/qrsh_utils/qsh_doc.txt b/cpp/src/tests/qrsh_utils/qsh_doc.txt
deleted file mode 100644
index ad5990b38b..0000000000
--- a/cpp/src/tests/qrsh_utils/qsh_doc.txt
+++ /dev/null
@@ -1,309 +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.
- *
- */
-
-##############################################
- qrsh: a Qpid-based remote shell utility
-
- Last updated: 3 Aug 09 Mick Goulish
-##############################################
-
-
-
-=============================
-Overview
-=============================
-
- You're writing a multi-box test, and you want to write a
- shell script in which you start processes on other boxes
- and kill them (or send arbitrary signals to them).
-
- But ssh doesn't let you signal them, and bash isn't the
- greatest language in the world for creating data structures
- (like you need to associate the PIDs with box names and
- executable names.)
-
- Qsh is a utility implemented on Qpid that you can use from
- within your bash script, or any other scripting language.
- With it, you can:
-
- 1. run any executable on any box in your cluster.
-
- 2. don't worry about PIDs and box-names. You associate
- your own abstract names with the executable instances,
- and then use those names in the rest of your script.
- I.e. "broker_1" "sender_3" etc.
-
- 3. Launch the executable and wait until it returns, and
- get its exit code.
-
- 4. Launch your executable and do other stuff, then come
- back later and see if it has exited.
-
- 5. Get whatever it sent to stdout or stderr.
-
- 6. Get the contents of any other file.
-
- 7. send a command to all your boxes at once
-
- 8. send a command to a randomly selected box.
-
- 9. define groups of boxes, and send a command simultaneously
- to all boxes in a given group.
-
-
-
-
-=============================
-Using It
-=============================
-
- 1. You need to run a Qpid broker.
-
- 2. You start a Qpid client ( which is called a qrsh_server )
- on all the boxes you care about. And you give them all
- names like "mrg13", "mrg14" etc. The names can be anything
- you want, but I've always used one qrsh_server per box,
- and given it the box name. ( However, you can run two on
- one box, they won't collide. )
-
- 3. After you start all servers, send a "start" command to any
- one of them:
-
- 4. The qrsh_servers use the fanout exchange to talk to each
- other.
-
- 5. In your script, you run an executable called "qrsh". It knows
- how to talk to the servers, do what you want, and retrieve
- the data you want.
-
-
- example start script: (this does 4 servers on the same box)
- -------------------------------------------------------------
-
- echo "Starting server mrg22 ..."
- ./qrsh_server mrg22 ./qrsh_run 127.0.0.1 5813 &
-
- echo "Starting server mrg23 ..."
- ./qrsh_server mrg23 ./qrsh_run 127.0.0.1 5813 &
-
- echo "Starting server mrg24 ..."
- ./qrsh_server mrg24 ./qrsh_run 127.0.0.1 5813 &
-
- echo "Starting server mrg25 ..."
- ./qrsh_server mrg25 ./qrsh_run 127.0.0.1 5813 &
-
- echo "Issuing start command..."
- sleep 2
- ./qrsh 127.0.0.1 5813 mrg22 start
- sleep 1
-
- echo "Ready."
-
- # end of script.
-
-
-
-
-
-
-=============================
-Qrsh Syntax
-=============================
-
- qrsh host port server_name command_name arg*
-
-
- "host" and "port" specify the Qpid server to connect to.
-
- "server_name" can be anything you want. I always use the name
- of the box that the server is running on.
-
- "command_name" is the name that you choose to assign to
- the process you are running. Each process that you decide
- to name must have a unique name within this script.
-
- Or it could be a reserved command name, that Qsh
- interprets in a special way.
-
- Reserved command names are:
-
- exec
- exec_wait
- exited
- get
-
- "exec" means "interpret the rest of the command line as a
- command to be executed by the designated server.
-
- "exec_wait" means same as "exec", but wait for the command
- to terminate, and return its exit code.
-
- "exited" -- you provide 1 arg, which is an abstract
- process name. qrsh returns 1 if that process has exited,
- else 0.
-
- "get" -- you provide one arg which is a path. qrsh returns
- (by printing to stdout) the contents of that file.
-
- "arg*" is zero or more arguments. They are interpreted
- differently depending on whether you are using one of
- the above reserved command names, or making up your own
- abstract name for a command.
-
-
-
-
-=============================
-Examples
-=============================
-
- 1. Run a process on a remote box.
-
- qrsh mrg23 command_1 /usr/sbin/whatever foo bar baz
-
- Returns immediately.
-
-
-
- 2. Kill a process that you started earlier:
-
- qrsh mrg23 exec kill -9 command_1
-
- After the word "exec" put any command line you want.
- The server you're sending this to will replace all abstract
- names in the command with process IDs. ( In this example,
- just the word "command_1" will be replaced. ) Then it will
- execute the command.
-
-
-
- 3. Execute a command, and wait for it to finish
-
- qrsh mrg23 exec_wait command_name args
-
-
-
- 4. Check on whether a command you issude earlier has exited.
-
- ./qrsh mrg23 exited command_3
-
- Returns 1 if it has exited, else 0.
-
-
-
- 5. Get the contents of a file from the remote system:
-
- ./qrsh mrg23 get /tmp/foo
-
- Prints the contents to stdout.
-
-
-
- 6. Send a command to all servers at once:
-
- # This example causes them all to print thir names to stderr.
- ./qrsh all sayName
-
-
- 7. Define a group of servers and send a command to that group.
-
- #! /bin/bash
-
- # Make a group of two of the servers, using "alias",
- # and send the group a command.
-
- qrsh 127.0.0.1 5813 \
- mrg22 alias group_1
-
- qrsh 127.0.0.1 5813 \
- mrg23 alias group_1
-
- echo "Asking group_1 to say their names... "
- qrsh 127.0.0.1 5813 \
- group_1 sayName
-
- # end of script.
-
-
-
-
- 8. Execute a command and get its stdout and stderr contents.
-
- #! /bin/bash
-
- echo "Run a command..."
- ./qrsh 127.0.0.1 5813 \
- mrg23 command_4 my_command foo bar baz
-
- echo "Wait for a while..."
- sleep 10
-
- echo "Get stderr output:"
- echo "------------- begin stderr ---------------"
- ./qrsh 127.0.0.1 5813 \
- mrg23 get command_4 stderr
- echo "------------- end stderr ---------------"
- echo " "
-
- echo " "
- echo "Get stdout output:"
- echo "------------- begin stdout ---------------"
- ./qrsh 127.0.0.1 5813 \
- mrg23 get command_4 stdout
- echo "------------- end stdout ---------------"
-
- # end of script.
-
-
-
-
- 9. Send a command to one of your servers, selected
- at random.
-
- #! /bin/bash
-
- # I do it multiple times here, so I can see
- # that it really is selecting randomly.
-
- echo "asking any server to say his name ..."
- ./qrsh 127.0.0.1 5813 \
- any sayName
- sleep 1
-
- echo "asking any server to say his name ..."
- ./qrsh 127.0.0.1 5813 \
- any sayName
- sleep 1
-
- echo "asking any server to say his name ..."
- ./qrsh 127.0.0.1 5813 \
- any sayName
- sleep 1
-
- echo "asking any server to say his name ..."
- ./qrsh 127.0.0.1 5813 \
- any sayName
-
- # end of script.
-
-
-
-
diff --git a/cpp/src/tests/queue_flow_limit_tests.py b/cpp/src/tests/queue_flow_limit_tests.py
new file mode 100644
index 0000000000..dec7cfb3af
--- /dev/null
+++ b/cpp/src/tests/queue_flow_limit_tests.py
@@ -0,0 +1,371 @@
+#!/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 sys
+from qpid.testlib import TestBase010
+from qpid import datatypes, messaging
+from qpid.messaging import Message, Empty
+from threading import Thread, Lock
+from logging import getLogger
+from time import sleep, time
+from os import environ, popen
+
+class QueueFlowLimitTests(TestBase010):
+
+ def __getattr__(self, name):
+ if name == "assertGreater":
+ return lambda a, b: self.failUnless(a > b)
+ else:
+ raise AttributeError
+
+ def _create_queue(self, name,
+ stop_count=None, resume_count=None,
+ stop_size=None, resume_size=None,
+ max_size=None, max_count=None):
+ """ Create a queue with the given flow settings via the queue.declare
+ command.
+ """
+ args={}
+ if (stop_count is not None):
+ args["qpid.flow_stop_count"] = stop_count;
+ if (resume_count is not None):
+ args["qpid.flow_resume_count"] = resume_count;
+ if (stop_size is not None):
+ args["qpid.flow_stop_size"] = stop_size;
+ if (resume_size is not None):
+ args["qpid.flow_resume_size"] = resume_size;
+ if (max_size is not None):
+ args["qpid.max_size"] = max_size;
+ if (max_count is not None):
+ args["qpid.max_count"] = max_count;
+
+
+ self.session.queue_declare(queue=name, arguments=args)
+
+ qs = self.qmf.getObjects(_class="queue")
+ for i in qs:
+ if i.name == name:
+ # verify flow settings
+ if (stop_count is not None):
+ self.assertEqual(i.arguments.get("qpid.flow_stop_count"), stop_count)
+ if (resume_count is not None):
+ self.assertEqual(i.arguments.get("qpid.flow_resume_count"), resume_count)
+ if (stop_size is not None):
+ self.assertEqual(i.arguments.get("qpid.flow_stop_size"), stop_size)
+ if (resume_size is not None):
+ self.assertEqual(i.arguments.get("qpid.flow_resume_size"), resume_size)
+ if (max_size is not None):
+ self.assertEqual(i.arguments.get("qpid.max_size"), max_size)
+ if (max_count is not None):
+ self.assertEqual(i.arguments.get("qpid.max_count"), max_count)
+ self.failIf(i.flowStopped)
+ return i.getObjectId()
+ self.fail("Unable to create queue '%s'" % name)
+ return None
+
+
+ def _delete_queue(self, name):
+ """ Delete a named queue
+ """
+ self.session.queue_delete(queue=name)
+
+
+ def _start_qpid_send(self, queue, count, content="X", capacity=100):
+ """ Use the qpid-send client to generate traffic to a queue.
+ """
+ command = "qpid-send" + \
+ " -b" + " %s:%s" % (self.broker.host, self.broker.port) \
+ + " -a " + str(queue) \
+ + " --messages " + str(count) \
+ + " --content-string " + str(content) \
+ + " --capacity " + str(capacity)
+ return popen(command)
+
+ def _start_qpid_receive(self, queue, count, timeout=5):
+ """ Use the qpid-receive client to consume from a queue.
+ Note well: prints one line of text to stdout for each consumed msg.
+ """
+ command = "qpid-receive" + \
+ " -b " + "%s:%s" % (self.broker.host, self.broker.port) \
+ + " -a " + str(queue) \
+ + " --messages " + str(count) \
+ + " --timeout " + str(timeout) \
+ + " --print-content yes"
+ return popen(command)
+
+ def test_qpid_config_cmd(self):
+ """ Test the qpid-config command's ability to configure a queue's flow
+ control thresholds.
+ """
+ tool = environ.get("QPID_CONFIG_EXEC")
+ if tool:
+ command = tool + \
+ " --broker-addr=%s:%s " % (self.broker.host, self.broker.port) \
+ + "add queue test01 --flow-stop-count=999" \
+ + " --flow-resume-count=55 --flow-stop-size=5000000" \
+ + " --flow-resume-size=100000"
+ cmd = popen(command)
+ rc = cmd.close()
+ self.assertEqual(rc, None)
+
+ # now verify the settings
+ self.startQmf();
+ qs = self.qmf.getObjects(_class="queue")
+ for i in qs:
+ if i.name == "test01":
+ self.assertEqual(i.arguments.get("qpid.flow_stop_count"), 999)
+ self.assertEqual(i.arguments.get("qpid.flow_resume_count"), 55)
+ self.assertEqual(i.arguments.get("qpid.flow_stop_size"), 5000000)
+ self.assertEqual(i.arguments.get("qpid.flow_resume_size"), 100000)
+ self.failIf(i.flowStopped)
+ break;
+ self.assertEqual(i.name, "test01")
+ self._delete_queue("test01")
+
+
+ def test_flow_count(self):
+ """ Create a queue with count-based flow limit. Spawn several
+ producers which will exceed the limit. Verify limit exceeded. Consume
+ all messages. Verify flow control released.
+ """
+ self.startQmf();
+ oid = self._create_queue("test-q", stop_count=373, resume_count=229)
+ self.assertEqual(self.qmf.getObjects(_objectId=oid)[0].flowStoppedCount, 0)
+
+ sndr1 = self._start_qpid_send("test-q", count=1213, content="XXX", capacity=50);
+ sndr2 = self._start_qpid_send("test-q", count=797, content="Y", capacity=13);
+ sndr3 = self._start_qpid_send("test-q", count=331, content="ZZZZZ", capacity=149);
+ totalMsgs = 1213 + 797 + 331
+
+ # wait until flow control is active
+ deadline = time() + 10
+ while (not self.qmf.getObjects(_objectId=oid)[0].flowStopped) and \
+ time() < deadline:
+ pass
+ self.failUnless(self.qmf.getObjects(_objectId=oid)[0].flowStopped)
+ depth = self.qmf.getObjects(_objectId=oid)[0].msgDepth
+ self.assertGreater(depth, 373)
+
+ # now wait until the enqueues stop happening - ensure that
+ # not all msgs have been sent (senders are blocked)
+ sleep(1)
+ newDepth = self.qmf.getObjects(_objectId=oid)[0].msgDepth
+ while depth != newDepth:
+ depth = newDepth;
+ sleep(1)
+ newDepth = self.qmf.getObjects(_objectId=oid)[0].msgDepth
+ self.assertGreater(totalMsgs, depth)
+
+ # drain the queue
+ rcvr = self._start_qpid_receive("test-q",
+ count=totalMsgs)
+ count = 0;
+ x = rcvr.readline() # prints a line for each received msg
+ while x:
+ count += 1;
+ x = rcvr.readline()
+
+ sndr1.close();
+ sndr2.close();
+ sndr3.close();
+ rcvr.close();
+
+ self.assertEqual(count, totalMsgs)
+ self.failIf(self.qmf.getObjects(_objectId=oid)[0].flowStopped)
+ self.failUnless(self.qmf.getObjects(_objectId=oid)[0].flowStoppedCount)
+
+ self._delete_queue("test-q")
+
+
+ def test_flow_size(self):
+ """ Create a queue with size-based flow limit. Spawn several
+ producers which will exceed the limit. Verify limit exceeded. Consume
+ all messages. Verify flow control released.
+ """
+ self.startQmf();
+ oid = self._create_queue("test-q", stop_size=351133, resume_size=251143)
+
+ sndr1 = self._start_qpid_send("test-q", count=1699, content="X"*439, capacity=53);
+ sndr2 = self._start_qpid_send("test-q", count=1129, content="Y"*631, capacity=13);
+ sndr3 = self._start_qpid_send("test-q", count=881, content="Z"*823, capacity=149);
+ totalMsgs = 1699 + 1129 + 881
+
+ # wait until flow control is active
+ deadline = time() + 10
+ while (not self.qmf.getObjects(_objectId=oid)[0].flowStopped) and \
+ time() < deadline:
+ pass
+ self.failUnless(self.qmf.getObjects(_objectId=oid)[0].flowStopped)
+ self.assertGreater(self.qmf.getObjects(_objectId=oid)[0].byteDepth, 351133)
+
+ # now wait until the enqueues stop happening - ensure that
+ # not all msgs have been sent (senders are blocked)
+ depth = self.qmf.getObjects(_objectId=oid)[0].msgDepth
+ sleep(1)
+ newDepth = self.qmf.getObjects(_objectId=oid)[0].msgDepth
+ while depth != newDepth:
+ depth = newDepth;
+ sleep(1)
+ newDepth = self.qmf.getObjects(_objectId=oid)[0].msgDepth
+ self.assertGreater(totalMsgs, depth)
+
+ # drain the queue
+ rcvr = self._start_qpid_receive("test-q",
+ count=totalMsgs)
+ count = 0;
+ x = rcvr.readline() # prints a line for each received msg
+ while x:
+ count += 1;
+ x = rcvr.readline()
+
+ sndr1.close();
+ sndr2.close();
+ sndr3.close();
+ rcvr.close();
+
+ self.assertEqual(count, totalMsgs)
+ self.failIf(self.qmf.getObjects(_objectId=oid)[0].flowStopped)
+
+ self._delete_queue("test-q")
+
+
+ def verify_limit(self, testq):
+ """ run a limit check against the testq object
+ """
+
+ testq.mgmt = self.qmf.getObjects(_objectId=testq.oid)[0]
+
+ # fill up the queue, waiting until flow control is active
+ sndr1 = self._start_qpid_send(testq.mgmt.name, count=testq.sendCount, content=testq.content)
+ deadline = time() + 10
+ while (not testq.mgmt.flowStopped) and time() < deadline:
+ testq.mgmt.update()
+
+ self.failUnless(testq.verifyStopped())
+
+ # now consume enough messages to drop below the flow resume point, and
+ # verify flow control is released.
+ rcvr = self._start_qpid_receive(testq.mgmt.name, count=testq.consumeCount)
+ rcvr.readlines() # prints a line for each received msg
+ rcvr.close();
+
+ # we should now be below the resume threshold
+ self.failUnless(testq.verifyResumed())
+
+ self._delete_queue(testq.mgmt.name)
+ sndr1.close();
+
+
+ def test_default_flow_count(self):
+ """ Create a queue with count-based size limit, and verify the computed
+ thresholds using the broker's default ratios.
+ """
+ class TestQ:
+ def __init__(self, oid):
+ # Use the broker-wide default flow thresholds of 80%/70% (see
+ # run_queue_flow_limit_tests) to base the thresholds off the
+ # queue's max_count configuration parameter
+ # max_count == 1000 -> stop == 800, resume == 700
+ self.oid = oid
+ self.sendCount = 1000
+ self.consumeCount = 301 # (send - resume) + 1 to reenable flow
+ self.content = "X"
+ def verifyStopped(self):
+ self.mgmt.update()
+ return self.mgmt.flowStopped and (self.mgmt.msgDepth > 800)
+ def verifyResumed(self):
+ self.mgmt.update()
+ return (not self.mgmt.flowStopped) and (self.mgmt.msgDepth < 700)
+
+ self.startQmf();
+ oid = self._create_queue("test-X", max_count=1000)
+ self.verify_limit(TestQ(oid))
+
+
+ def test_default_flow_size(self):
+ """ Create a queue with byte-based size limit, and verify the computed
+ thresholds using the broker's default ratios.
+ """
+ class TestQ:
+ def __init__(self, oid):
+ # Use the broker-wide default flow thresholds of 80%/70% (see
+ # run_queue_flow_limit_tests) to base the thresholds off the
+ # queue's max_count configuration parameter
+ # max_size == 10000 -> stop == 8000 bytes, resume == 7000 bytes
+ self.oid = oid
+ self.sendCount = 2000
+ self.consumeCount = 601 # (send - resume) + 1 to reenable flow
+ self.content = "XXXXX" # 5 bytes per message sent.
+ def verifyStopped(self):
+ self.mgmt.update()
+ return self.mgmt.flowStopped and (self.mgmt.byteDepth > 8000)
+ def verifyResumed(self):
+ self.mgmt.update()
+ return (not self.mgmt.flowStopped) and (self.mgmt.byteDepth < 7000)
+
+ self.startQmf();
+ oid = self._create_queue("test-Y", max_size=10000)
+ self.verify_limit(TestQ(oid))
+
+
+ def test_blocked_queue_delete(self):
+ """ Verify that blocked senders are unblocked when a queue that is flow
+ controlled is deleted.
+ """
+
+ class BlockedSender(Thread):
+ def __init__(self, tester, queue, count, capacity=10):
+ self.tester = tester
+ self.queue = queue
+ self.count = count
+ self.capacity = capacity
+ Thread.__init__(self)
+ self.done = False
+ self.start()
+ def run(self):
+ # spawn qpid-send
+ p = self.tester._start_qpid_send(self.queue,
+ self.count,
+ self.capacity)
+ p.close() # waits for qpid-send to complete
+ self.done = True
+
+ self.startQmf();
+ oid = self._create_queue("kill-q", stop_size=10, resume_size=2)
+ q = self.qmf.getObjects(_objectId=oid)[0]
+ self.failIf(q.flowStopped)
+
+ sender = BlockedSender(self, "kill-q", count=100)
+ # wait for flow control
+ deadline = time() + 10
+ while (not q.flowStopped) and time() < deadline:
+ q.update()
+
+ self.failUnless(q.flowStopped)
+ self.failIf(sender.done) # sender blocked
+
+ self._delete_queue("kill-q")
+ sender.join(5)
+ self.failIf(sender.isAlive())
+ self.failUnless(sender.done)
+
+
+
+
diff --git a/cpp/src/tests/replication_test b/cpp/src/tests/replication_test
index 691fd20b0c..8c37568875 100755
--- a/cpp/src/tests/replication_test
+++ b/cpp/src/tests/replication_test
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
#
# Licensed to the Apache Software Foundation (ASF) under one
diff --git a/cpp/src/tests/run_acl_tests b/cpp/src/tests/run_acl_tests
index aff13408ed..41f41e20e1 100755
--- a/cpp/src/tests/run_acl_tests
+++ b/cpp/src/tests/run_acl_tests
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
#
# Licensed to the Apache Software Foundation (ASF) under one
diff --git a/cpp/src/tests/run_cli_tests b/cpp/src/tests/run_cli_tests
index 3f1388b9f5..ec5c71b646 100755
--- a/cpp/src/tests/run_cli_tests
+++ b/cpp/src/tests/run_cli_tests
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
#
# Licensed to the Apache Software Foundation (ASF) under one
@@ -70,7 +70,8 @@ stop_brokers() {
if test -d ${PYTHON_DIR} ; then
start_brokers
echo "Running CLI tests using brokers on ports $LOCAL_PORT $REMOTE_PORT"
- $QPID_PYTHON_TEST -m cli_tests -b localhost:$LOCAL_PORT -Dremote-port=$REMOTE_PORT -Dcli-dir=$CLI_DIR $targs $@
+ PYTHON_TESTS=${PYTHON_TESTS:-$*}
+ $QPID_PYTHON_TEST -m cli_tests -b localhost:$LOCAL_PORT -Dremote-port=$REMOTE_PORT -Dcli-dir=$CLI_DIR $targs $PYTHON_TESTS $@
RETCODE=$?
stop_brokers
if test x$RETCODE != x0; then
diff --git a/cpp/src/tests/run_federation_sys_tests b/cpp/src/tests/run_federation_sys_tests
new file mode 100755
index 0000000000..f5f772d72e
--- /dev/null
+++ b/cpp/src/tests/run_federation_sys_tests
@@ -0,0 +1,97 @@
+#!/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.
+#
+
+# Run the federation system tests.
+
+source ./test_env.sh
+
+MODULENAME=federation_sys
+
+# Test for clustering
+ps -u root | grep 'aisexec\|corosync' > /dev/null
+if (( $? == 0 )); then
+ CLUSTERING_ENABLED=1
+else
+ echo "WARNING: No clustering detected; tests using it will be ignored."
+fi
+
+# Test for long test
+if [[ "$1" == "LONG_TEST" ]]; then
+ USE_LONG_TEST=1
+ shift # get rid of this param so it is not treated as a test name
+fi
+
+trap stop_brokers INT TERM QUIT
+
+SKIPTESTS="-i federation_sys.E_* -i federation_sys.F_* -i federation_sys.G_* -i federation_sys.H_*"
+if [ -z ${USE_LONG_TEST} ]; then
+ SKIPTESTS="-i federation_sys.A_Long* -i federation_sys.B_Long* ${SKIPTESTS}"
+fi
+echo "WARNING: Tests using persistence will be ignored."
+if [ -z ${CLUSTERING_ENABLED} ]; then
+ SKIPTESTS="${SKIPTESTS} -i federation_sys.C_* -i federation_sys.D_*"
+elif [ -z ${USE_LONG_TEST} ]; then
+ SKIPTESTS="${SKIPTESTS} -i federation_sys.C_Long* -i federation_sys.D_Long*"
+fi
+
+start_brokers() {
+ start_broker() {
+ ${QPIDD_EXEC} --daemon --port 0 --auth no --no-data-dir $1 > qpidd.port
+ PORT=`cat qpidd.port`
+ eval "$2=${PORT}"
+ }
+ start_broker "" LOCAL_PORT
+ start_broker "" REMOTE_PORT
+ if [ -n "${CLUSTERING_ENABLED}" ]; then
+ start_broker "--load-module ${CLUSTER_LIB} --cluster-name test-cluster-1" CLUSTER_C1_1
+ start_broker "--load-module ${CLUSTER_LIB} --cluster-name test-cluster-1" CLUSTER_C1_2
+ start_broker "--load-module ${CLUSTER_LIB} --cluster-name test-cluster-2" CLUSTER_C2_1
+ start_broker "--load-module ${CLUSTER_LIB} --cluster-name test-cluster-2" CLUSTER_C2_2
+ fi
+ rm qpidd.port
+}
+
+stop_brokers() {
+ ${QPIDD_EXEC} -q --port ${LOCAL_PORT}
+ ${QPIDD_EXEC} -q --port ${REMOTE_PORT}
+ if [ -n "${CLUSTERING_ENABLED}" ]; then
+ ${QPID_CLUSTER_EXEC} --all-stop --force localhost:${CLUSTER_C1_1}
+ ${QPID_CLUSTER_EXEC} --all-stop --force localhost:${CLUSTER_C2_1}
+ fi
+}
+
+if test -d ${PYTHON_DIR} ; then
+ start_brokers
+ if [ -z ${CLUSTERING_ENABLED} ]; then
+ echo "Running federation tests using brokers on local port ${LOCAL_PORT}, remote port ${REMOTE_PORT} (NOTE: clustering is DISABLED)"
+ else
+ echo "Running federation tests using brokers on local port ${LOCAL_PORT}, remote port ${REMOTE_PORT}, local cluster nodes ${CLUSTER_C1_1} ${CLUSTER_C1_2}, remote cluster nodes ${CLUSTER_C2_1} ${CLUSTER_C2_2}"
+ fi
+ if [ -z ${USE_LONG_TEST} ]; then
+ echo "NOTE: To run a full set of federation system tests, use \"make check-long\". To test with persistence, run the store version of this script."
+ fi
+ ${QPID_PYTHON_TEST} -m ${MODULENAME} ${SKIPTESTS} -b localhost:${REMOTE_PORT} -Dlocal-port=${LOCAL_PORT} -Dremote-port=${REMOTE_PORT} -Dlocal-cluster-ports="${CLUSTER_C1_1} ${CLUSTER_C1_2}" -Dremote-cluster-ports="${CLUSTER_C2_1} ${CLUSTER_C2_2}" $@
+ RETCODE=$?
+ stop_brokers
+ if test x${RETCODE} != x0; then
+ echo "FAIL federation tests"; exit 1;
+ fi
+fi
diff --git a/cpp/src/tests/run_federation_tests b/cpp/src/tests/run_federation_tests
index 4be27a2e85..14af4807ba 100755
--- a/cpp/src/tests/run_federation_tests
+++ b/cpp/src/tests/run_federation_tests
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
#
# Licensed to the Apache Software Foundation (ASF) under one
@@ -55,7 +55,7 @@ stop_brokers() {
if test -d ${PYTHON_DIR} ; then
start_brokers
echo "Running federation tests using brokers on ports $LOCAL_PORT $REMOTE_PORT $REMOTE_B1 $REMOTE_B2"
- $QPID_PYTHON_TEST -m federation $SKIPTESTS -b localhost:$LOCAL_PORT -Dremote-port=$REMOTE_PORT -Dextra-brokers="$REMOTE_B1 $REMOTE_B2" $@
+ $QPID_PYTHON_TEST -m federation "$SKIPTESTS" -b localhost:$LOCAL_PORT -Dremote-port=$REMOTE_PORT -Dextra-brokers="$REMOTE_B1 $REMOTE_B2" $@
RETCODE=$?
stop_brokers
if test x$RETCODE != x0; then
diff --git a/cpp/src/tests/run_header_test b/cpp/src/tests/run_header_test
index 07658343e7..34008132cc 100755
--- a/cpp/src/tests/run_header_test
+++ b/cpp/src/tests/run_header_test
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
#
# Licensed to the Apache Software Foundation (ASF) under one
diff --git a/cpp/src/tests/run_long_federation_sys_tests b/cpp/src/tests/run_long_federation_sys_tests
new file mode 100644
index 0000000000..69dc08d11c
--- /dev/null
+++ b/cpp/src/tests/run_long_federation_sys_tests
@@ -0,0 +1,24 @@
+#! /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.
+#
+
+# Run the federation system tests (long version).
+
+./run_federation_sys_tests LONG_TEST $@
diff --git a/cpp/src/tests/run_msg_group_tests b/cpp/src/tests/run_msg_group_tests
new file mode 100755
index 0000000000..8423022521
--- /dev/null
+++ b/cpp/src/tests/run_msg_group_tests
@@ -0,0 +1,66 @@
+#!/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.
+#
+#script to run a sequence of message group queue tests via make
+
+#setup path to find qpid-config and msg_group_test progs
+source ./test_env.sh
+
+export PATH=$PWD:$srcdir:$PYTHON_COMMANDS:$PATH
+
+#set port to connect to via env var
+test -s qpidd.port && QPID_PORT=`cat qpidd.port`
+
+#trap cleanup INT TERM QUIT
+
+QUEUE_NAME="group-queue"
+GROUP_KEY="My-Group-Id"
+
+BROKER_URL="${QPID_BROKER:-localhost}:${QPID_PORT:-5672}"
+
+run_test() {
+ $@
+}
+
+##set -x
+
+declare -i i=0
+declare -a tests
+tests=("qpid-config -a $BROKER_URL add queue $QUEUE_NAME --group-header=${GROUP_KEY} --shared-groups"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 103 --group-size 13 --receivers 2 --senders 3 --capacity 3 --ack-frequency 7 --randomize-group-size --interleave 3"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 103 --group-size 13 --receivers 2 --senders 3 --capacity 7 --ack-frequency 7 --randomize-group-size"
+ "qpid-config -a $BROKER_URL add queue ${QUEUE_NAME}-two --group-header=${GROUP_KEY} --shared-groups"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 103 --group-size 13 --receivers 2 --senders 3 --capacity 7 --ack-frequency 3 --randomize-group-size"
+ "msg_group_test -b $BROKER_URL -a ${QUEUE_NAME}-two --group-key $GROUP_KEY --messages 103 --group-size 13 --receivers 2 --senders 3 --capacity 3 --ack-frequency 7 --randomize-group-size --interleave 5"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 59 --group-size 5 --receivers 2 --senders 3 --capacity 1 --ack-frequency 3 --randomize-group-size"
+ "qpid-config -a $BROKER_URL del queue ${QUEUE_NAME}-two --force"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 59 --group-size 3 --receivers 2 --senders 3 --capacity 1 --ack-frequency 1 --randomize-group-size"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 211 --group-size 13 --receivers 2 --senders 3 --capacity 47 --ack-frequency 79 --interleave 53"
+ "qpid-config -a $BROKER_URL del queue $QUEUE_NAME --force")
+
+while [ -n "${tests[i]}" ]; do
+ run_test ${tests[i]}
+ RETCODE=$?
+ if test x$RETCODE != x0; then
+ echo "FAILED message group test. Failed command: \"${tests[i]}\"";
+ exit 1;
+ fi
+ i+=1
+done
diff --git a/cpp/src/tests/run_msg_group_tests_soak b/cpp/src/tests/run_msg_group_tests_soak
new file mode 100755
index 0000000000..5231f74755
--- /dev/null
+++ b/cpp/src/tests/run_msg_group_tests_soak
@@ -0,0 +1,60 @@
+#!/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.
+#
+#script to run a sequence of long-running message group tests via make
+
+#setup path to find qpid-config and msg_group_test test progs
+source ./test_env.sh
+
+export PATH=$PWD:$srcdir:$PYTHON_COMMANDS:$PATH
+
+#set port to connect to via env var
+test -s qpidd.port && QPID_PORT=`cat qpidd.port`
+
+#trap cleanup INT TERM QUIT
+
+QUEUE_NAME="group-queue"
+GROUP_KEY="My-Group-Id"
+
+BROKER_URL="${QPID_BROKER:-localhost}:${QPID_PORT:-5672}"
+
+run_test() {
+ $@
+}
+
+##set -x
+
+declare -i i=0
+declare -a tests
+tests=("qpid-config -a $BROKER_URL add queue $QUEUE_NAME --group-header=${GROUP_KEY} --shared-groups"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 10007 --receivers 3 --senders 5 --group-size 211 --randomize-group-size --capacity 47 --ack-frequency 97"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 10007 --receivers 3 --senders 5 --group-size 211 --randomize-group-size --capacity 79 --ack-frequency 79"
+ "msg_group_test -b $BROKER_URL -a $QUEUE_NAME --group-key $GROUP_KEY --messages 10007 --receivers 3 --senders 5 --group-size 211 --randomize-group-size --capacity 97 --ack-frequency 47"
+ "qpid-config -a $BROKER_URL del queue $QUEUE_NAME --force")
+
+while [ -n "${tests[i]}" ]; do
+ run_test ${tests[i]}
+ RETCODE=$?
+ if test x$RETCODE != x0; then
+ echo "FAILED message group test. Failed command: \"${tests[i]}\"";
+ exit 1;
+ fi
+ i+=1
+done
diff --git a/cpp/src/tests/run_queue_flow_limit_tests b/cpp/src/tests/run_queue_flow_limit_tests
new file mode 100755
index 0000000000..f921cf5e7e
--- /dev/null
+++ b/cpp/src/tests/run_queue_flow_limit_tests
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+# Run tests against Queue producer flow control.
+
+source ./test_env.sh
+test -d $PYTHON_DIR || { echo "Skipping queue flow control tests, no python dir."; exit 0; }
+
+LOG_FILE=queue_flow_limit_test.log
+PORT=""
+
+trap stop_broker INT TERM QUIT
+
+error() {
+ echo $*
+ exit 1;
+}
+
+start_broker() {
+ # Note: if you change the DEFAULT_THRESHOLDS, you will need to update queue_flow_limit_tests.py
+ DEFAULT_THRESHOLDS="--default-flow-stop-threshold=80 --default-flow-resume-threshold=70"
+ rm -rf $LOG_FILE
+ PORT=$($QPIDD_EXEC $DEFAULT_THRESHOLDS --auth=no --no-module-dir --daemon --port=0 -t --log-to-file $LOG_FILE) || error "Could not start broker"
+}
+
+stop_broker() {
+ test -n "$PORT" && $QPIDD_EXEC --no-module-dir --quit --port $PORT
+}
+
+start_broker
+echo "Running Queue flow limit tests using broker on port $PORT"
+$QPID_PYTHON_TEST -m queue_flow_limit_tests $SKIPTESTS -b localhost:$PORT $@
+RETCODE=$?
+stop_broker
+if test x$RETCODE != x0; then
+ echo "FAIL queue flow limit tests"; exit 1;
+fi
+rm -rf $LOG_FILE
+
diff --git a/cpp/src/tests/run_store_tests.ps1 b/cpp/src/tests/run_store_tests.ps1
index 76b46737f0..b2f0b1ccd8 100644
--- a/cpp/src/tests/run_store_tests.ps1
+++ b/cpp/src/tests/run_store_tests.ps1
@@ -111,7 +111,7 @@ Invoke-Expression "$prog --quit --port $env:QPID_PORT" | Write-Output
# Test 2... store.py starts/stops/restarts its own brokers
$tests = "*"
-$env:PYTHONPATH="$PYTHON_DIR;$srcdir"
+$env:PYTHONPATH="$PYTHON_DIR;$QMF_LIB;$srcdir"
$env:QPIDD_EXEC="$prog"
$env:STORE_LIB="$store_dir\store$suffix.dll"
if ($test_store -eq "MSSQL") {
diff --git a/cpp/src/tests/run_test b/cpp/src/tests/run_test
index 4b227621bc..6ec1fd892b 100755
--- a/cpp/src/tests/run_test
+++ b/cpp/src/tests/run_test
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
diff --git a/cpp/src/tests/sasl.mk b/cpp/src/tests/sasl.mk
index 5b8419f604..69b24c3f8a 100644
--- a/cpp/src/tests/sasl.mk
+++ b/cpp/src/tests/sasl.mk
@@ -30,9 +30,21 @@ check_PROGRAMS+=sasl_version
sasl_version_SOURCES=sasl_version.cpp
sasl_version_LDADD=$(lib_client)
-TESTS += run_cluster_authentication_test sasl_fed sasl_fed_ex
+TESTS += run_cluster_authentication_test sasl_fed sasl_fed_ex_dynamic sasl_fed_ex_link sasl_fed_ex_queue sasl_fed_ex_route sasl_fed_ex_route_cluster sasl_fed_ex_link_cluster sasl_fed_ex_queue_cluster sasl_fed_ex_dynamic_cluster sasl_no_dir
LONG_TESTS += run_cluster_authentication_soak
-EXTRA_DIST += run_cluster_authentication_test sasl_fed sasl_fed_ex run_cluster_authentication_soak
+EXTRA_DIST += run_cluster_authentication_test \
+ sasl_fed \
+ sasl_fed_ex \
+ run_cluster_authentication_soak \
+ sasl_fed_ex_dynamic \
+ sasl_fed_ex_link \
+ sasl_fed_ex_queue \
+ sasl_fed_ex_route \
+ sasl_fed_ex_dynamic_cluster \
+ sasl_fed_ex_link_cluster \
+ sasl_fed_ex_queue_cluster \
+ sasl_fed_ex_route_cluster \
+ sasl_no_dir
endif # HAVE_SASL
diff --git a/cpp/src/tests/sasl_fed b/cpp/src/tests/sasl_fed
index 9845a20838..884c44177c 100755
--- a/cpp/src/tests/sasl_fed
+++ b/cpp/src/tests/sasl_fed
@@ -123,7 +123,7 @@ n_messages=100
#--------------------------------------------------
#echo " Sending 100 messages to $broker_1_port "
#--------------------------------------------------
-$builddir/datagen --count $n_messages | $SENDER_EXEC --username zag --password zag --exchange $EXCHANGE_NAME --routing-key $ROUTING_KEY --port $broker_1_port
+$builddir/datagen --count $n_messages | $SENDER_EXEC --mechanism DIGEST-MD5 --username zag --password zag --exchange $EXCHANGE_NAME --routing-key $ROUTING_KEY --port $broker_1_port
sleep 5
diff --git a/cpp/src/tests/sasl_fed_ex b/cpp/src/tests/sasl_fed_ex
index 0740650d6c..716a806874 100755
--- a/cpp/src/tests/sasl_fed_ex
+++ b/cpp/src/tests/sasl_fed_ex
@@ -19,22 +19,52 @@
# under the License.
#
-
#===============================================================================
-# This test creates a federated link between two brokers using SASL security.
+# These tests create federated links between two brokers using SASL security.
# The SASL mechanism used is EXTERNAL, which is satisfied by SSL
# transport-layer security.
#===============================================================================
source ./test_env.sh
+script_name=`basename $0`
+
+if [ $# -lt 1 ] || [ $# -gt 2 ]
+then
+ echo
+ # These are the four different ways of creating links ( or routes+links )
+ # that the qpid-route command provides.
+ echo "Usage: ${script_name} dynamic|link|queue|route [cluster]"
+ echo
+ exit 1
+fi
+
+# Has the user told us to do clustering ? -----------
+clustering_flag=
+if [ $# -eq "2" ] && [ "$2" == "cluster" ]; then
+ clustering_flag=true
+fi
+
+qpid_route_method=$1
+
+# Debugging print. --------------------------
+debug=
+function print {
+ if [ "$debug" ]; then
+ echo "${script_name}: $1"
+ fi
+}
+
+print "=========== start sasl_fed_ex $* ============"
+
+
# This minimum value corresponds to sasl version 2.1.22
minimum_sasl_version=131350
sasl_version=`$QPID_TEST_EXEC_DIR/sasl_version`
-# This test is necessary becasue this sasl version is the first one that permits
+# This test is necessary because this sasl version is the first one that permits
# redirection of the sasl config file path.
if [ "$sasl_version" -lt "$minimum_sasl_version" ]; then
echo "sasl_fed: must have sasl version 2.1.22 or greater. ( Integer value: $minimum_sasl_version ) Version is: $sasl_version"
@@ -60,6 +90,7 @@ create_certs() {
delete_certs() {
if [[ -e ${CERT_DIR} ]] ; then
+ print "removing cert dir ${CERT_DIR}"
rm -rf ${CERT_DIR}
fi
}
@@ -72,22 +103,40 @@ if [[ !(-x $CERTUTIL) ]] ; then
fi
delete_certs
-create_certs || error "Could not create test certificate"
-
+create_certs 2> /dev/null
+if [ ! $? ]; then
+ error "Could not create test certificate"
+ exit 1
+fi
-sasl_config_file=$builddir/sasl_config
+sasl_config_dir=$builddir/sasl_config
-my_random_number=$RANDOM
-tmp_root=/tmp/sasl_fed_$my_random_number
+tmp_root=${builddir}/sasl_fed_ex_temp
+print "results dir is ${tmp_root}"
+rm -rf ${tmp_root}
mkdir -p $tmp_root
SRC_SSL_PORT=6667
DST_SSL_PORT=6666
+SRC_SSL_PORT_2=6668
+DST_SSL_PORT_2=6669
+
SRC_TCP_PORT=5801
DST_TCP_PORT=5807
-SSL_LIB=../.libs/ssl.so
+SRC_TCP_PORT_2=5802
+DST_TCP_PORT_2=5803
+
+CLUSTER_NAME_SUFFIX=`hostname | tr '.' ' ' | awk '{print $1}'`
+CLUSTER_1_NAME=sasl_fed_ex_cluster_1_${CLUSTER_NAME_SUFFIX}
+CLUSTER_2_NAME=sasl_fed_ex_cluster_2_${CLUSTER_NAME_SUFFIX}
+
+print "CLUSTER_1_NAME == ${CLUSTER_1_NAME}"
+print "CLUSTER_2_NAME == ${CLUSTER_2_NAME}"
+
+SSL_LIB=${moduledir}/ssl.so
+CLUSTER_LIB=${moduledir}/cluster.so
export QPID_SSL_CERT_NAME=${TEST_HOSTNAME}
@@ -116,52 +165,112 @@ export QPID_SSL_CERT_NAME=${TEST_HOSTNAME}
# 5. DST pulls messages off the temp queue on SRC to itself.
#
+COMMON_BROKER_OPTIONS=" \
+ --ssl-sasl-no-dict \
+ --sasl-config=$sasl_config_dir \
+ --ssl-require-client-authentication \
+ --auth yes \
+ --ssl-cert-db $CERT_DIR \
+ --ssl-cert-password-file $CERT_PW_FILE \
+ --ssl-cert-name $TEST_HOSTNAME \
+ --no-data-dir \
+ --no-module-dir \
+ --load-module ${SSL_LIB} \
+ --mgmt-enable=yes \
+ --log-enable info+ \
+ --log-source yes \
+ --daemon "
+
+
+function start_brokers {
+ if [ $1 ]; then
+ # clustered ----------------------------------------
+ print "Starting SRC cluster"
+
+ print " src broker 1"
+ $QPIDD_EXEC \
+ --port=${SRC_TCP_PORT} \
+ --ssl-port ${SRC_SSL_PORT} \
+ ${COMMON_BROKER_OPTIONS} \
+ --load-module ${CLUSTER_LIB} \
+ --cluster-name ${CLUSTER_1_NAME} \
+ --log-to-file $tmp_root/qpidd_src.log 2> /dev/null
+
+ broker_ports[0]=${SRC_TCP_PORT}
+
+ print " src broker 2"
+ $QPIDD_EXEC \
+ --port=${SRC_TCP_PORT_2} \
+ --ssl-port ${SRC_SSL_PORT_2} \
+ ${COMMON_BROKER_OPTIONS} \
+ --load-module ${CLUSTER_LIB} \
+ --cluster-name ${CLUSTER_1_NAME} \
+ --log-to-file $tmp_root/qpidd_src_2.log 2> /dev/null
+
+ broker_ports[1]=${SRC_TCP_PORT_2}
+
+
+ print "Starting DST cluster"
+
+ print " dst broker 1"
+ $QPIDD_EXEC \
+ --port=${DST_TCP_PORT} \
+ --ssl-port ${DST_SSL_PORT} \
+ ${COMMON_BROKER_OPTIONS} \
+ --load-module ${CLUSTER_LIB} \
+ --cluster-name ${CLUSTER_2_NAME} \
+ --log-to-file $tmp_root/qpidd_dst.log 2> /dev/null
+
+ broker_ports[2]=${DST_TCP_PORT}
+
+ print " dst broker 2"
+ $QPIDD_EXEC \
+ --port=${DST_TCP_PORT_2} \
+ --ssl-port ${DST_SSL_PORT_2} \
+ ${COMMON_BROKER_OPTIONS} \
+ --load-module ${CLUSTER_LIB} \
+ --cluster-name ${CLUSTER_2_NAME} \
+ --log-to-file $tmp_root/qpidd_dst_2.log 2> /dev/null
+
+ broker_ports[3]=${DST_TCP_PORT_2}
+
+ else
+ # vanilla brokers --------------------------------
+ print "Starting SRC broker"
+ $QPIDD_EXEC \
+ --port=${SRC_TCP_PORT} \
+ --ssl-port ${SRC_SSL_PORT} \
+ ${COMMON_BROKER_OPTIONS} \
+ --log-to-file $tmp_root/qpidd_src.log 2> /dev/null
+
+ broker_ports[0]=${SRC_TCP_PORT}
+
+ print "Starting DST broker"
+ $QPIDD_EXEC \
+ --port=${DST_TCP_PORT} \
+ --ssl-port ${DST_SSL_PORT} \
+ ${COMMON_BROKER_OPTIONS} \
+ --log-to-file $tmp_root/qpidd_dst.log 2> /dev/null
+
+ broker_ports[1]=${DST_TCP_PORT}
+ fi
+}
+
+function halt_brokers {
+ n_brokers=${#broker_ports[@]}
+ print "Halting ${n_brokers} brokers."
+ for i in $(seq 0 $((${n_brokers} - 1)))
+ do
+ halt_port=${broker_ports[$i]}
+ print "Halting broker $i on port ${halt_port}"
+ $QPIDD_EXEC --port ${halt_port} --quit
+ done
+
+}
-#echo "-----------------------"
-#echo "Starting SRC broker"
-#echo "-----------------------"
-$QPIDD_EXEC \
- --port=${SRC_TCP_PORT} \
- --ssl-port ${SRC_SSL_PORT} \
- --ssl-sasl-no-dict \
- --sasl-config=$sasl_config_file \
- --ssl-require-client-authentication \
- --auth yes \
- --ssl-cert-db $CERT_DIR \
- --ssl-cert-password-file $CERT_PW_FILE \
- --ssl-cert-name $TEST_HOSTNAME \
- --no-data-dir \
- --no-module-dir \
- --load-module ${SSL_LIB} \
- --mgmt-enable=yes \
- --log-enable info+ \
- --log-source yes \
- --daemon \
- --log-to-file $tmp_root/qpidd_src.log 2> /dev/null
-
-
-#echo "-----------------------"
-#echo "Starting DST broker"
-#echo "-----------------------"
-$QPIDD_EXEC \
- --port=${DST_TCP_PORT} \
- --ssl-port ${DST_SSL_PORT} \
- --ssl-cert-db $CERT_DIR \
- --ssl-cert-password-file $CERT_PW_FILE \
- --ssl-cert-name $TEST_HOSTNAME \
- --ssl-sasl-no-dict \
- --ssl-require-client-authentication \
- --sasl-config=$sasl_config_file \
- --no-data-dir \
- --no-module-dir \
- --load-module ${SSL_LIB} \
- --mgmt-enable=yes \
- --log-enable info+ \
- --log-source yes \
- --daemon \
- $COMMON_BROKER_OPTIONS \
- --log-to-file $tmp_root/qpidd_dst.log 2> /dev/null
+
+start_brokers $clustering_flag
# I am not randomizing these names, because this test creates its own brokers.
@@ -170,76 +279,83 @@ ROUTING_KEY=sasl_fed_queue
EXCHANGE_NAME=sasl_fedex
-#echo "-----------------------"
-#echo "add exchanges"
-#echo "-----------------------"
+print "add exchanges"
$QPID_CONFIG_EXEC -a localhost:${SRC_TCP_PORT} add exchange direct $EXCHANGE_NAME
$QPID_CONFIG_EXEC -a localhost:${DST_TCP_PORT} add exchange direct $EXCHANGE_NAME
-#echo "-----------------------"
-#echo "add queues"
-#echo "-----------------------"
+print "add queues"
$QPID_CONFIG_EXEC -a localhost:${SRC_TCP_PORT} add queue $QUEUE_NAME
$QPID_CONFIG_EXEC -a localhost:${DST_TCP_PORT} add queue $QUEUE_NAME
-#echo "-----------------------"
-#echo "create bindings"
-#echo "-----------------------"
+print "create bindings"
$QPID_CONFIG_EXEC -a localhost:${SRC_TCP_PORT} bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY
$QPID_CONFIG_EXEC -a localhost:${DST_TCP_PORT} bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY
-#echo "-----------------------"
-#echo "qpid-route route add"
-#echo "-----------------------"
+#
# NOTE: The SRC broker *must* be referred to as $TEST_HOSTNAME, and not as "localhost".
# It must be referred to by the exact string given as the Common Name (CN) in the cert,
# which was created in the function create_certs, above.
-$QPID_ROUTE_EXEC route add localhost:${DST_TCP_PORT} $TEST_HOSTNAME:${SRC_SSL_PORT} -t ssl $EXCHANGE_NAME $ROUTING_KEY "" "" EXTERNAL
-#echo "-----------------------"
-#echo "view the route :"
-#echo "-----------------------"
-#$PYTHON_COMMANDS/qpid-route route list localhost:${DST_TCP_PORT}
-
-# I don't know how to avoid this sleep yet. It has to come after route-creation.
-sleep 5
-n_messages=100
-./datagen --count ${n_messages} | ./sender --broker localhost --port ${SRC_TCP_PORT} --exchange ${EXCHANGE_NAME} --routing-key ${ROUTING_KEY} --mechanism ANONYMOUS
+#----------------------------------------------------------------
+# Use qpid-route to create the link, or the link+route, depending
+# on which of its several methods was requested.
+#----------------------------------------------------------------
+if [ ${qpid_route_method} == "dynamic" ]; then
+ print "dynamic add"
+ $QPID_ROUTE_EXEC -t ssl dynamic add localhost:${DST_TCP_PORT} $TEST_HOSTNAME:${SRC_SSL_PORT} $EXCHANGE_NAME "" "" EXTERNAL
+elif [ ${qpid_route_method} == "link" ]; then
+ print "link add"
+ $QPID_ROUTE_EXEC -t ssl link add localhost:${DST_TCP_PORT} $TEST_HOSTNAME:${SRC_SSL_PORT} EXTERNAL
+elif [ ${qpid_route_method} == "queue" ]; then
+ print "queue add"
+ $QPID_ROUTE_EXEC -t ssl queue add localhost:${DST_TCP_PORT} $TEST_HOSTNAME:${SRC_SSL_PORT} $EXCHANGE_NAME $ROUTING_KEY EXTERNAL
+elif [ ${qpid_route_method} == "route" ]; then
+ print "route add"
+ $QPID_ROUTE_EXEC -t ssl route add localhost:${DST_TCP_PORT} $TEST_HOSTNAME:${SRC_SSL_PORT} $EXCHANGE_NAME $ROUTING_KEY "" "" EXTERNAL
+else
+ echo "unknown method: |${qpid_route_method}|"
+ echo " choices are: dynamic|link|queue|route "
+ halt_brokers
+ exit 1
+fi
-#echo "-----------------------"
-#echo "Examine DST Broker"
-#echo "-----------------------"
-dst_message_count=`qpid-stat -q localhost:${DST_TCP_PORT} | grep sasl_fed_queue | awk '{print $2}'`
+# I don't know how to avoid this sleep yet. It has to come after route-creation
+# to avoid false negatives.
+sleep 5
+# This should work the same whether or not we are running a clustered test.
+# In the case of clustered tests, the status is not printed by qpid_route.
+# So in either case, I will look only at the transport field, which should be "ssl".
+print "check the link"
+link_status=$($QPID_ROUTE_EXEC link list localhost:${DST_TCP_PORT} | tail -1 | awk '{print $3}')
-#echo "-----------------------"
-#echo "Asking brokers to quit."
-#echo "-----------------------"
-$QPIDD_EXEC --port ${SRC_TCP_PORT} --quit
-$QPIDD_EXEC --port ${DST_TCP_PORT} --quit
+halt_brokers
+sleep 1
-#echo "-----------------------"
-#echo "Removing temporary directory $tmp_root"
-#echo "-----------------------"
-rm -rf $tmp_root
+if [ ! ${link_status} ]; then
+ print "link_status is empty"
+ print "result: fail"
+ exit 2
+fi
-if [ "$dst_message_count" -eq "$n_messages" ]; then
- #echo "good: |$dst_message_count| == |$n_messages|"
+if [ ${link_status} == "ssl" ]; then
+ print "result: good"
+ # Only remove the tmp_root on success, to permit debugging.
+ print "Removing temporary directory $tmp_root"
+ rm -rf $tmp_root
exit 0
-else
- #echo "not ideal: |$dst_message_count| != |$n_messages|"
- exit 1
fi
-
-
+print "link_status has a bad value: ${link_status}"
+print "result: fail"
+exit 3
diff --git a/cpp/src/tests/sasl_fed_ex_dynamic b/cpp/src/tests/sasl_fed_ex_dynamic
new file mode 100755
index 0000000000..c20b8d69a0
--- /dev/null
+++ b/cpp/src/tests/sasl_fed_ex_dynamic
@@ -0,0 +1,27 @@
+#! /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.
+#
+
+
+source ./test_env.sh
+
+${srcdir}/sasl_fed_ex dynamic
+
+
diff --git a/cpp/src/tests/sasl_fed_ex_dynamic_cluster b/cpp/src/tests/sasl_fed_ex_dynamic_cluster
new file mode 100755
index 0000000000..b0cceccecb
--- /dev/null
+++ b/cpp/src/tests/sasl_fed_ex_dynamic_cluster
@@ -0,0 +1,28 @@
+#! /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.
+#
+
+
+source ./test_env.sh
+source $srcdir/ais_check
+
+with_ais_group ${srcdir}/sasl_fed_ex dynamic cluster
+
+
diff --git a/cpp/src/tests/sasl_fed_ex_link b/cpp/src/tests/sasl_fed_ex_link
new file mode 100755
index 0000000000..7b232d4874
--- /dev/null
+++ b/cpp/src/tests/sasl_fed_ex_link
@@ -0,0 +1,27 @@
+#! /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.
+#
+
+
+source ./test_env.sh
+
+${srcdir}/sasl_fed_ex link
+
+
diff --git a/cpp/src/tests/sasl_fed_ex_link_cluster b/cpp/src/tests/sasl_fed_ex_link_cluster
new file mode 100755
index 0000000000..4139300b12
--- /dev/null
+++ b/cpp/src/tests/sasl_fed_ex_link_cluster
@@ -0,0 +1,28 @@
+#! /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.
+#
+
+
+source ./test_env.sh
+source $srcdir/ais_check
+
+with_ais_group ${srcdir}/sasl_fed_ex link cluster
+
+
diff --git a/cpp/src/tests/sasl_fed_ex_queue b/cpp/src/tests/sasl_fed_ex_queue
new file mode 100755
index 0000000000..be0c10cf63
--- /dev/null
+++ b/cpp/src/tests/sasl_fed_ex_queue
@@ -0,0 +1,27 @@
+#! /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.
+#
+
+
+source ./test_env.sh
+
+${srcdir}/sasl_fed_ex queue
+
+
diff --git a/cpp/src/tests/sasl_fed_ex_queue_cluster b/cpp/src/tests/sasl_fed_ex_queue_cluster
new file mode 100755
index 0000000000..f251420e08
--- /dev/null
+++ b/cpp/src/tests/sasl_fed_ex_queue_cluster
@@ -0,0 +1,28 @@
+#! /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.
+#
+
+
+source ./test_env.sh
+source ${srcdir}/ais_check
+
+with_ais_group ${srcdir}/sasl_fed_ex queue cluster
+
+
diff --git a/cpp/src/tests/sasl_fed_ex_route b/cpp/src/tests/sasl_fed_ex_route
new file mode 100755
index 0000000000..dd5c4f3cac
--- /dev/null
+++ b/cpp/src/tests/sasl_fed_ex_route
@@ -0,0 +1,27 @@
+#! /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.
+#
+
+
+source ./test_env.sh
+
+${srcdir}/sasl_fed_ex route
+
+
diff --git a/cpp/src/tests/sasl_fed_ex_route_cluster b/cpp/src/tests/sasl_fed_ex_route_cluster
new file mode 100755
index 0000000000..a5d1542def
--- /dev/null
+++ b/cpp/src/tests/sasl_fed_ex_route_cluster
@@ -0,0 +1,28 @@
+#! /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.
+#
+
+
+source ./test_env.sh
+source ${srcdir}/ais_check
+
+with_ais_group ${srcdir}/sasl_fed_ex route cluster
+
+
diff --git a/cpp/src/tests/sasl_no_dir b/cpp/src/tests/sasl_no_dir
new file mode 100755
index 0000000000..15a36014bb
--- /dev/null
+++ b/cpp/src/tests/sasl_no_dir
@@ -0,0 +1,218 @@
+#! /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.
+#
+
+source ./test_env.sh
+
+script_name=`basename $0`
+
+# This minimum value corresponds to sasl version 2.1.22
+minimum_sasl_version=131350
+
+sasl_version=`$QPID_TEST_EXEC_DIR/sasl_version`
+
+# This test is necessary because this sasl version is the first one that permits
+# redirection of the sasl config file path.
+if [ "$sasl_version" -lt "$minimum_sasl_version" ]; then
+ echo "sasl_fed: must have sasl version 2.1.22 or greater. ( Integer value: $minimum_sasl_version ) Version is: $sasl_version"
+ exit 0
+fi
+
+
+sasl_config_dir=$builddir/sasl_config
+
+
+# Debugging print. --------------------------
+debug=
+function print {
+ if [ "$debug" ]; then
+ echo "${script_name}: $1"
+ fi
+}
+
+
+my_random_number=$RANDOM
+tmp_root=/tmp/sasl_fed_$my_random_number
+mkdir -p $tmp_root
+
+
+LOG_FILE=$tmp_root/qpidd.log
+
+# If you want to see this test fail, just comment out this 'mv' command.
+print "Moving sasl configuration dir."
+mv ${sasl_config_dir} ${sasl_config_dir}-
+
+
+#--------------------------------------------------
+print " Starting broker"
+#--------------------------------------------------
+$QPIDD_EXEC \
+ -p 0 \
+ --no-data-dir \
+ --auth=yes \
+ --mgmt-enable=yes \
+ --log-enable info+ \
+ --log-source yes \
+ --log-to-file ${LOG_FILE} \
+ --sasl-config=$sasl_config_dir \
+ -d 2> /dev/null 1> $tmp_root/broker_port
+
+
+
+# If it works right, the output will look something like this: ( two lines long )
+# Daemon startup failed: SASL: sasl_set_path failed: no such directory: /home/mick/trunk/qpid/cpp/src/tests/sasl_config (qpid/broker/SaslAuthenticator.cpp:112)
+# 2011-10-13 14:07:00 critical qpidd.cpp:83: Unexpected error: Daemon startup failed: SASL: sasl_set_path failed: no such directory: /home/mick/trunk/qpid/cpp/src/tests/sasl_config (qpid/broker/SaslAuthenticator.cpp:112)
+
+result=`cat ${LOG_FILE} | grep "sasl_set_path failed: no such directory" | wc -l `
+
+#--------------------------------------------------
+print "Restore the Sasl config dir to its original place."
+#--------------------------------------------------
+mv ${sasl_config_dir}- ${sasl_config_dir}
+
+if [ "2" -eq ${result} ]; then
+ print "result: success"
+ rm -rf $tmp_root
+ exit 0
+fi
+
+
+# If this test fails, the broker is still alive.
+# Kill it.
+broker_port=`cat $tmp_root/broker_port`
+#--------------------------------------------------
+print "Asking broker to quit."
+#--------------------------------------------------
+$QPIDD_EXEC --port $broker_port --quit
+
+rm -rf $tmp_root
+
+print "result: fail"
+exit 1
+
+#! /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.
+#
+
+source ./test_env.sh
+
+script_name=`basename $0`
+
+# This minimum value corresponds to sasl version 2.1.22
+minimum_sasl_version=131350
+
+sasl_version=`$QPID_TEST_EXEC_DIR/sasl_version`
+
+# This test is necessary because this sasl version is the first one that permits
+# redirection of the sasl config file path.
+if [ "$sasl_version" -lt "$minimum_sasl_version" ]; then
+ echo "sasl_fed: must have sasl version 2.1.22 or greater. ( Integer value: $minimum_sasl_version ) Version is: $sasl_version"
+ exit 0
+fi
+
+
+sasl_config_dir=$builddir/sasl_config
+
+
+# Debugging print. --------------------------
+debug=
+function print {
+ if [ "$debug" ]; then
+ echo "${script_name}: $1"
+ fi
+}
+
+
+my_random_number=$RANDOM
+tmp_root=/tmp/sasl_fed_$my_random_number
+mkdir -p $tmp_root
+
+
+LOG_FILE=$tmp_root/qpidd.log
+
+# If you want to see this test fail, just comment out this 'mv' command.
+print "Moving sasl configuration dir."
+mv ${sasl_config_dir} ${sasl_config_dir}-
+
+
+#--------------------------------------------------
+print " Starting broker"
+#--------------------------------------------------
+$QPIDD_EXEC \
+ -p 0 \
+ --no-data-dir \
+ --auth=yes \
+ --mgmt-enable=yes \
+ --log-enable info+ \
+ --log-source yes \
+ --log-to-file ${LOG_FILE} \
+ --sasl-config=$sasl_config_dir \
+ -d 2> /dev/null 1> $tmp_root/broker_port
+
+
+
+# If it works right, the output will look something like this: ( two lines long )
+# Daemon startup failed: SASL: sasl_set_path failed: no such directory: /home/mick/trunk/qpid/cpp/src/tests/sasl_config (qpid/broker/SaslAuthenticator.cpp:112)
+# 2011-10-13 14:07:00 critical qpidd.cpp:83: Unexpected error: Daemon startup failed: SASL: sasl_set_path failed: no such directory: /home/mick/trunk/qpid/cpp/src/tests/sasl_config (qpid/broker/SaslAuthenticator.cpp:112)
+
+result=`cat ${LOG_FILE} | grep "sasl_set_path failed: no such directory" | wc -l `
+
+#--------------------------------------------------
+print "Restore the Sasl config dir to its original place."
+#--------------------------------------------------
+mv ${sasl_config_dir}- ${sasl_config_dir}
+
+if [ "2" -eq ${result} ]; then
+ print "result: success"
+ rm -rf $tmp_root
+ exit 0
+fi
+
+
+# If this test fails, the broker is still alive.
+# Kill it.
+broker_port=`cat $tmp_root/broker_port`
+#--------------------------------------------------
+print "Asking broker to quit."
+#--------------------------------------------------
+$QPIDD_EXEC --port $broker_port --quit
+
+rm -rf $tmp_root
+
+print "result: fail"
+exit 1
+
diff --git a/cpp/src/tests/sasl_test_setup.sh b/cpp/src/tests/sasl_test_setup.sh
index 6395ba6ec3..3e69c0f02b 100755
--- a/cpp/src/tests/sasl_test_setup.sh
+++ b/cpp/src/tests/sasl_test_setup.sh
@@ -30,6 +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
EOF
# Populate temporary sasl db.
diff --git a/cpp/src/tests/sender.cpp b/cpp/src/tests/sender.cpp
index 9850e851da..063b5e87dc 100644
--- a/cpp/src/tests/sender.cpp
+++ b/cpp/src/tests/sender.cpp
@@ -120,7 +120,7 @@ void Sender::execute(AsyncSession& session, bool isRetry)
string data;
while (getline(std::cin, data)) {
message.setData(data);
- message.getHeaders().setInt("SN", ++sent);
+ //message.getHeaders().setInt("SN", ++sent);
string matchKey;
if (lvqMatchValues && getline(lvqMatchValues, matchKey)) {
message.getHeaders().setString(QueueOptions::strLVQMatchProperty, matchKey);
diff --git a/cpp/src/tests/ssl_test b/cpp/src/tests/ssl_test
index 04584f169d..6c056f4288 100755
--- a/cpp/src/tests/ssl_test
+++ b/cpp/src/tests/ssl_test
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
#
# Licensed to the Apache Software Foundation (ASF) under one
@@ -47,9 +47,13 @@ delete_certs() {
fi
}
-COMMON_OPTS="--daemon --no-data-dir --no-module-dir --auth no --config $CONFIG --load-module $SSL_LIB --ssl-cert-db $CERT_DIR --ssl-cert-password-file $CERT_PW_FILE --ssl-cert-name $TEST_HOSTNAME --require-encryption"
+COMMON_OPTS="--daemon --no-data-dir --no-module-dir --config $CONFIG --load-module $SSL_LIB --ssl-cert-db $CERT_DIR --ssl-cert-password-file $CERT_PW_FILE --ssl-cert-name $TEST_HOSTNAME"
start_broker() { # $1 = extra opts
- ../qpidd --transport ssl --port 0 --ssl-port 0 $COMMON_OPTS $1;
+ ../qpidd --transport ssl --port 0 --ssl-port 0 $COMMON_OPTS --require-encryption --auth no $1;
+}
+
+start_authenticating_broker() {
+ ../qpidd --transport ssl --port 0 --ssl-port 0 $COMMON_OPTS --require-encryption --ssl-sasl-no-dict --ssl-require-client-authentication --auth yes;
}
stop_brokers() {
@@ -64,6 +68,13 @@ cleanup() {
delete_certs
}
+pick_port() {
+ # We need a fixed port to set --cluster-url. Use qpidd to pick a free port.
+ PICK=`../qpidd --no-module-dir -dp0`
+ ../qpidd --no-module-dir -qp $PICK
+ echo $PICK
+}
+
CERTUTIL=$(type -p certutil)
if [[ !(-x $CERTUTIL) ]] ; then
echo "No certutil, skipping ssl test";
@@ -93,7 +104,7 @@ test "$MSG" = "hello" || { echo "receive failed '$MSG' != 'hello'"; exit 1; }
#### Client Authentication tests
-PORT2=`start_broker --ssl-require-client-authentication` || error "Could not start broker"
+PORT2=`start_authenticating_broker` || error "Could not start broker"
echo "Running SSL client authentication test on port $PORT2"
URL=amqp:ssl:$TEST_HOSTNAME:$PORT2
@@ -109,19 +120,22 @@ test "$MSG3" = "" || { echo "receive succeeded without valid ssl cert '$MSG3' !=
stop_brokers
+#Test multiplexed connection where SSL and plain TCP are served by the same port
+PORT=`pick_port`; ../qpidd --port $PORT --ssl-port $PORT $COMMON_OPTS --transport ssl --auth no
+echo "Running multiplexed SSL/TCP test on $PORT"
+
+./qpid-perftest --count ${COUNT} --port ${PORT} -P ssl -b $TEST_HOSTNAME --summary || { echo "SSL on multiplexed connection failed!"; exit 1; }
+./qpid-perftest --count ${COUNT} --port ${PORT} -P tcp -b $TEST_HOSTNAME --summary || { echo "Plain TCP on multiplexed connection failed!"; exit 1; }
+
+stop_brokers
+
test -z $CLUSTER_LIB && exit 0 # Exit if cluster not supported.
## Test failover in a cluster using SSL only
. $srcdir/ais_check # Will exit if clustering not enabled.
-pick_port() {
- # We need a fixed port to set --cluster-url. Use qpidd to pick a free port.
- PICK=`../qpidd --no-module-dir -dp0`
- ../qpidd --no-module-dir -qp $PICK
- echo $PICK
-}
ssl_cluster_broker() { # $1 = port
- ../qpidd $COMMON_OPTS --load-module $CLUSTER_LIB --cluster-name ssl_test.$HOSTNAME.$$ --cluster-url amqp:ssl:$TEST_HOSTNAME:$1 --port 0 --ssl-port $1 --transport ssl > /dev/null
+ ../qpidd $COMMON_OPTS --require-encryption --auth no --load-module $CLUSTER_LIB --cluster-name ssl_test.$HOSTNAME.$$ --cluster-url amqp:ssl:$TEST_HOSTNAME:$1 --port 0 --ssl-port $1 --transport ssl > /dev/null
# Wait for broker to be ready
qpid-ping -Pssl -b $TEST_HOSTNAME -qp $1 || { echo "Cannot connect to broker on $1"; exit 1; }
echo "Running SSL cluster broker on port $1"
diff --git a/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp b/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp
index a0b665db73..024f20b147 100644
--- a/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp
+++ b/cpp/src/tests/windows/DisableWin32ErrorWindows.cpp
@@ -26,7 +26,9 @@
// include this file with the executable being built. If the default
// behaviors are desired, don't include this file in the build.
+#if defined(_MSC_VER)
#include <crtdbg.h>
+#endif
#include <windows.h>
#include <iostream>
@@ -53,12 +55,14 @@ static redirect_errors_to_stderr block;
redirect_errors_to_stderr::redirect_errors_to_stderr()
{
+#if defined(_MSC_VER)
_CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR);
_CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_FILE);
_CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR);
_CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_FILE);
_CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+#endif
// Prevent the system from displaying the critical-error-handler
// and can't-open-file message boxes.
diff --git a/cpp/src/windows/QpiddBroker.cpp b/cpp/src/windows/QpiddBroker.cpp
index 884cdc366b..42ba97bdb1 100644
--- a/cpp/src/windows/QpiddBroker.cpp
+++ b/cpp/src/windows/QpiddBroker.cpp
@@ -147,7 +147,7 @@ NamedSharedMemory<T>::NamedSharedMemory(const std::string& n) :
name(n),
memory(NULL),
data(0)
-{};
+{}
template <typename T>
NamedSharedMemory<T>::~NamedSharedMemory() {
@@ -155,7 +155,7 @@ NamedSharedMemory<T>::~NamedSharedMemory() {
::UnmapViewOfFile(data);
if (memory != NULL)
::CloseHandle(memory);
-};
+}
template <typename T>
T& NamedSharedMemory<T>::create() {
diff --git a/cpp/src/windows/resources/template-resource.rc b/cpp/src/windows/resources/template-resource.rc
index 725d1c9391..8ca0a90890 100644
--- a/cpp/src/windows/resources/template-resource.rc
+++ b/cpp/src/windows/resources/template-resource.rc
@@ -24,7 +24,7 @@
//
// Generated from the TEXTINCLUDE 2 resource.
//
-#include "afxres.h"
+#include "windows.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
diff --git a/cpp/src/xml.mk b/cpp/src/xml.mk
index 0d700fcc03..baf3803647 100644
--- a/cpp/src/xml.mk
+++ b/cpp/src/xml.mk
@@ -16,7 +16,7 @@
# specific language governing permissions and limitations
# under the License.
#
-dmodule_LTLIBRARIES += xml.la
+dmoduleexec_LTLIBRARIES += xml.la
xml_la_SOURCES = \
qpid/xml/XmlExchange.cpp \
diff --git a/cpp/xml/cluster.xml b/cpp/xml/cluster.xml
index be1c1f868c..899625f5ec 100644
--- a/cpp/xml/cluster.xml
+++ b/cpp/xml/cluster.xml
@@ -8,9 +8,9 @@
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
--
+-
- http://www.apache.org/licenses/LICENSE-2.0
--
+-
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an
- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@@ -78,10 +78,6 @@
<field name="left" type="vbin16"/> <!-- packed member-id array -->
</control>
- <control name="message-expired" code="0x12">
- <field name="id" type="uint64"/>
- </control>
-
<domain name="error-type" type="uint8" label="Types of error">
<enum>
<choice name="none" value="0"/>
@@ -89,7 +85,7 @@
<choice name="connection" value="2"/>
</enum>
</domain>
-
+
<!-- Check for error consistency across the cluster -->
<control name="error-check" code="0x14">
<field name="type" type="error-type"/>
@@ -116,6 +112,11 @@
<field name="message" type="vbin32"/>
</control>
+ <!-- Update the cluster time -->
+ <control name="clock" code="0x22">
+ <field name="time" type="uint64"/>
+ </control>
+
</class>
<!-- Controls associated with a specific connection. -->
@@ -149,7 +150,7 @@
<!-- Abort a connection that is sending invalid data. -->
<control name="abort" code="0x4"/>
-
+
<!-- Update controls. Sent to a new broker in joining mode.
A connection is updated as followed:
- send the shadow's management ID in shadow-perpare on the update connection
@@ -183,7 +184,7 @@
<field name="position" type="sequence-no"/>
<field name="tag" type="str8"/>
<field name="id" type="sequence-no"/>
- <field name="acquired" type="bit"/> <!--If not set, message follows. -->
+ <field name="acquired" type="bit"/> <!--If not set, message is on update queue. -->
<field name="accepted" type="bit"/>
<field name="cancelled" type="bit"/>
<field name="completed" type="bit"/>
@@ -192,9 +193,9 @@
<field name="enqueued" type="bit"/>
<field name="credit" type="uint32"/>
</control>
-
+
<!-- Tx transaction state. -->
- <control name="tx-start" code="0x12"/>
+ <control name="tx-start" code="0x12"/>
<control name="tx-accept" code="0x13"> <field name="commands" type="sequence-set"/> </control>
<control name="tx-dequeue" code="0x14"> <field name="queue" type="str8"/> </control>
<control name="tx-enqueue" code="0x15"> <field name="queue" type="str8"/> </control>
@@ -204,13 +205,37 @@
</control>
<control name="tx-end" code="0x17"/>
<control name="accumulated-ack" code="0x18"> <field name="commands" type="sequence-set"/> </control>
-
+
<!-- Consumers in the connection's output task -->
<control name="output-task" code="0x19">
<field name="channel" type="uint16"/>
<field name="name" type="str8"/>
</control>
+ <!-- Dtx transaction state. -->
+ <control name="dtx-start" code="0x1A">
+ <field name="xid" type="str16"/>
+ <field name="ended" type="bit"/>
+ <field name="suspended" type="bit"/>
+ <field name="failed" type="bit"/>
+ <field name="expired" type="bit"/>
+ </control>
+ <control name="dtx-end" code="0x1B"/>
+
+ <control name="dtx-ack" code="0x1C"/>
+
+ <control name="dtx-buffer-ref" code="0x1D">
+ <field name="xid" type="str16"/>
+ <field name="index" type="uint32"/>
+ <field name="suspended" type="bit"/>
+ </control>
+
+ <control name="dtx-work-record" code="0x1E">
+ <field name="xid" type="str16"/>
+ <field name="prepared" type="bit"/>
+ <field name="timeout" type="uint32"/>
+ </control>
+
<!-- Complete a session state update. -->
<control name="session-state" code="0x1F">
<!-- Target session deduced from channel number. -->
@@ -222,6 +247,7 @@
<field name="received" type="sequence-no"/> <!-- Received up to here (>= expected) -->
<field name="unknown-completed" type="sequence-set"/> <!-- Completed but not known to peer. -->
<field name="received-incomplete" type="sequence-set"/> <!-- Received and incomplete -->
+ <field name="dtx-selected" type="bit"/>
</control>
<!-- Complete a shadow connection update. -->
@@ -252,10 +278,6 @@
<!-- Replicate encoded exchanges/queues. -->
<control name="exchange" code="0x31"><field name="encoded" type="str32"/></control>
- <control name="queue" code="0x32"><field name="encoded" type="str32"/></control>
-
- <!-- Set expiry-id for subsequent messages. -->
- <control name="expiry-id" code="0x33"><field name="expiry-id" type="uint64"/></control>
<!-- Add a listener to a queue -->
<control name="add-queue-listener" code="0x34">
@@ -282,6 +304,26 @@
<field name="position" type="uint8"/>
<field name="count" type="uint8"/>
</control>
+
+ <!-- Replicate a QueueObserver for a given queue. -->
+ <control name="queue-observer-state" code="0x39">
+ <field name="queue" type="str8"/>
+ <field name="observer-id" type="str8"/>
+ <field name="state" type="map"/> <!-- "name"=value -->
+ </control>
+
+ <!-- Update the cluster time -->
+ <control name="clock" code="0x40">
+ <field name="time" type="uint64"/>
+ </control>
+
+ <!-- Update a queue's dequeue rate -->
+ <control name="queue-dequeue-since-purge-state" code="0x41">
+ <field name="queue" type="str8"/>
+ <field name="dequeueSincePurge" type="uint32"/>
+ </control>
+
+
</class>
</amqp>