summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Godfrey <rgodfrey@apache.org>2009-10-05 11:29:03 +0000
committerRobert Godfrey <rgodfrey@apache.org>2009-10-05 11:29:03 +0000
commit2e20e00f3816aa63f3633a5a16f1891261750c74 (patch)
treee33cee5d10c452e83bc6970553223f0077e8fb53
parent2cc551d076f3388539392cd36827ad83654460d6 (diff)
downloadqpid-python-2e20e00f3816aa63f3633a5a16f1891261750c74.tar.gz
Merged from trunk up to r804202
git-svn-id: https://svn.apache.org/repos/asf/qpid/branches/java-broker-0-10@821761 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--qpid/cpp/INSTALL-WINDOWS10
-rw-r--r--qpid/cpp/bindings/qmf/Makefile.am5
-rw-r--r--qpid/cpp/bindings/qmf/python/Makefile.am12
-rw-r--r--qpid/cpp/bindings/qmf/ruby/qmf.rb27
-rw-r--r--qpid/cpp/bindings/qmf/ruby/ruby.i15
-rw-r--r--[-rwxr-xr-x]qpid/cpp/bindings/qmf/tests/Makefile.am (renamed from qpid/python/run-tests)22
-rwxr-xr-xqpid/cpp/bindings/qmf/tests/agent_ruby.rb99
-rwxr-xr-xqpid/cpp/bindings/qmf/tests/python_console.py59
-rwxr-xr-xqpid/cpp/bindings/qmf/tests/run_interop_tests67
-rw-r--r--qpid/cpp/boost-1.32-support/supressions638
-rw-r--r--qpid/cpp/configure.ac1
-rw-r--r--qpid/cpp/examples/qmf-agent/Makefile2
-rw-r--r--qpid/cpp/include/qpid/Msg.h23
-rw-r--r--qpid/cpp/src/CMakeLists.txt2
-rw-r--r--qpid/cpp/src/Makefile.am16
-rw-r--r--qpid/cpp/src/cluster.mk9
-rw-r--r--qpid/cpp/src/qmf/Agent.cpp2
-rw-r--r--qpid/cpp/src/qpid/broker/Broker.cpp2
-rw-r--r--qpid/cpp/src/qpid/broker/BrokerSingleton.cpp36
-rw-r--r--qpid/cpp/src/qpid/broker/BrokerSingleton.h53
-rw-r--r--qpid/cpp/src/qpid/broker/DirectExchange.cpp22
-rw-r--r--qpid/cpp/src/qpid/broker/Link.cpp29
-rw-r--r--qpid/cpp/src/qpid/broker/Link.h3
-rw-r--r--qpid/cpp/src/qpid/broker/LinkRegistry.cpp13
-rw-r--r--qpid/cpp/src/qpid/broker/LinkRegistry.h4
-rw-r--r--qpid/cpp/src/qpid/broker/MessageStore.h2
-rw-r--r--qpid/cpp/src/qpid/broker/MessageStoreModule.cpp4
-rw-r--r--qpid/cpp/src/qpid/broker/MessageStoreModule.h2
-rw-r--r--qpid/cpp/src/qpid/broker/NullMessageStore.cpp2
-rw-r--r--qpid/cpp/src/qpid/broker/NullMessageStore.h2
-rw-r--r--qpid/cpp/src/qpid/broker/Queue.h4
-rw-r--r--qpid/cpp/src/qpid/broker/QueueCleaner.cpp5
-rw-r--r--qpid/cpp/src/qpid/broker/QueueCleaner.h1
-rw-r--r--qpid/cpp/src/qpid/broker/SemanticState.cpp54
-rw-r--r--qpid/cpp/src/qpid/broker/SemanticState.h5
-rw-r--r--qpid/cpp/src/qpid/broker/SessionAdapter.cpp7
-rw-r--r--qpid/cpp/src/qpid/broker/SessionState.cpp3
-rw-r--r--qpid/cpp/src/qpid/broker/Timer.cpp120
-rw-r--r--qpid/cpp/src/qpid/broker/Timer.h87
-rw-r--r--qpid/cpp/src/qpid/broker/TopicExchange.cpp23
-rw-r--r--qpid/cpp/src/qpid/client/ConnectionHandler.cpp12
-rw-r--r--qpid/cpp/src/qpid/client/StateManager.cpp12
-rw-r--r--qpid/cpp/src/qpid/client/StateManager.h1
-rw-r--r--qpid/cpp/src/qpid/cluster/Cluster.cpp46
-rw-r--r--qpid/cpp/src/qpid/cluster/Cluster.h12
-rw-r--r--qpid/cpp/src/qpid/cluster/ClusterPlugin.cpp2
-rw-r--r--qpid/cpp/src/qpid/cluster/ClusterSettings.h2
-rw-r--r--qpid/cpp/src/qpid/cluster/Cpg.cpp24
-rw-r--r--qpid/cpp/src/qpid/cluster/Cpg.h29
-rw-r--r--qpid/cpp/src/qpid/cluster/Event.cpp2
-rw-r--r--qpid/cpp/src/qpid/cluster/Multicaster.cpp32
-rw-r--r--qpid/cpp/src/qpid/cluster/Multicaster.h3
-rw-r--r--qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp1
-rw-r--r--qpid/cpp/src/qpid/cluster/PollerDispatch.cpp7
-rw-r--r--qpid/cpp/src/qpid/cluster/PollerDispatch.h1
-rw-r--r--qpid/cpp/src/qpid/cluster/Quorum_cman.cpp66
-rw-r--r--qpid/cpp/src/qpid/cluster/Quorum_cman.h24
-rw-r--r--qpid/cpp/src/qpid/cluster/Quorum_null.h9
-rw-r--r--qpid/cpp/src/qpid/cluster/WatchDogPlugin.cpp114
-rw-r--r--qpid/cpp/src/qpid/cluster/qpidd_watchdog.cpp60
-rw-r--r--qpid/cpp/src/qpid/framing/AMQFrame.cpp2
-rw-r--r--qpid/cpp/src/qpid/framing/IsInSequenceSet.h51
-rw-r--r--qpid/cpp/src/qpid/sys/DispatchHandle.cpp5
-rw-r--r--qpid/cpp/src/qpid/sys/Timer.cpp25
-rw-r--r--qpid/cpp/src/qpid/sys/Timer.h2
-rw-r--r--qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp5
-rwxr-xr-xqpid/cpp/src/qpid/sys/windows/LockFile.cpp3
-rw-r--r--qpid/cpp/src/tests/Makefile.am15
-rw-r--r--qpid/cpp/src/tests/PollerTest.cpp67
-rw-r--r--qpid/cpp/src/tests/TimerTest.cpp3
-rwxr-xr-xqpid/cpp/src/tests/acl.py89
-rwxr-xr-xqpid/cpp/src/tests/cli_tests.py61
-rw-r--r--qpid/cpp/src/tests/cluster.mk69
-rwxr-xr-xqpid/cpp/src/tests/cluster_tests.py103
-rw-r--r--qpid/cpp/src/tests/dlclose_noop.c2
-rwxr-xr-xqpid/cpp/src/tests/federation.py210
-rwxr-xr-xqpid/cpp/src/tests/python_tests7
-rw-r--r--qpid/cpp/src/tests/qrsh.cpp166
-rw-r--r--qpid/cpp/src/tests/qrsh_run.cpp321
-rw-r--r--qpid/cpp/src/tests/qrsh_server.cpp1062
-rwxr-xr-xqpid/cpp/src/tests/qrsh_utils/10_all30
-rwxr-xr-xqpid/cpp/src/tests/qrsh_utils/1_remote_run26
-rwxr-xr-xqpid/cpp/src/tests/qrsh_utils/2_forever26
-rwxr-xr-xqpid/cpp/src/tests/qrsh_utils/3_kill_it27
-rwxr-xr-xqpid/cpp/src/tests/qrsh_utils/4_wait_for_it26
-rwxr-xr-xqpid/cpp/src/tests/qrsh_utils/5_exited64
-rwxr-xr-xqpid/cpp/src/tests/qrsh_utils/6_get29
-rwxr-xr-xqpid/cpp/src/tests/qrsh_utils/7_get_output44
-rwxr-xr-xqpid/cpp/src/tests/qrsh_utils/8_any43
-rwxr-xr-xqpid/cpp/src/tests/qrsh_utils/9_alias38
-rw-r--r--qpid/cpp/src/tests/qrsh_utils/qrsh_example_command.cpp52
-rw-r--r--qpid/cpp/src/tests/qrsh_utils/qrsh_forever.cpp50
-rw-r--r--qpid/cpp/src/tests/qrsh_utils/qsh_doc.txt309
-rwxr-xr-xqpid/cpp/src/tests/run_acl_tests4
-rwxr-xr-xqpid/cpp/src/tests/run_cli_tests4
-rwxr-xr-xqpid/cpp/src/tests/run_cluster_tests30
-rwxr-xr-xqpid/cpp/src/tests/run_federation_tests5
-rwxr-xr-xqpid/cpp/src/tests/run_long_cluster_tests2
-rwxr-xr-xqpid/cpp/src/tests/test_watchdog16
-rw-r--r--qpid/cpp/src/windows/QpiddBroker.cpp11
-rw-r--r--qpid/java/broker/build.xml2
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java91
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java3
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java13
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java7
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java7
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java7
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/CurrentActor.java57
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/ManagementActor.java87
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/QueueActor.java52
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/LogMessages.properties107
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/LogMessages_en_US.properties10
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/QueueLogSubject.java4
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java9
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java18
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java33
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java8
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java6
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java39
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java3
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java7
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBinding.java2
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java48
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java2
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java7
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java5
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/LogMessageTest.java71
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/ManagementActorTest.java22
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/QueueActorTest.java119
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/QueueMessagesTest.java119
-rw-r--r--qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/SubscriptionMessagesTest.java12
-rw-r--r--qpid/java/build.xml4
-rw-r--r--qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedBroker.java22
-rw-r--r--qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedQueue.java41
-rw-r--r--qpid/java/management/common/src/test/java/org/apache/qpid/management/common/mbeans/ManagedQueueTest.java84
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java4
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/MBeanUtility.java67
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java10
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/queue/QueueOperationsTabControl.java17
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/MBeanTypeTabControl.java32
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/QueueTypeTabControl.java653
-rw-r--r--qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/users/UserManagementTabControl.java25
-rw-r--r--qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/AllObjects.java2
-rw-r--r--qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/ConnectionObject.java2
-rw-r--r--qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/ExchangeObject.java3
-rw-r--r--qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/ObjectNames.java7
-rw-r--r--qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/QueueObject.java2
-rw-r--r--qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/UserManagementObject.java2
-rw-r--r--qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/VirtualHostObject.java2
-rw-r--r--qpid/java/systests/build.xml2
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java669
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BrokerLoggingTest.java12
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ChannelLoggingTest.java22
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DerbyMessageStoreLoggingTest.java73
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DurableQueueLoggingTest.java16
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ManagementLoggingTest.java51
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/logging/QueueLoggingTest.java1
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java135
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/logging/VirtualHostLoggingTest.java139
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/server/store/PersistentStoreTest.java70
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/client/FlowControlTest.java18
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java42
-rw-r--r--qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitorTest.java4
-rw-r--r--qpid/java/test-profiles/010Excludes3
-rw-r--r--qpid/java/test-profiles/Excludes1
-rw-r--r--qpid/java/test-profiles/java-derby.testprofile2
-rw-r--r--qpid/java/test-profiles/java.testprofile2
-rw-r--r--qpid/python/README.txt28
-rw-r--r--qpid/python/cpp_failing_0-10.txt0
-rw-r--r--qpid/python/cpp_failing_0-8.txt0
-rw-r--r--qpid/python/cpp_failing_0-9.txt4
-rw-r--r--qpid/python/java_failing_0-8.txt2
-rw-r--r--qpid/python/java_failing_0-9.txt18
-rw-r--r--qpid/python/mllib/dom.py15
-rwxr-xr-xqpid/python/pal2py274
-rwxr-xr-xqpid/python/perftest113
-rw-r--r--qpid/python/qmf/console.py28
-rwxr-xr-xqpid/python/qpid-python-test95
-rw-r--r--qpid/python/qpid/assembler.py118
-rw-r--r--qpid/python/qpid/client.py7
-rw-r--r--qpid/python/qpid/codec010.py227
-rw-r--r--qpid/python/qpid/connection.py65
-rw-r--r--qpid/python/qpid/connection08.py15
-rw-r--r--qpid/python/qpid/datatypes.py6
-rw-r--r--qpid/python/qpid/delegates.py33
-rw-r--r--qpid/python/qpid/exceptions.py1
-rw-r--r--qpid/python/qpid/framer.py63
-rw-r--r--qpid/python/qpid/framing.py172
-rw-r--r--qpid/python/qpid/generator.py48
-rw-r--r--qpid/python/qpid/harness.py (renamed from qpid/python/tests_0-9/execution.py)15
-rw-r--r--qpid/python/qpid/messaging.py29
-rw-r--r--qpid/python/qpid/ops.py283
-rw-r--r--qpid/python/qpid/peer.py4
-rw-r--r--qpid/python/qpid/session.py199
-rw-r--r--qpid/python/qpid/spec.py4
-rw-r--r--qpid/python/qpid/spec010.py708
-rw-r--r--qpid/python/qpid/testlib.py327
-rw-r--r--qpid/python/qpid/tests/framing.py116
-rw-r--r--qpid/python/qpid/tests/messaging.py8
-rw-r--r--qpid/python/qpid_config.py2
-rwxr-xr-xqpid/python/rule2test108
-rw-r--r--qpid/python/tests/__init__.py10
-rw-r--r--qpid/python/tests/assembler.py78
-rw-r--r--qpid/python/tests/codec.py14
-rw-r--r--qpid/python/tests/codec010.py14
-rw-r--r--qpid/python/tests/connection.py24
-rw-r--r--qpid/python/tests/datatypes.py12
-rw-r--r--qpid/python/tests/framer.py95
-rw-r--r--qpid/python/tests/spec.py74
-rw-r--r--qpid/python/tests/spec010.py70
-rw-r--r--qpid/python/tests_0-10/__init__.py1
-rw-r--r--qpid/python/tests_0-10/management.py2
-rw-r--r--qpid/python/tests_0-10/message.py4
-rw-r--r--qpid/python/tests_0-10/tx.py2
-rw-r--r--qpid/python/tests_0-8/__init__.py2
-rw-r--r--qpid/python/tests_0-8/basic.py7
-rw-r--r--qpid/python/tests_0-8/broker.py24
-rw-r--r--qpid/python/tests_0-8/example.py2
-rw-r--r--qpid/python/tests_0-8/queue.py2
-rw-r--r--qpid/python/tests_0-8/testlib.py2
-rw-r--r--qpid/python/tests_0-8/tx.py2
-rw-r--r--qpid/python/tests_0-9/__init__.py2
-rw-r--r--qpid/python/tests_0-9/basic.py396
-rw-r--r--qpid/python/tests_0-9/broker.py133
-rw-r--r--qpid/python/tests_0-9/dtx.py587
-rw-r--r--qpid/python/tests_0-9/example.py94
-rw-r--r--qpid/python/tests_0-9/exchange.py327
-rw-r--r--qpid/python/tests_0-9/message.py657
-rw-r--r--qpid/python/tests_0-9/query.py2
-rw-r--r--qpid/python/tests_0-9/queue.py261
-rw-r--r--qpid/python/tests_0-9/testlib.py66
-rw-r--r--qpid/python/tests_0-9/tx.py188
-rwxr-xr-xqpid/review/agenda.py23
-rw-r--r--qpid/review/svnlog2wiki.xsl2
-rw-r--r--qpid/specs/amqp.0-9.xml35
235 files changed, 8181 insertions, 6441 deletions
diff --git a/qpid/cpp/INSTALL-WINDOWS b/qpid/cpp/INSTALL-WINDOWS
index e4e1c6b1fc..43d953aa9e 100644
--- a/qpid/cpp/INSTALL-WINDOWS
+++ b/qpid/cpp/INSTALL-WINDOWS
@@ -102,8 +102,9 @@ files that are part of the build.
CMakeSetup on the desktop - it is named CMake.
- The CMakeSetup window has 2 directory selection areas at the top; one for
where the source is located (C:\qpid\trunk\qpid\cpp) and one for where you
- wish to place the build. A subdirectory of the source directory is generally
- preferred (C:\qpid\trunk\qpid\cpp\build)
+ wish to place the build. A directory separate from the source directory is
+ generally preferred; it can be, but need not be, a subdirectory to the
+ source. (C:\qpid\trunk\qpid\cpp\build)
- The first time you run CMakeSetup it will ask you to select a generator.
You should select the method you prefer to build with: Visual Studio 2008
or NMake Makefiles.
@@ -145,4 +146,7 @@ Linux. To try it out "make doxygen" then open doxygen/html/index.html.
7. Troubleshooting
==================
-There are currently two JIRAs open against Windows.
+When the broker is executed it will try to store a file in the "qpidd"
+subdirectory of the current user's temporary file directory, or in
+C:\WINDOWS\TEMP. If the qpidd directory can't be created or accessed the
+broker startup will fail.
diff --git a/qpid/cpp/bindings/qmf/Makefile.am b/qpid/cpp/bindings/qmf/Makefile.am
index 1693248e9d..68312a4208 100644
--- a/qpid/cpp/bindings/qmf/Makefile.am
+++ b/qpid/cpp/bindings/qmf/Makefile.am
@@ -17,6 +17,9 @@
# under the License.
#
+if HAVE_SWIG
+
EXTRA_DIST = qmfengine.i
-SUBDIRS = ruby
+SUBDIRS = ruby tests
+endif
diff --git a/qpid/cpp/bindings/qmf/python/Makefile.am b/qpid/cpp/bindings/qmf/python/Makefile.am
index 9bb1b00d50..7b3f4d3be7 100644
--- a/qpid/cpp/bindings/qmf/python/Makefile.am
+++ b/qpid/cpp/bindings/qmf/python/Makefile.am
@@ -21,27 +21,27 @@ if HAVE_PYTHON_DEVEL
INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src/qmf -I$(top_srcdir)/src -I$(top_builddir)/src
-EXTRA_DIST = python.i
-
generated_file_list = \
qmfengine.cpp \
qmfengine.py
+EXTRA_DIST = python.i
+BUILT_SOURCES = $(generated_file_list)
+
$(generated_file_list): $(srcdir)/python.i $(srcdir)/../qmfengine.i
swig -python -c++ -Wall -I/usr/include $(INCLUDES) $(QPID_CXXFLAGS) -I$(top_srcdir)/src/qmf -o qmfengine.cpp $(srcdir)/python.i
-AM_CPPFLAGS = $(QPID_CXXFLAGS) $(INCLUDES) -I$(srcdir)/qmf -I$(PYTHON_INC)
-AM_CXXFLAGS = $(QPID_CXXFLAGS)
-
pylibdir = $(PYTHON_LIB)
lib_LTLIBRARIES = _qmfengine.la
_qmfengine_la_LDFLAGS = -avoid-version -module -shrext "$(PYTHON_SO)"
_qmfengine_la_LIBADD = $(PYTHON_LIBS) -L$(top_builddir)/src/.libs -lqpidclient $(top_builddir)/src/libqmfcommon.la
-
+_qmfengine_la_CXXFLAGS = $(INCLUDES) -I$(srcdir)/qmf -I$(PYTHON_INC)
_qmfengine_la_SOURCES = \
qmfengine.cpp
+CLEANFILES = $(generated_file_list)
+
endif # HAVE_PYTHON_DEVEL
diff --git a/qpid/cpp/bindings/qmf/ruby/qmf.rb b/qpid/cpp/bindings/qmf/ruby/qmf.rb
index fd4f09977a..7ee447c675 100644
--- a/qpid/cpp/bindings/qmf/ruby/qmf.rb
+++ b/qpid/cpp/bindings/qmf/ruby/qmf.rb
@@ -172,7 +172,6 @@ module Qmf
@by_hash[@map.key(a)] = by_key(@map.key(a))
a += 1
end
-
end
def [] (key)
@@ -486,21 +485,21 @@ module Qmf
end
end
- def set_attr(name, value)
+ def set_attr(name, v)
val = value(name)
case val.getType
- when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.setUint(value)
- when TYPE_UINT64 then val.setUint64(value)
- when TYPE_SSTR, TYPE_LSTR then value ? val.setString(value) : val.setString('')
- when TYPE_ABSTIME then val.setInt64(value)
- when TYPE_DELTATIME then val.setUint64(value)
- when TYPE_REF then val.setObjectId(value.impl)
- when TYPE_BOOL then value ? val.setBool(value) : val.setBool(0)
- when TYPE_FLOAT then val.setFloat(value)
- when TYPE_DOUBLE then val.setDouble(value)
- when TYPE_UUID then val.setUuid(value)
- when TYPE_INT8, TYPE_INT16, TYPE_INT32 then val.setInt(value)
- when TYPE_INT64 then val.setInt64(value)
+ when TYPE_UINT8, TYPE_UINT16, TYPE_UINT32 then val.setUint(v)
+ when TYPE_UINT64 then val.setUint64(v)
+ when TYPE_SSTR, TYPE_LSTR then v ? val.setString(v) : val.setString('')
+ when TYPE_ABSTIME then val.setInt64(v)
+ when TYPE_DELTATIME then val.setUint64(v)
+ when TYPE_REF then val.setObjectId(v.impl)
+ when TYPE_BOOL then v ? val.setBool(v) : val.setBool(0)
+ when TYPE_FLOAT then val.setFloat(v)
+ when TYPE_DOUBLE then val.setDouble(v)
+ when TYPE_UUID then val.setUuid(v)
+ when TYPE_INT8, TYPE_INT16, TYPE_INT32 then val.setInt(v)
+ when TYPE_INT64 then val.setInt64(v)
when TYPE_MAP
when TYPE_OBJECT
when TYPE_LIST
diff --git a/qpid/cpp/bindings/qmf/ruby/ruby.i b/qpid/cpp/bindings/qmf/ruby/ruby.i
index 76f8a18dcf..a8a2a87a97 100644
--- a/qpid/cpp/bindings/qmf/ruby/ruby.i
+++ b/qpid/cpp/bindings/qmf/ruby/ruby.i
@@ -18,7 +18,6 @@
*/
%include stl.i
-%trackobjects;
%module qmfengine
@@ -32,9 +31,19 @@
$result = (VALUE) $1;
}
+%typemap (in) uint16_t
+{
+ $1 = NUM2UINT ($input);
+}
+
+%typemap (out) uint16_t
+{
+ $result = UINT2NUM((unsigned short) $1);
+}
+
%typemap (in) uint32_t
{
- $1 = FIX2UINT ((uint32_t) $input);
+ $1 = NUM2UINT ($input);
}
%typemap (out) uint32_t
@@ -48,7 +57,7 @@
%typemap (in) uint64_t
{
- $1 = FIX2ULONG ((uint64_t) $input);
+ $1 = NUM2ULONG ($input);
}
%typemap (out) uint64_t
diff --git a/qpid/python/run-tests b/qpid/cpp/bindings/qmf/tests/Makefile.am
index 84b76ebfc1..1ff8a64bed 100755..100644
--- a/qpid/python/run-tests
+++ b/qpid/cpp/bindings/qmf/tests/Makefile.am
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
@@ -7,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
@@ -18,18 +17,5 @@
# under the License.
#
-import sys, logging
-from qpid.testlib import testrunner
-from qpid.log import enable, WARN, DEBUG
-
-if "-vv" in sys.argv:
- level = DEBUG
-else:
- level = WARN
-
-enable("qpid", level)
-
-if not testrunner.run(): sys.exit(1)
-
-
-
+TESTS = run_interop_tests
+EXTRA_DIST = run_interop_tests
diff --git a/qpid/cpp/bindings/qmf/tests/agent_ruby.rb b/qpid/cpp/bindings/qmf/tests/agent_ruby.rb
new file mode 100755
index 0000000000..d395810cb6
--- /dev/null
+++ b/qpid/cpp/bindings/qmf/tests/agent_ruby.rb
@@ -0,0 +1,99 @@
+#!/usr/bin/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.
+#
+
+require 'qmf'
+require 'socket'
+
+class Model
+ attr_reader :parent_class, :child_class
+
+ def initialize
+ @parent_class = Qmf::SchemaObjectClass.new("org.apache.qpid.qmf", "parent")
+ @parent_class.add_property(Qmf::SchemaProperty.new("name", Qmf::TYPE_SSTR, :index => true))
+ @parent_class.add_property(Qmf::SchemaProperty.new("state", Qmf::TYPE_SSTR))
+ @parent_class.add_property(Qmf::SchemaProperty.new("uint32val", Qmf::TYPE_UINT32))
+ @parent_class.add_statistic(Qmf::SchemaStatistic.new("queryCount", Qmf::TYPE_UINT32, :unit => "query", :desc => "Query count"))
+
+ method = Qmf::SchemaMethod.new("create_child", :desc => "Create a new child object")
+ method.add_argument(Qmf::SchemaArgument.new("child_name", Qmf::TYPE_LSTR, :dir => Qmf::DIR_IN))
+ method.add_argument(Qmf::SchemaArgument.new("child_ref", Qmf::TYPE_REF, :dir => Qmf::DIR_OUT))
+
+ @parent_class.add_method(method)
+
+ @child_class = Qmf::SchemaObjectClass.new("org.apache.qpid.qmf", "child")
+ @child_class.add_property(Qmf::SchemaProperty.new("name", Qmf::TYPE_SSTR, :index => true))
+ end
+
+ def register(agent)
+ agent.register_class(@parent_class)
+ agent.register_class(@child_class)
+ end
+end
+
+
+class App < Qmf::AgentHandler
+ def get_query(context, query, userId)
+# puts "Query: user=#{userId} context=#{context} class=#{query.class_name} object_num=#{query.object_id.object_num_low if query.object_id}"
+ #@parent.inc_attr("queryCount")
+ if query.class_name == 'parent'
+ @agent.query_response(context, @parent)
+ end
+ @agent.query_complete(context)
+ end
+
+ def method_call(context, name, object_id, args, userId)
+# puts "Method: user=#{userId} context=#{context} method=#{name} object_num=#{object_id.object_num_low if object_id} args=#{args}"
+ oid = @agent.alloc_object_id(2)
+ args['child_ref'] = oid
+ @child = Qmf::QmfObject.new(@model.child_class)
+ @child.set_attr("name", args.by_key("child_name"))
+ @child.set_object_id(oid)
+ @agent.method_response(context, 0, "OK", args)
+ end
+
+ def main
+ @settings = Qmf::ConnectionSettings.new
+ @settings.host = ARGV[0] if ARGV.size > 0
+ @settings.port = ARGV[1].to_i if ARGV.size > 1
+ @connection = Qmf::Connection.new(@settings)
+ @agent = Qmf::Agent.new(self)
+
+ @model = Model.new
+ @model.register(@agent)
+
+ @agent.set_connection(@connection)
+
+ @parent = Qmf::QmfObject.new(@model.parent_class)
+ @parent.set_attr("name", "Parent One")
+ @parent.set_attr("state", "OPERATIONAL")
+ @parent.set_attr("uint32val", 0xa5a5a5a5)
+
+ oid = @agent.alloc_object_id(1)
+ @parent.set_object_id(oid)
+
+ sleep
+ end
+end
+
+app = App.new
+app.main
+
+
diff --git a/qpid/cpp/bindings/qmf/tests/python_console.py b/qpid/cpp/bindings/qmf/tests/python_console.py
new file mode 100755
index 0000000000..7a8a2cb9fe
--- /dev/null
+++ b/qpid/cpp/bindings/qmf/tests/python_console.py
@@ -0,0 +1,59 @@
+#!/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.datatypes import Message
+from qpid.queue import Empty
+from time import sleep
+
+class QmfInteropTests(TestBase010):
+
+ def test_A_agent_presence(self):
+ self.startQmf();
+ qmf = self.qmf
+
+ agents = []
+ count = 0
+ while len(agents) == 0:
+ agents = qmf.getObjects(_class="agent")
+ sleep(1)
+ count += 1
+ if count > 5:
+ self.fail("Timed out waiting for remote agent")
+
+ def test_B_basic_types(self):
+ self.startQmf();
+ qmf = self.qmf
+
+ parents = qmf.getObjects(_class="parent")
+ self.assertEqual(len(parents), 1)
+ self.assertEqual(parents[0].uint32val, 0xA5A5A5A5)
+
+ def getProperty(self, msg, name):
+ for h in msg.headers:
+ if hasattr(h, name): return getattr(h, name)
+ return None
+
+ def getAppHeader(self, msg, name):
+ headers = self.getProperty(msg, "application_headers")
+ if headers:
+ return headers[name]
+ return None
diff --git a/qpid/cpp/bindings/qmf/tests/run_interop_tests b/qpid/cpp/bindings/qmf/tests/run_interop_tests
new file mode 100755
index 0000000000..e6fc872dbb
--- /dev/null
+++ b/qpid/cpp/bindings/qmf/tests/run_interop_tests
@@ -0,0 +1,67 @@
+#!/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 the qmf interoperability tests.
+MY_DIR=`dirname \`which $0\``
+QPID_DIR=${MY_DIR}/../../../..
+BUILD_DIR=../../..
+PYTHON_DIR=${QPID_DIR}/python
+BROKER_DIR=${BUILD_DIR}/src
+API_DIR=${BUILD_DIR}/bindings/qmf
+SPEC_DIR=${QPID_DIR}/specs
+
+trap stop_broker INT TERM QUIT
+
+start_broker() {
+ ${BROKER_DIR}/qpidd --daemon --port 0 --no-data-dir --no-module-dir --auth no > _qpidd.port
+ BROKER_PORT=`cat _qpidd.port`
+}
+
+stop_broker() {
+ ${BROKER_DIR}/qpidd -q --port $BROKER_PORT
+ echo "Broker stopped"
+}
+
+start_ruby_agent() {
+ ruby -I${MY_DIR}/../ruby -I${API_DIR}/ruby/.libs ${MY_DIR}/agent_ruby.rb localhost $BROKER_PORT &
+ AGENT_PID=$!
+}
+
+stop_ruby_agent() {
+ kill $AGENT_PID
+}
+
+if test -d ${PYTHON_DIR} ; then
+ start_broker
+ echo "Running qmf interop tests using broker on port $BROKER_PORT"
+ PYTHONPATH=${PYTHON_DIR}:${MY_DIR}
+ export PYTHONPATH
+ echo " Ruby Agent vs. Pure-Python Console"
+ start_ruby_agent
+ echo " Ruby agent started at pid $AGENT_PID"
+ ${PYTHON_DIR}/qpid-python-test -m python_console -b localhost:$BROKER_PORT $@
+ RETCODE=$?
+ stop_ruby_agent
+ stop_broker
+ if test x$RETCODE != x0; then
+ echo "FAIL qmf interop tests"; exit 1;
+ fi
+fi
diff --git a/qpid/cpp/boost-1.32-support/supressions b/qpid/cpp/boost-1.32-support/supressions
index df03360757..0747e32cc7 100644
--- a/qpid/cpp/boost-1.32-support/supressions
+++ b/qpid/cpp/boost-1.32-support/supressions
@@ -151,3 +151,641 @@
fun:_Znwm
fun:_ZN5boost9unit_test9test_caseC2ENS0_13basic_cstringIKcEEbmb
}
+
+{
+ dl_open_1
+ Memcheck:Leak
+ fun:*alloc
+ fun:_dl_new_object
+ fun:_dl_map_object_from_fd
+ fun:_dl_map_object
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_sasl_get_plugin
+ fun:_sasl_load_plugins
+ fun:sasl_client_init
+}
+{
+ dl_open_2
+ Memcheck:Leak
+ fun:*alloc
+ fun:_dl_*
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_sasl_get_plugin
+ fun:_sasl_load_plugins
+ fun:sasl_client_init
+}
+{
+ dl_open_3
+ Memcheck:Leak
+ fun:malloc
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_sasl_get_plugin
+ fun:_sasl_load_plugins
+ fun:sasl_client_init
+}
+{
+ dl_open_4
+ Memcheck:Leak
+ fun:*alloc
+ fun:_dl_new_object
+ fun:_dl_map_object_from_fd
+ fun:_dl_map_object
+ fun:openaux
+ fun:_dl_catch_error
+ fun:_dl_map_object_deps
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_sasl_get_plugin
+ fun:_sasl_load_plugins
+ fun:sasl_client_init
+}
+{
+ dl_open_5
+ Memcheck:Leak
+ fun:malloc
+ fun:_dl_map_object
+ fun:openaux
+ fun:_dl_catch_error
+ fun:_dl_map_object_deps
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_sasl_get_plugin
+ fun:_sasl_load_plugins
+ fun:sasl_client_init
+}
+{
+ dl_open_6
+ Memcheck:Leak
+ fun:malloc
+ fun:expand_dynamic_string_token
+ fun:_dl_map_object
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_sasl_get_plugin
+ fun:_sasl_load_plugins
+ fun:sasl_client_init
+}
+{
+ dl_open_7
+ Memcheck:Leak
+ fun:malloc
+ fun:_dl_map_object_deps
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ dl_open_8
+ Memcheck:Leak
+ fun:malloc
+ fun:_dl_map_object_deps
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:calloc
+ fun:_dlerror_run
+ fun:dlclose
+ fun:_sasl_done_with_plugins
+ fun:sasl_done
+ fun:__tcf_2
+ fun:__cxa_finalize
+}
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:calloc
+ fun:_dl_allocate_tls
+ fun:pthread_create@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys13ThreadPrivateC1EPNS0_8RunnableE
+ fun:_ZN4qpid3sys6ThreadC1EPNS0_8RunnableE
+ fun:_ZN4qpid6client12TCPConnector4initEv
+ fun:_ZN4qpid6client14ConnectionImpl4openEv
+ fun:_ZN4qpid6client10Connection4openERKNS0_18ConnectionSettingsE
+ fun:_ZN6ClientC2Ev
+ fun:main
+}
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:calloc
+ fun:_dl_allocate_tls
+ fun:pthread_create@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys13ThreadPrivateC1EPNS0_8RunnableE
+ fun:_ZN4qpid3sys6ThreadC1EPNS0_8RunnableE
+ fun:_ZN4qpid6client12TCPConnector4initEv
+ fun:_ZN4qpid6client14ConnectionImpl4openEv
+ fun:_ZN4qpid6client10Connection4openERKNS0_18ConnectionSettingsE
+ fun:main
+}
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:calloc
+ fun:_dl_allocate_tls
+ fun:pthread_create@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys13ThreadPrivateC1EPNS0_8RunnableE
+ fun:_ZN4qpid3sys6ThreadC1EPNS0_8RunnableE
+ fun:_ZN4qpid6client12TCPConnector4initEv
+ fun:_ZN4qpid6client14ConnectionImpl4openEv
+ fun:_ZN4qpid6client10Connection4openERKNS0_18ConnectionSettingsE
+ fun:_ZN4qpid6client10Connection4openERKSsiS3_S3_S3_t
+ fun:_ZN15ProxyConnectionC1Ei
+ fun:_ZN15SessionFixtureTI15ProxyConnectionN4qpid6client12Session_0_10EEC1ENS1_6broker6Broker7OptionsE
+ fun:_Z18DisconnectedListenv
+ 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
+}
+{
+ dl_open_9
+ Memcheck:Leak
+ fun:calloc
+ fun:_dl_check_map_versions
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ dl_open_10
+ Memcheck:Leak
+ fun:calloc
+ fun:_dl_new_object
+ fun:_dl_map_object_from_fd
+ fun:_dl_map_object
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ <insert a suppression name here>
+ Memcheck:Param
+ socketcall.sendto(msg)
+ fun:send
+ fun:get_mapping
+ fun:__nscd_get_map_ref
+ fun:nscd_gethst_r
+ fun:__nscd_gethostbyname2_r
+ fun:gethostbyname2_r@@GLIBC_2.2.5
+ fun:gaih_inet
+ fun:getaddrinfo
+ fun:_ZNK4qpid3sys6Socket7connectERKSst
+ fun:_ZN4qpid6client12TCPConnector7connectERKSsi
+ fun:_ZN4qpid6client14ConnectionImpl4openEv
+ fun:_ZN4qpid6client10Connection4openERKNS0_18ConnectionSettingsE
+ fun:main
+}
+{
+ <insert a suppression name here>
+ Memcheck:Param
+ socketcall.sendto(msg)
+ fun:send
+ fun:get_mapping
+ fun:__nscd_get_map_ref
+ fun:nscd_gethst_r
+ fun:__nscd_gethostbyname2_r
+ fun:gethostbyname2_r@@GLIBC_2.2.5
+ fun:gaih_inet
+ fun:getaddrinfo
+ fun:_ZNK4qpid3sys6Socket7connectERKSst
+ fun:_ZN20ClientSessionFixtureC1Ev
+ fun:_Z14testXmlBindingv
+}
+{
+ dl_open_11
+ Memcheck:Leak
+ fun:malloc
+ fun:decompose_rpath
+ fun:_dl_map_object
+ fun:openaux
+ fun:_dl_catch_error
+ fun:_dl_map_object_deps
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:calloc
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ dl_open_12
+ Memcheck:Leak
+ fun:malloc
+ fun:_dl_map_object
+ fun:openaux
+ fun:_dl_catch_error
+ fun:_dl_map_object_deps
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ dl_open_13
+ Memcheck:Leak
+ fun:malloc
+ fun:expand_dynamic_string_token
+ fun:_dl_map_object
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ dl_open_14
+ Memcheck:Leak
+ fun:malloc
+ fun:realloc
+ fun:_dl_new_object
+ fun:_dl_map_object_from_fd
+ fun:_dl_map_object
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ dl_open_15
+ Memcheck:Leak
+ fun:malloc
+ fun:_dl_new_object
+ fun:_dl_map_object_from_fd
+ fun:_dl_map_object
+ fun:openaux
+ fun:_dl_catch_error
+ fun:_dl_map_object_deps
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ dl_open_16
+ Memcheck:Leak
+ fun:malloc
+ fun:decompose_rpath
+ fun:_dl_map_object
+ fun:openaux
+ fun:_dl_catch_error
+ fun:_dl_map_object_deps
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:calloc
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ dl_open_17
+ Memcheck:Leak
+ fun:malloc
+ fun:_dl_map_object
+ fun:openaux
+ fun:_dl_catch_error
+ fun:_dl_map_object_deps
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ dl_open_18
+ Memcheck:Leak
+ fun:malloc
+ fun:expand_dynamic_string_token
+ fun:_dl_map_object
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ dl_open_19
+ Memcheck:Leak
+ fun:malloc
+ fun:realloc
+ fun:_dl_new_object
+ fun:_dl_map_object_from_fd
+ fun:_dl_map_object
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ dl_open_20
+ Memcheck:Leak
+ fun:malloc
+ fun:_dl_new_object
+ fun:_dl_map_object_from_fd
+ fun:_dl_map_object
+ fun:openaux
+ fun:_dl_catch_error
+ fun:_dl_map_object_deps
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:calloc
+ fun:_dl_allocate_tls
+ fun:pthread_create@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys13ThreadPrivateC1EPNS0_8RunnableE
+ fun:_ZN4qpid3sys6ThreadC1EPNS0_8RunnableE
+ fun:_ZN4qpid3sys5Timer5startEv
+ fun:_ZN4qpid3sys5TimerC1Ev
+ fun:_ZN4qpid6broker6BrokerC1ERKNS1_7OptionsE
+ fun:_ZN4qpid6broker6Broker6createERKNS1_7OptionsE
+ fun:_ZN20ClientSessionFixtureC1Ev
+ fun:_Z14testXmlBindingv
+}
+{
+ dl_open_21
+ Memcheck:Leak
+ fun:malloc
+ fun:decompose_rpath
+ fun:_dl_map_object
+ fun:openaux
+ fun:_dl_catch_error
+ fun:_dl_map_object_deps
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:calloc
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ dl_open_22
+ Memcheck:Leak
+ fun:malloc
+ fun:_dl_map_object
+ fun:openaux
+ fun:_dl_catch_error
+ fun:_dl_map_object_deps
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ dl_open_23
+ Memcheck:Leak
+ fun:malloc
+ fun:expand_dynamic_string_token
+ fun:_dl_map_object
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ dl_open_24
+ Memcheck:Leak
+ fun:malloc
+ fun:realloc
+ fun:_dl_new_object
+ fun:_dl_map_object_from_fd
+ fun:_dl_map_object
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ dl_open_25
+ Memcheck:Leak
+ fun:malloc
+ fun:_dl_new_object
+ fun:_dl_map_object_from_fd
+ fun:_dl_map_object
+ fun:openaux
+ fun:_dl_catch_error
+ fun:_dl_map_object_deps
+ fun:dl_open_worker
+ fun:_dl_catch_error
+ fun:_dl_open
+ fun:dlopen_doit
+ fun:_dl_catch_error
+ fun:_dlerror_run
+ fun:dlopen@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys5Shlib4loadEPKc
+ fun:_GLOBAL__I__ZN53_GLOBAL__N_XmlClientSessionTest.cpp_CF4CBC2E_2FCE92D42_1E
+}
+{
+ <insert a suppression name here>
+ Memcheck:Param
+ socketcall.sendto(msg)
+ fun:send
+ fun:get_mapping
+ fun:__nscd_get_map_ref
+ fun:nscd_gethst_r
+ fun:__nscd_gethostbyname2_r
+ fun:gethostbyname2_r@@GLIBC_2.2.5
+ fun:gaih_inet
+ fun:getaddrinfo
+ fun:_ZNK4qpid3sys6Socket7connectERKSst
+ fun:_ZN4qpid6client12TCPConnector7connectERKSsi
+ fun:_ZN4qpid6client14ConnectionImpl4openEv
+ fun:_ZN4qpid6client10Connection4openERKNS0_18ConnectionSettingsE
+ fun:_ZN6ClientC2Ev
+ fun:main
+}
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:calloc
+ fun:_dlerror_run
+ fun:dlclose
+ fun:_sasl_done_with_plugins
+ fun:sasl_done
+ fun:__tcf_2
+ fun:__cxa_finalize
+}
+{
+ <insert a suppression name here>
+ Memcheck:Leak
+ fun:calloc
+ fun:_dl_allocate_tls
+ fun:pthread_create@@GLIBC_2.2.5
+ fun:_ZN4qpid3sys13ThreadPrivateC1EPNS0_8RunnableE
+ fun:_ZN4qpid3sys6ThreadC1EPNS0_8RunnableE
+ fun:_ZN4qpid6client12TCPConnector4initEv
+ fun:_ZN4qpid6client14ConnectionImpl4openEv
+ fun:_ZN4qpid6client10Connection4openERKNS0_18ConnectionSettingsE
+ fun:_ZN6ClientC2Ev
+ fun:main
+}
+{
+ <insert a suppression name here>
+ Memcheck:Param
+ socketcall.sendto(msg)
+ fun:send
+ fun:get_mapping
+ fun:__nscd_get_map_ref
+ fun:nscd_gethst_r
+ fun:__nscd_gethostbyname2_r
+ fun:gethostbyname2_r@@GLIBC_2.2.5
+ fun:gaih_inet
+ fun:getaddrinfo
+ fun:_ZNK4qpid3sys6Socket7connectERKSst
+}
diff --git a/qpid/cpp/configure.ac b/qpid/cpp/configure.ac
index 6d7ed16871..adf3da06b8 100644
--- a/qpid/cpp/configure.ac
+++ b/qpid/cpp/configure.ac
@@ -514,6 +514,7 @@ AC_CONFIG_FILES([
examples/tradedemo/Makefile
bindings/qmf/Makefile
bindings/qmf/ruby/Makefile
+ bindings/qmf/tests/Makefile
managementgen/Makefile
etc/Makefile
src/Makefile
diff --git a/qpid/cpp/examples/qmf-agent/Makefile b/qpid/cpp/examples/qmf-agent/Makefile
index 7e460bcd16..e652edb1a2 100644
--- a/qpid/cpp/examples/qmf-agent/Makefile
+++ b/qpid/cpp/examples/qmf-agent/Makefile
@@ -25,7 +25,7 @@ OUT_FILE = $(SRC_DIR)/qmf-agent
CC = gcc
LIB_DIR = $(QPID_DIR)/cpp/src/.libs
-CC_INCLUDES = -I$(SRC_DIR) -I$(QPID_DIR)/cpp/src -I$(QPID_DIR)/cpp/src/gen -I$(GEN_DIR)
+CC_INCLUDES = -I$(SRC_DIR) -I$(QPID_DIR)/cpp/include -I$(GEN_DIR)
CC_FLAGS = -g -O3
LD_FLAGS = -lqmfagent -lqmfcommon -L$(LIB_DIR)
SPEC_DIR = $(QPID_DIR)/specs
diff --git a/qpid/cpp/include/qpid/Msg.h b/qpid/cpp/include/qpid/Msg.h
index 5149a94d7f..6e08a89571 100644
--- a/qpid/cpp/include/qpid/Msg.h
+++ b/qpid/cpp/include/qpid/Msg.h
@@ -43,14 +43,29 @@ struct Msg {
Msg(const Msg& m) : os(m.str()) {}
std::string str() const { return os.str(); }
operator std::string() const { return str(); }
+
+ Msg& operator<<(long n) { os << n; return *this; }
+ Msg& operator<<(unsigned long n) { os << n; return *this; }
+ Msg& operator<<(bool n) { os << n; return *this; }
+ Msg& operator<<(short n) { os << n; return *this; }
+ Msg& operator<<(unsigned short n) { os << n; return *this; }
+ Msg& operator<<(int n) { os << n; return *this; }
+ Msg& operator<<(unsigned int n) { os << n; return *this; }
+#ifdef _GLIBCXX_USE_LONG_LONG
+ Msg& operator<<(long long n) { os << n; return *this; }
+ Msg& operator<<(unsigned long long n) { os << n; return *this; }
+#endif
+ Msg& operator<<(double n) { os << n; return *this; }
+ Msg& operator<<(float n) { os << n; return *this; }
+ Msg& operator<<(long double n) { os << n; return *this; }
+
+ template <class T> Msg& operator<<(const T& t) { os <<t; return *this; }
};
-template <class T> const Msg& operator<<(const Msg& m, const T& t) {
- const_cast<std::ostringstream&>(m.os)<<t; return m;
-}
+
inline std::ostream& operator<<(std::ostream& o, const Msg& m) {
- return o<<m.str();
+ return o << m.str();
}
/** Construct a message using operator << and append (file:line) */
diff --git a/qpid/cpp/src/CMakeLists.txt b/qpid/cpp/src/CMakeLists.txt
index 3739e22cd1..a1d01ceca4 100644
--- a/qpid/cpp/src/CMakeLists.txt
+++ b/qpid/cpp/src/CMakeLists.txt
@@ -533,7 +533,6 @@ set (libqpidbroker_SOURCES
qpid/amqp_0_10/Connection.h
qpid/amqp_0_10/Connection.cpp
qpid/broker/Broker.cpp
- qpid/broker/BrokerSingleton.cpp
qpid/broker/Exchange.cpp
qpid/broker/ExpiryPolicy.cpp
qpid/broker/Queue.cpp
@@ -586,7 +585,6 @@ set (libqpidbroker_SOURCES
qpid/broker/SessionHandler.h
qpid/broker/SessionHandler.cpp
qpid/broker/System.cpp
- qpid/broker/Timer.cpp
qpid/broker/TopicExchange.cpp
qpid/broker/TxAccept.cpp
qpid/broker/TxBuffer.cpp
diff --git a/qpid/cpp/src/Makefile.am b/qpid/cpp/src/Makefile.am
index 1d5e52408a..0eff526203 100644
--- a/qpid/cpp/src/Makefile.am
+++ b/qpid/cpp/src/Makefile.am
@@ -115,6 +115,7 @@ INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(srcdir) -I=$(bu
# Destination for intalled programs and tests defined here
#
qpidexecdir = $(libexecdir)/qpid
+AM_CXXFLAGS += -DQPID_EXEC_DIR=\"$(qpidexecdir)\"
qpidexec_PROGRAMS =
qpidexec_SCRIPTS =
qpidtestdir = $(qpidexecdir)/tests
@@ -376,6 +377,7 @@ libqpidcommon_la_SOURCES += \
qpid/framing/InitiationHandler.h \
qpid/framing/InputHandler.h \
qpid/framing/Invoker.h \
+ qpid/framing/IsInSequenceSet.h \
qpid/framing/MethodBodyFactory.h \
qpid/framing/MethodContent.h \
qpid/framing/ModelMethod.h \
@@ -406,6 +408,8 @@ libqpidcommon_la_SOURCES += \
qpid/log/OstreamOutput.h \
qpid/log/Selector.cpp \
qpid/log/Statement.cpp \
+ qpid/management/Manageable.cpp \
+ qpid/management/ManagementObject.cpp \
qpid/memory.h \
qpid/pointer_to_other.h \
qpid/ptr_map.h \
@@ -477,8 +481,6 @@ libqpidbroker_la_SOURCES = \
qpid/broker/Broker.cpp \
qpid/broker/Broker.h \
qpid/broker/BrokerImportExport.h \
- qpid/broker/BrokerSingleton.cpp \
- qpid/broker/BrokerSingleton.h \
qpid/broker/Connection.cpp \
qpid/broker/Connection.h \
qpid/broker/ConnectionFactory.cpp \
@@ -601,8 +603,6 @@ libqpidbroker_la_SOURCES = \
qpid/broker/SignalHandler.h \
qpid/broker/System.cpp \
qpid/broker/System.h \
- qpid/broker/Timer.cpp \
- qpid/broker/Timer.h \
qpid/broker/TopicExchange.cpp \
qpid/broker/TopicExchange.h \
qpid/broker/TransactionalStore.h \
@@ -616,13 +616,11 @@ libqpidbroker_la_SOURCES = \
qpid/broker/TxPublish.h \
qpid/broker/Vhost.cpp \
qpid/broker/Vhost.h \
- qpid/management/IdAllocator.h \
- qpid/management/Manageable.cpp \
+ qpid/management/IdAllocator.h \
qpid/management/ManagementAgent.cpp \
- qpid/management/ManagementAgent.h \
+ qpid/management/ManagementAgent.h \
qpid/management/ManagementExchange.cpp \
- qpid/management/ManagementExchange.h \
- qpid/management/ManagementObject.cpp \
+ qpid/management/ManagementExchange.h \
qpid/sys/TCPIOPlugin.cpp
diff --git a/qpid/cpp/src/cluster.mk b/qpid/cpp/src/cluster.mk
index c4907a1b04..d90a06e1e2 100644
--- a/qpid/cpp/src/cluster.mk
+++ b/qpid/cpp/src/cluster.mk
@@ -89,4 +89,13 @@ cluster_la_LIBADD= -lcpg $(libcman) libqpidbroker.la libqpidclient.la
cluster_la_CXXFLAGS = $(AM_CXXFLAGS) -fno-strict-aliasing
cluster_la_LDFLAGS = $(PLUGINLDFLAGS)
+# The watchdog plugin and helper executable
+dmodule_LTLIBRARIES += watchdog.la
+watchdog_la_SOURCES = qpid/cluster/WatchDogPlugin.cpp
+watchdog_la_LIBADD = libqpidbroker.la
+watchdog_la_LDFLAGS = $(PLUGINLDFLAGS)
+
+qpidexec_PROGRAMS += qpidd_watchdog
+qpidd_watchdog_SOURCES = qpid/cluster/qpidd_watchdog.cpp
+
endif # HAVE_LIBCPG
diff --git a/qpid/cpp/src/qmf/Agent.cpp b/qpid/cpp/src/qmf/Agent.cpp
index 64d34f7ecd..6d59ae2750 100644
--- a/qpid/cpp/src/qmf/Agent.cpp
+++ b/qpid/cpp/src/qmf/Agent.cpp
@@ -239,7 +239,6 @@ AgentImpl::AgentImpl(char* _label, bool i) :
AgentImpl::~AgentImpl()
{
- cout << "AgentImpl::~AgentImpl" << endl;
}
void AgentImpl::setStoreDir(char* path)
@@ -859,7 +858,6 @@ Agent::Agent(char* label, bool internalStore)
Agent::~Agent()
{
- cout << "Agent::~Agent" << endl;
delete impl;
}
diff --git a/qpid/cpp/src/qpid/broker/Broker.cpp b/qpid/cpp/src/qpid/broker/Broker.cpp
index a937a6b9b2..8b6cb5e049 100644
--- a/qpid/cpp/src/qpid/broker/Broker.cpp
+++ b/qpid/cpp/src/qpid/broker/Broker.cpp
@@ -223,7 +223,7 @@ Broker::Broker(const Broker::Options& conf) :
}
else {
QPID_LOG(notice, "Cluster recovery: recovered journal data discarded and journal files pushed down");
- store->discardInit(true);
+ store->truncateInit(true); // save old files in subdir
}
}
diff --git a/qpid/cpp/src/qpid/broker/BrokerSingleton.cpp b/qpid/cpp/src/qpid/broker/BrokerSingleton.cpp
deleted file mode 100644
index 9dfdd1f6b8..0000000000
--- a/qpid/cpp/src/qpid/broker/BrokerSingleton.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- *
- * Copyright (c) 2006 The Apache Software Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#include "qpid/broker/BrokerSingleton.h"
-
-namespace qpid {
-namespace broker {
-
-BrokerSingleton::BrokerSingleton() {
- if (broker.get() == 0)
- broker = Broker::create();
- boost::intrusive_ptr<Broker>::operator=(broker);
-}
-
-BrokerSingleton::~BrokerSingleton() {
- broker->shutdown();
-}
-
-boost::intrusive_ptr<Broker> BrokerSingleton::broker;
-
-}} // namespace qpid::broker
diff --git a/qpid/cpp/src/qpid/broker/BrokerSingleton.h b/qpid/cpp/src/qpid/broker/BrokerSingleton.h
deleted file mode 100644
index 44a9b8b77c..0000000000
--- a/qpid/cpp/src/qpid/broker/BrokerSingleton.h
+++ /dev/null
@@ -1,53 +0,0 @@
-#ifndef _broker_BrokerSingleton_h
-#define _broker_BrokerSingleton_h
-
-/*
- *
- * Copyright (c) 2006 The Apache Software Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-#include "qpid/broker/Broker.h"
-#include "qpid/broker/BrokerImportExport.h"
-
-namespace qpid {
-namespace broker {
-
-/**
- * BrokerSingleton is a smart pointer to a process-wide singleton broker
- * started on an os-chosen port. The broker starts the first time
- * an instance of BrokerSingleton is created and runs untill the process exits.
- *
- * Useful for unit tests that want to share a broker between multiple
- * tests to reduce overhead of starting/stopping a broker for every test.
- *
- * Tests that need a new broker can create it directly.
- *
- * THREAD UNSAFE.
- */
-class QPID_BROKER_EXTERN BrokerSingleton : public boost::intrusive_ptr<Broker>
-{
- public:
- BrokerSingleton();
- ~BrokerSingleton();
- private:
- static boost::intrusive_ptr<Broker> broker;
-};
-
-}} // namespace qpid::broker
-
-
-
-#endif /*!_broker_BrokerSingleton_h*/
diff --git a/qpid/cpp/src/qpid/broker/DirectExchange.cpp b/qpid/cpp/src/qpid/broker/DirectExchange.cpp
index 454b497f11..b9f24dee5f 100644
--- a/qpid/cpp/src/qpid/broker/DirectExchange.cpp
+++ b/qpid/cpp/src/qpid/broker/DirectExchange.cpp
@@ -92,12 +92,24 @@ bool DirectExchange::bind(Queue::shared_ptr queue, const string& routingKey, con
if (bk.fedBinding.count() == 0)
unbind(queue, routingKey, 0);
} else if (fedOp == fedOpReorigin) {
- for (std::map<string, BoundKey>::iterator iter = bindings.begin();
- iter != bindings.end(); iter++) {
- const BoundKey& bk = iter->second;
- if (bk.fedBinding.hasLocal()) {
- propagateFedOp(iter->first, string(), fedOpBind, string());
+ /** gather up all the keys that need rebinding in a local vector
+ * while holding the lock. Then propagate once the lock is
+ * released
+ */
+ std::vector<std::string> keys2prop;
+ {
+ Mutex::ScopedLock l(lock);
+ for (Bindings::iterator iter = bindings.begin();
+ iter != bindings.end(); iter++) {
+ const BoundKey& bk = iter->second;
+ if (bk.fedBinding.hasLocal()) {
+ keys2prop.push_back(iter->first);
+ }
}
+ } /* lock dropped */
+ for (std::vector<std::string>::const_iterator key = keys2prop.begin();
+ key != keys2prop.end(); key++) {
+ propagateFedOp( *key, string(), fedOpBind, string());
}
}
diff --git a/qpid/cpp/src/qpid/broker/Link.cpp b/qpid/cpp/src/qpid/broker/Link.cpp
index 061e627f47..9ce0c710bd 100644
--- a/qpid/cpp/src/qpid/broker/Link.cpp
+++ b/qpid/cpp/src/qpid/broker/Link.cpp
@@ -174,18 +174,24 @@ void Link::closed (int, std::string text)
destroy();
}
+void Link::checkClosePermission()
+{
+ Mutex::ScopedLock mutex(lock);
+
+ AclModule* acl = getBroker()->getAcl();
+ std::string userID = getUsername() + "@" + getBroker()->getOptions().realm;
+ if (acl && !acl->authorise(userID,acl::ACT_DELETE,acl::OBJ_LINK,"")){
+ throw NotAllowedException("ACL denied delete link request");
+ }
+}
+
+
void Link::destroy ()
{
Bridges toDelete;
{
Mutex::ScopedLock mutex(lock);
- AclModule* acl = getBroker()->getAcl();
- std::string userID = getUsername() + "@" + getBroker()->getOptions().realm;
- if (acl && !acl->authorise(userID,acl::ACT_DELETE,acl::OBJ_LINK,"")){
- throw NotAllowedException("ACL denied delete link request");
- }
-
QPID_LOG (info, "Inter-broker link to " << host << ":" << port << " removed by management");
if (connection)
connection->close(CLOSE_CODE_CONNECTION_FORCED, "closed by management");
@@ -412,9 +418,14 @@ Manageable::status_t Link::ManagementMethod (uint32_t op, Args& args, string& te
switch (op)
{
case _qmf::Link::METHOD_CLOSE :
- closing = true;
- if (state != STATE_CONNECTING)
- destroy();
+ checkClosePermission();
+ if (!closing) {
+ closing = true;
+ if (state != STATE_CONNECTING && connection) {
+ //connection can only be closed on the connections own IO processing thread
+ connection->requestIOProcessing(boost::bind(&Link::destroy, this));
+ }
+ }
return Manageable::STATUS_OK;
case _qmf::Link::METHOD_BRIDGE :
diff --git a/qpid/cpp/src/qpid/broker/Link.h b/qpid/cpp/src/qpid/broker/Link.h
index 12f03095f0..318eb5bd32 100644
--- a/qpid/cpp/src/qpid/broker/Link.h
+++ b/qpid/cpp/src/qpid/broker/Link.h
@@ -85,7 +85,8 @@ namespace qpid {
void startConnectionLH(); // Start the IO Connection
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 tryFailover(); // Called during maintenance visit
+ void checkClosePermission(); // ACL check for explict mgmt call to close this link
public:
typedef boost::shared_ptr<Link> shared_ptr;
diff --git a/qpid/cpp/src/qpid/broker/LinkRegistry.cpp b/qpid/cpp/src/qpid/broker/LinkRegistry.cpp
index c70392eb23..f32587dd68 100644
--- a/qpid/cpp/src/qpid/broker/LinkRegistry.cpp
+++ b/qpid/cpp/src/qpid/broker/LinkRegistry.cpp
@@ -49,10 +49,18 @@ LinkRegistry::LinkRegistry () :
LinkRegistry::LinkRegistry (Broker* _broker) :
broker(_broker), timer(&broker->getTimer()),
+ maintenanceTask(new Periodic(*this)),
parent(0), store(0), passive(false), passiveChanged(false),
realm(broker->getOptions().realm)
{
- timer->add (new Periodic(*this));
+ timer->add(maintenanceTask);
+}
+
+LinkRegistry::~LinkRegistry()
+{
+ // This test is only necessary if the default constructor above is present
+ if (maintenanceTask)
+ maintenanceTask->cancel();
}
LinkRegistry::Periodic::Periodic (LinkRegistry& _links) :
@@ -61,7 +69,8 @@ LinkRegistry::Periodic::Periodic (LinkRegistry& _links) :
void LinkRegistry::Periodic::fire ()
{
links.periodicMaintenance ();
- links.timer->add (new Periodic(links));
+ setupNextFire();
+ links.timer->add(this);
}
void LinkRegistry::periodicMaintenance ()
diff --git a/qpid/cpp/src/qpid/broker/LinkRegistry.h b/qpid/cpp/src/qpid/broker/LinkRegistry.h
index d1a4201c82..09a89298b6 100644
--- a/qpid/cpp/src/qpid/broker/LinkRegistry.h
+++ b/qpid/cpp/src/qpid/broker/LinkRegistry.h
@@ -30,6 +30,7 @@
#include "qpid/sys/Timer.h"
#include "qpid/management/Manageable.h"
#include <boost/shared_ptr.hpp>
+#include <boost/intrusive_ptr.hpp>
namespace qpid {
namespace broker {
@@ -63,6 +64,7 @@ namespace broker {
qpid::sys::Mutex lock;
Broker* broker;
sys::Timer* timer;
+ boost::intrusive_ptr<qpid::sys::TimerTask> maintenanceTask;
management::Manageable* parent;
MessageStore* store;
bool passive;
@@ -77,6 +79,8 @@ namespace broker {
public:
LinkRegistry (); // Only used in store tests
LinkRegistry (Broker* _broker);
+ ~LinkRegistry();
+
std::pair<boost::shared_ptr<Link>, bool>
declare(std::string& host,
uint16_t port,
diff --git a/qpid/cpp/src/qpid/broker/MessageStore.h b/qpid/cpp/src/qpid/broker/MessageStore.h
index 363a474c22..f5c55a50f8 100644
--- a/qpid/cpp/src/qpid/broker/MessageStore.h
+++ b/qpid/cpp/src/qpid/broker/MessageStore.h
@@ -63,7 +63,7 @@ class MessageStore : public TransactionalStore, public Recoverable {
* @param pushDownStoreFiles If true, will move content of the store dir into a
* subdir, leaving the store dir otherwise empty.
*/
- virtual void discardInit(const bool pushDownStoreFiles = false) = 0;
+ virtual void truncateInit(const bool pushDownStoreFiles = false) = 0;
/**
* Record the existence of a durable queue
diff --git a/qpid/cpp/src/qpid/broker/MessageStoreModule.cpp b/qpid/cpp/src/qpid/broker/MessageStoreModule.cpp
index bf2a27b539..0b8a5db1c7 100644
--- a/qpid/cpp/src/qpid/broker/MessageStoreModule.cpp
+++ b/qpid/cpp/src/qpid/broker/MessageStoreModule.cpp
@@ -41,9 +41,9 @@ MessageStoreModule::~MessageStoreModule()
bool MessageStoreModule::init(const Options*) { return true; }
-void MessageStoreModule::discardInit(const bool pushDownStoreFiles)
+void MessageStoreModule::truncateInit(const bool pushDownStoreFiles)
{
- TRANSFER_EXCEPTION(store->discardInit(pushDownStoreFiles));
+ TRANSFER_EXCEPTION(store->truncateInit(pushDownStoreFiles));
}
void MessageStoreModule::create(PersistableQueue& queue, const FieldTable& args)
diff --git a/qpid/cpp/src/qpid/broker/MessageStoreModule.h b/qpid/cpp/src/qpid/broker/MessageStoreModule.h
index d6963336fc..02cbd13cf1 100644
--- a/qpid/cpp/src/qpid/broker/MessageStoreModule.h
+++ b/qpid/cpp/src/qpid/broker/MessageStoreModule.h
@@ -40,7 +40,7 @@ class MessageStoreModule : public MessageStore
MessageStoreModule(MessageStore* store);
bool init(const Options* options);
- void discardInit(const bool pushDownStoreFiles = false);
+ void truncateInit(const bool pushDownStoreFiles = false);
std::auto_ptr<TransactionContext> begin();
std::auto_ptr<TPCTransactionContext> begin(const std::string& xid);
void prepare(TPCTransactionContext& txn);
diff --git a/qpid/cpp/src/qpid/broker/NullMessageStore.cpp b/qpid/cpp/src/qpid/broker/NullMessageStore.cpp
index 87431453af..6339b655f8 100644
--- a/qpid/cpp/src/qpid/broker/NullMessageStore.cpp
+++ b/qpid/cpp/src/qpid/broker/NullMessageStore.cpp
@@ -54,7 +54,7 @@ NullMessageStore::NullMessageStore() : nextPersistenceId(1) {
bool NullMessageStore::init(const Options* /*options*/) {return true;}
-void NullMessageStore::discardInit(const bool /*pushDownStoreFiles*/) {}
+void NullMessageStore::truncateInit(const bool /*pushDownStoreFiles*/) {}
void NullMessageStore::create(PersistableQueue& queue, const framing::FieldTable& /*args*/)
{
diff --git a/qpid/cpp/src/qpid/broker/NullMessageStore.h b/qpid/cpp/src/qpid/broker/NullMessageStore.h
index 48d0e1ea22..e148ec4d51 100644
--- a/qpid/cpp/src/qpid/broker/NullMessageStore.h
+++ b/qpid/cpp/src/qpid/broker/NullMessageStore.h
@@ -42,7 +42,7 @@ class NullMessageStore : public MessageStore
QPID_BROKER_EXTERN NullMessageStore();
QPID_BROKER_EXTERN virtual bool init(const Options* options);
- QPID_BROKER_EXTERN virtual void discardInit(const bool pushDownStoreFiles = false);
+ QPID_BROKER_EXTERN virtual void truncateInit(const bool pushDownStoreFiles = false);
QPID_BROKER_EXTERN virtual std::auto_ptr<TransactionContext> begin();
QPID_BROKER_EXTERN virtual std::auto_ptr<TPCTransactionContext> begin(const std::string& xid);
QPID_BROKER_EXTERN virtual void prepare(TPCTransactionContext& txn);
diff --git a/qpid/cpp/src/qpid/broker/Queue.h b/qpid/cpp/src/qpid/broker/Queue.h
index 6703d06bbb..77799fd967 100644
--- a/qpid/cpp/src/qpid/broker/Queue.h
+++ b/qpid/cpp/src/qpid/broker/Queue.h
@@ -323,10 +323,10 @@ namespace qpid {
* Must be >= the current sequence number.
* Used by cluster to replicate queues.
*/
- void setPosition(framing::SequenceNumber pos);
+ QPID_BROKER_EXTERN void setPosition(framing::SequenceNumber pos);
/** return current position sequence number for the next message on the queue.
*/
- framing::SequenceNumber getPosition();
+ QPID_BROKER_EXTERN framing::SequenceNumber getPosition();
int getEventMode();
void setQueueEventManager(QueueEvents&);
QPID_BROKER_EXTERN void insertSequenceNumbers(const std::string& key);
diff --git a/qpid/cpp/src/qpid/broker/QueueCleaner.cpp b/qpid/cpp/src/qpid/broker/QueueCleaner.cpp
index 83f3f83520..c80fe89035 100644
--- a/qpid/cpp/src/qpid/broker/QueueCleaner.cpp
+++ b/qpid/cpp/src/qpid/broker/QueueCleaner.cpp
@@ -28,6 +28,11 @@ namespace broker {
QueueCleaner::QueueCleaner(QueueRegistry& q, sys::Timer& t) : queues(q), timer(t) {}
+QueueCleaner::~QueueCleaner()
+{
+ if (task) task->cancel();
+}
+
void QueueCleaner::start(qpid::sys::Duration p)
{
task = new Task(*this, p);
diff --git a/qpid/cpp/src/qpid/broker/QueueCleaner.h b/qpid/cpp/src/qpid/broker/QueueCleaner.h
index c351cadd8a..11c2d180ac 100644
--- a/qpid/cpp/src/qpid/broker/QueueCleaner.h
+++ b/qpid/cpp/src/qpid/broker/QueueCleaner.h
@@ -36,6 +36,7 @@ class QueueCleaner
{
public:
QPID_BROKER_EXTERN QueueCleaner(QueueRegistry& queues, sys::Timer& timer);
+ QPID_BROKER_EXTERN ~QueueCleaner();
QPID_BROKER_EXTERN void start(qpid::sys::Duration period);
private:
class Task : public sys::TimerTask
diff --git a/qpid/cpp/src/qpid/broker/SemanticState.cpp b/qpid/cpp/src/qpid/broker/SemanticState.cpp
index 37a9e9b4af..bdd5f33601 100644
--- a/qpid/cpp/src/qpid/broker/SemanticState.cpp
+++ b/qpid/cpp/src/qpid/broker/SemanticState.cpp
@@ -31,6 +31,8 @@
#include "qpid/broker/TxPublish.h"
#include "qpid/framing/reply_exceptions.h"
#include "qpid/framing/MessageTransferBody.h"
+#include "qpid/framing/SequenceSet.h"
+#include "qpid/framing/IsInSequenceSet.h"
#include "qpid/log/Statement.h"
#include "qpid/ptr_map.h"
#include "qpid/broker/AclModule.h"
@@ -49,8 +51,9 @@
namespace qpid {
namespace broker {
-using std::mem_fun_ref;
+using namespace std;
using boost::intrusive_ptr;
+using boost::bind;
using namespace qpid::broker;
using namespace qpid::framing;
using namespace qpid::sys;
@@ -631,13 +634,27 @@ void SemanticState::ConsumerImpl::notify()
}
-void SemanticState::accepted(DeliveryId first, DeliveryId last)
-{
- AckRange range = findRange(first, last);
+// Test that a DeliveryRecord's ID is in a sequence set and some other
+// predicate on DeliveryRecord holds.
+template <class Predicate> struct IsInSequenceSetAnd {
+ IsInSequenceSet isInSet;
+ Predicate predicate;
+ IsInSequenceSetAnd(const SequenceSet& s, Predicate p) : isInSet(s), predicate(p) {}
+ bool operator()(DeliveryRecord& dr) {
+ return isInSet(dr.getId()) && predicate(dr);
+ }
+};
+
+template<class Predicate> IsInSequenceSetAnd<Predicate>
+isInSequenceSetAnd(const SequenceSet& s, Predicate p) {
+ return IsInSequenceSetAnd<Predicate>(s,p);
+}
+
+void SemanticState::accepted(const SequenceSet& commands) {
if (txBuffer.get()) {
//in transactional mode, don't dequeue or remove, just
//maintain set of acknowledged messages:
- accumulatedAck.add(first, last);
+ accumulatedAck.add(commands);
if (dtxBuffer.get()) {
//if enlisted in a dtx, copy the relevant slice from
@@ -649,21 +666,28 @@ void SemanticState::accepted(DeliveryId first, DeliveryId last)
//mark the relevant messages as 'ended' in unacked
//if the messages are already completed, they can be
//removed from the record
- DeliveryRecords::iterator removed = remove_if(range.start, range.end, mem_fun_ref(&DeliveryRecord::setEnded));
- unacked.erase(removed, range.end);
+ DeliveryRecords::iterator removed =
+ remove_if(unacked.begin(), unacked.end(),
+ isInSequenceSetAnd(commands,
+ bind(&DeliveryRecord::setEnded, _1)));
+ unacked.erase(removed, unacked.end());
}
} else {
- DeliveryRecords::iterator removed = remove_if(range.start, range.end, boost::bind(&DeliveryRecord::accept, _1, (TransactionContext*) 0));
- unacked.erase(removed, range.end);
+ DeliveryRecords::iterator removed =
+ remove_if(unacked.begin(), unacked.end(),
+ isInSequenceSetAnd(commands,
+ bind(&DeliveryRecord::accept, _1,
+ (TransactionContext*) 0)));
+ unacked.erase(removed, unacked.end());
}
}
-void SemanticState::completed(DeliveryId first, DeliveryId last)
-{
- AckRange range = findRange(first, last);
-
- DeliveryRecords::iterator removed = remove_if(range.start, range.end, boost::bind(&SemanticState::complete, this, _1));
- unacked.erase(removed, range.end);
+void SemanticState::completed(const SequenceSet& commands) {
+ DeliveryRecords::iterator removed =
+ remove_if(unacked.begin(), unacked.end(),
+ isInSequenceSetAnd(commands,
+ bind(&SemanticState::complete, this, _1)));
+ unacked.erase(removed, unacked.end());
requestDispatch();
}
diff --git a/qpid/cpp/src/qpid/broker/SemanticState.h b/qpid/cpp/src/qpid/broker/SemanticState.h
index 4b87c59c68..da8383fc12 100644
--- a/qpid/cpp/src/qpid/broker/SemanticState.h
+++ b/qpid/cpp/src/qpid/broker/SemanticState.h
@@ -206,9 +206,8 @@ class SemanticState : private boost::noncopyable {
void reject(DeliveryId first, DeliveryId last);
void handle(boost::intrusive_ptr<Message> msg);
- //final 0-10 spec (completed and accepted are distinct):
- void completed(DeliveryId deliveryTag, DeliveryId endTag);
- void accepted(DeliveryId deliveryTag, DeliveryId endTag);
+ void completed(const framing::SequenceSet& commands);
+ void accepted(const framing::SequenceSet& commands);
void attached();
void detached();
diff --git a/qpid/cpp/src/qpid/broker/SessionAdapter.cpp b/qpid/cpp/src/qpid/broker/SessionAdapter.cpp
index 8eed673add..b0c5e9ea00 100644
--- a/qpid/cpp/src/qpid/broker/SessionAdapter.cpp
+++ b/qpid/cpp/src/qpid/broker/SessionAdapter.cpp
@@ -429,13 +429,11 @@ void SessionAdapter::QueueHandlerImpl::delete_(const string& queue, bool ifUnuse
}
}
-
SessionAdapter::MessageHandlerImpl::MessageHandlerImpl(SemanticState& s) :
HandlerHelper(s),
releaseRedeliveredOp(boost::bind(&SemanticState::release, &state, _1, _2, true)),
releaseOp(boost::bind(&SemanticState::release, &state, _1, _2, false)),
- rejectOp(boost::bind(&SemanticState::reject, &state, _1, _2)),
- acceptOp(boost::bind(&SemanticState::accepted, &state, _1, _2))
+ rejectOp(boost::bind(&SemanticState::reject, &state, _1, _2))
{}
//
@@ -547,8 +545,7 @@ void SessionAdapter::MessageHandlerImpl::stop(const std::string& destination)
void SessionAdapter::MessageHandlerImpl::accept(const framing::SequenceSet& commands)
{
-
- commands.for_each(acceptOp);
+ state.accepted(commands);
}
framing::MessageAcquireResult SessionAdapter::MessageHandlerImpl::acquire(const framing::SequenceSet& transfers)
diff --git a/qpid/cpp/src/qpid/broker/SessionState.cpp b/qpid/cpp/src/qpid/broker/SessionState.cpp
index d4e5cfaa67..4c5aaf7fc4 100644
--- a/qpid/cpp/src/qpid/broker/SessionState.cpp
+++ b/qpid/cpp/src/qpid/broker/SessionState.cpp
@@ -357,8 +357,7 @@ void SessionState::sendCompletion() {
void SessionState::senderCompleted(const SequenceSet& commands) {
qpid::SessionState::senderCompleted(commands);
- for (SequenceSet::RangeIterator i = commands.rangesBegin(); i != commands.rangesEnd(); i++)
- semanticState.completed(i->first(), i->last());
+ semanticState.completed(commands);
}
void SessionState::readyToSend() {
diff --git a/qpid/cpp/src/qpid/broker/Timer.cpp b/qpid/cpp/src/qpid/broker/Timer.cpp
deleted file mode 100644
index 74d208eceb..0000000000
--- a/qpid/cpp/src/qpid/broker/Timer.cpp
+++ /dev/null
@@ -1,120 +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/Timer.h"
-#include <iostream>
-
-using boost::intrusive_ptr;
-using qpid::sys::AbsTime;
-using qpid::sys::Duration;
-using qpid::sys::Monitor;
-using qpid::sys::Thread;
-using qpid::sys::Mutex;
-using qpid::sys::ScopedLock;
-using namespace qpid::broker;
-
-TimerTask::TimerTask(Duration timeout) :
- duration(timeout), time(AbsTime::now(), timeout), cancelled(false) {}
-
-TimerTask::TimerTask(AbsTime _time) :
- duration(0), time(_time), cancelled(false) {}
-
-TimerTask::~TimerTask(){}
-
-void TimerTask::reset() { time = AbsTime(AbsTime::now(), duration); }
-
-void TimerTask::cancel() {
- ScopedLock<Mutex> l(cancelLock);
- cancelled = true;
-}
-
-bool TimerTask::isCancelled() const { return cancelled; }
-
-Timer::Timer() : active(false)
-{
- start();
-}
-
-Timer::~Timer()
-{
- stop();
-}
-
-void Timer::run()
-{
- Monitor::ScopedLock l(monitor);
- while(active){
- if (tasks.empty()) {
- monitor.wait();
- } else {
- intrusive_ptr<TimerTask> t = tasks.top();
- tasks.pop();
- {
- ScopedLock<Mutex> l(t->cancelLock);
- if (t->isCancelled()) {
- continue;
- } else if(t->time < AbsTime::now()) {
- Monitor::ScopedUnlock u(monitor);
- t->fire();
- continue;
- } else {
- // If the timer was adjusted into the future it might no longer
- // be the next event, so push and then get top to make sure
- tasks.push(t);
- }
- }
- monitor.wait(tasks.top()->time);
- }
- }
-}
-
-void Timer::add(intrusive_ptr<TimerTask> task)
-{
- Monitor::ScopedLock l(monitor);
- tasks.push(task);
- monitor.notify();
-}
-
-void Timer::start()
-{
- Monitor::ScopedLock l(monitor);
- if (!active) {
- active = true;
- runner = Thread(this);
- }
-}
-
-void Timer::stop()
-{
- {
- Monitor::ScopedLock l(monitor);
- if (!active) return;
- active = false;
- monitor.notifyAll();
- }
- runner.join();
-}
-
-bool Later::operator()(const intrusive_ptr<TimerTask>& a,
- const intrusive_ptr<TimerTask>& b) const
-{
- return a.get() && b.get() && a->time > b->time;
-}
-
diff --git a/qpid/cpp/src/qpid/broker/Timer.h b/qpid/cpp/src/qpid/broker/Timer.h
deleted file mode 100644
index 223f87eab6..0000000000
--- a/qpid/cpp/src/qpid/broker/Timer.h
+++ /dev/null
@@ -1,87 +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.
- *
- */
-#ifndef _Timer_
-#define _Timer_
-
-#include "qpid/broker/BrokerImportExport.h"
-#include "qpid/sys/Monitor.h"
-#include "qpid/sys/Thread.h"
-#include "qpid/sys/Runnable.h"
-#include "qpid/RefCounted.h"
-
-#include <memory>
-#include <queue>
-
-#include <boost/intrusive_ptr.hpp>
-
-namespace qpid {
-namespace broker {
-
-class Timer;
-
-struct TimerTask : public RefCounted {
- friend class Timer;
-
- const qpid::sys::Duration duration;
- qpid::sys::AbsTime time;
- volatile bool cancelled;
- qpid::sys::Mutex cancelLock;
-
- QPID_BROKER_EXTERN TimerTask(qpid::sys::Duration timeout);
- TimerTask(qpid::sys::AbsTime time);
- QPID_BROKER_EXTERN virtual ~TimerTask();
- void reset();
- void cancel();
- bool isCancelled() const;
- virtual void fire() = 0;
-};
-
-struct Later {
- bool operator()(const boost::intrusive_ptr<TimerTask>& a,
- const boost::intrusive_ptr<TimerTask>& b) const;
-};
-
-class Timer : private qpid::sys::Runnable {
- protected:
- qpid::sys::Monitor monitor;
- std::priority_queue<boost::intrusive_ptr<TimerTask>,
- std::vector<boost::intrusive_ptr<TimerTask> >,
- Later> tasks;
- qpid::sys::Thread runner;
- bool active;
-
- virtual void run();
-
- public:
- QPID_BROKER_EXTERN Timer();
- QPID_BROKER_EXTERN virtual ~Timer();
-
- QPID_BROKER_EXTERN void add(boost::intrusive_ptr<TimerTask> task);
- void start();
- void stop();
-
-};
-
-
-}}
-
-
-#endif
diff --git a/qpid/cpp/src/qpid/broker/TopicExchange.cpp b/qpid/cpp/src/qpid/broker/TopicExchange.cpp
index 6c8832179f..6bf0b104ea 100644
--- a/qpid/cpp/src/qpid/broker/TopicExchange.cpp
+++ b/qpid/cpp/src/qpid/broker/TopicExchange.cpp
@@ -225,12 +225,25 @@ bool TopicExchange::bind(Queue::shared_ptr queue, const string& routingKey, cons
if (reallyUnbind)
unbind(queue, routingPattern, 0);
} else if (fedOp == fedOpReorigin) {
- for (BindingMap::iterator iter = bindings.begin();
- iter != bindings.end(); iter++) {
- const BoundKey& bk = iter->second;
- if (bk.fedBinding.hasLocal()) {
- propagateFedOp(iter->first, string(), fedOpBind, string());
+ /** gather up all the keys that need rebinding in a local vector
+ * while holding the lock. Then propagate once the lock is
+ * released
+ */
+ std::vector<std::string> keys2prop;
+ {
+ RWlock::ScopedRlock l(lock);
+ for (BindingMap::iterator iter = bindings.begin();
+ iter != bindings.end(); iter++) {
+ const BoundKey& bk = iter->second;
+
+ if (bk.fedBinding.hasLocal()) {
+ keys2prop.push_back(iter->first);
+ }
}
+ } /* lock dropped */
+ for (std::vector<std::string>::const_iterator key = keys2prop.begin();
+ key != keys2prop.end(); key++) {
+ propagateFedOp( *key, string(), fedOpBind, string());
}
}
diff --git a/qpid/cpp/src/qpid/client/ConnectionHandler.cpp b/qpid/cpp/src/qpid/client/ConnectionHandler.cpp
index 3d338af920..9b2f662c8e 100644
--- a/qpid/cpp/src/qpid/client/ConnectionHandler.cpp
+++ b/qpid/cpp/src/qpid/client/ConnectionHandler.cpp
@@ -147,11 +147,15 @@ void ConnectionHandler::close()
fail("Connection closed before it was established");
break;
case OPEN:
- setState(CLOSING);
- proxy.close(200, OK);
- waitFor(FINISHED);
+ if (setState(CLOSING, OPEN)) {
+ proxy.close(200, OK);
+ waitFor(FINISHED);//FINISHED = CLOSED or FAILED
+ }
+ //else, state was changed from open after we checked, can only
+ //change to failed or closed, so nothing to do
break;
- // Nothing to do for CLOSING, CLOSED, FAILED or NOT_STARTED
+
+ // Nothing to do if already CLOSING, CLOSED, FAILED or if NOT_STARTED
}
}
diff --git a/qpid/cpp/src/qpid/client/StateManager.cpp b/qpid/cpp/src/qpid/client/StateManager.cpp
index 9239950a3f..5462e0fed4 100644
--- a/qpid/cpp/src/qpid/client/StateManager.cpp
+++ b/qpid/cpp/src/qpid/client/StateManager.cpp
@@ -60,6 +60,18 @@ void StateManager::setState(int s)
stateLock.notifyAll();
}
+bool StateManager::setState(int s, int expected)
+{
+ Monitor::ScopedLock l(stateLock);
+ if (state == expected) {
+ state = s;
+ stateLock.notifyAll();
+ return true;
+ } else {
+ return false;
+ }
+}
+
int StateManager::getState() const
{
Monitor::ScopedLock l(stateLock);
diff --git a/qpid/cpp/src/qpid/client/StateManager.h b/qpid/cpp/src/qpid/client/StateManager.h
index b01664a0c1..3c8412dfa7 100644
--- a/qpid/cpp/src/qpid/client/StateManager.h
+++ b/qpid/cpp/src/qpid/client/StateManager.h
@@ -36,6 +36,7 @@ class StateManager
public:
StateManager(int initial);
void setState(int state);
+ bool setState(int state, int expected);
int getState() const ;
void waitForStateChange(int current);
void waitFor(std::set<int> states);
diff --git a/qpid/cpp/src/qpid/cluster/Cluster.cpp b/qpid/cpp/src/qpid/cluster/Cluster.cpp
index ca3a7fa257..7bdc066767 100644
--- a/qpid/cpp/src/qpid/cluster/Cluster.cpp
+++ b/qpid/cpp/src/qpid/cluster/Cluster.cpp
@@ -190,6 +190,7 @@ Cluster::Cluster(const ClusterSettings& set, broker::Broker& b) :
boost::bind(&Cluster::leave, this),
"Error delivering frames",
poller),
+ quorum(boost::bind(&Cluster::leave, this)),
initialized(false),
decoder(boost::bind(&Cluster::deliverFrame, this, _1)),
discarding(true),
@@ -214,7 +215,6 @@ Cluster::Cluster(const ClusterSettings& set, broker::Broker& b) :
// Update exchange is used during updates to replicate messages without modifying delivery-properties.exchange.
broker.getExchanges().registerExchange(boost::shared_ptr<broker::Exchange>(new UpdateExchange(this)));
- if (settings.quorum) quorum.init();
cpg.join(name);
// pump the CPG dispatch manually till we get initialized.
while (!initialized)
@@ -226,10 +226,10 @@ Cluster::~Cluster() {
}
void Cluster::initialize() {
+ if (settings.quorum) quorum.start(poller);
if (myUrl.empty())
myUrl = Url::getIpAddressesUrl(broker.getPort(broker::Broker::TCP_TRANSPORT));
- QPID_LOG(notice, *this << " member " << self << " joining "
- << name << " with url=" << myUrl);
+ QPID_LOG(notice, *this << " joining cluster " << name << " with url=" << myUrl);
broker.getKnownBrokers = boost::bind(&Cluster::getUrls, this);
broker.setExpiryPolicy(expiryPolicy);
dispatcher.start();
@@ -315,7 +315,7 @@ void Cluster::leave(Lock&) {
// Deliver CPG message.
void Cluster::deliver(
cpg_handle_t /*handle*/,
- cpg_name* /*group*/,
+ const cpg_name* /*group*/,
uint32_t nodeid,
uint32_t pid,
void* msg,
@@ -323,9 +323,11 @@ void Cluster::deliver(
{
MemberId from(nodeid, pid);
framing::Buffer buf(static_cast<char*>(msg), msg_len);
- Event e(Event::decodeCopy(from, buf));
- LATENCY_TRACK(if (e.getConnectionId().getMember() == self) mcast.cpgLatency.finish());
- deliverEvent(e);
+ while (buf.available()) {
+ Event e(Event::decodeCopy(from, buf));
+ LATENCY_TRACK(if (e.getConnectionId().getMember() == self) mcast.cpgLatency.finish());
+ deliverEvent(e);
+ }
}
LATENCY_TRACK(sys::LatencyTracker<const char*> eventQueueLatencyTracker("EventQueue");)
@@ -404,6 +406,7 @@ LATENCY_TRACK(sys::LatencyTracker<const AMQBody*> doOutputTracker("DoOutput");)
LATENCY_TRACK(frameQueueLatencyTracker.finish(e.frame.getBody()));
LATENCY_TRACK(if (e.frame.getBody()->type() == CONTENT_BODY) doOutputTracker.start(e.frame.getBody()));
Mutex::ScopedLock l(lock);
+ if (state == LEFT) return;
EventFrame e(efConst);
const ClusterUpdateOfferBody* offer = castUpdateOffer(e.frame.getBody());
if (offer && error.isUnresolved()) {
@@ -507,10 +510,10 @@ ostream& operator<<(ostream& o, const AddrList& a) {
void Cluster::configChange (
cpg_handle_t /*handle*/,
- cpg_name */*group*/,
- cpg_address *current, int nCurrent,
- cpg_address *left, int nLeft,
- cpg_address */*joined*/, int /*nJoined*/)
+ const cpg_name */*group*/,
+ const cpg_address *current, int nCurrent,
+ const cpg_address *left, int nLeft,
+ const cpg_address *joined, int nJoined)
{
Mutex::ScopedLock l(lock);
if (state == INIT) { // First config change.
@@ -518,10 +521,13 @@ void Cluster::configChange (
broker.setRecovery(nCurrent == 1);
initialized = true;
}
- QPID_LOG(notice, *this << " membership change: " << AddrList(current, nCurrent)
- << AddrList(left, nLeft, "left: "));
+ QPID_LOG(notice, *this << " membership change: "
+ << AddrList(current, nCurrent) << "("
+ << AddrList(joined, nJoined, "joined: ")
+ << AddrList(left, nLeft, "left: ")
+ << ")");
std::string addresses;
- for (cpg_address* p = current; p < current+nCurrent; ++p)
+ for (const cpg_address* p = current; p < current+nCurrent; ++p)
addresses.append(MemberId(*p).str());
deliverEvent(Event::control(ClusterConfigChangeBody(ProtocolVersion(), addresses), self));
}
@@ -833,9 +839,9 @@ std::ostream& operator<<(std::ostream& o, const Cluster& cluster) {
"INIT", "JOINER", "UPDATEE", "CATCHUP", "READY", "OFFER", "UPDATER", "LEFT"
};
assert(sizeof(STATE)/sizeof(*STATE) == Cluster::LEFT+1);
- o << "cluster:" << STATE[cluster.state];
+ o << "cluster(" << cluster.self << " " << STATE[cluster.state];
if (cluster.settings.checkErrors && cluster.error.isUnresolved()) o << "/error";
- return o;
+ return o << ")";;
}
MemberId Cluster::getId() const {
@@ -846,14 +852,6 @@ broker::Broker& Cluster::getBroker() const {
return broker; // Immutable, no need to lock.
}
-void Cluster::checkQuorum() {
- if (!quorum.isQuorate()) {
- QPID_LOG(critical, *this << " disconnected from cluster quorum, shutting down");
- leave();
- throw Exception(QPID_MSG(*this << " disconnected from cluster quorum."));
- }
-}
-
void Cluster::setClusterId(const Uuid& uuid, Lock&) {
clusterId = uuid;
if (mgmtObject) {
diff --git a/qpid/cpp/src/qpid/cluster/Cluster.h b/qpid/cpp/src/qpid/cluster/Cluster.h
index a95f2ab60e..286d7867c9 100644
--- a/qpid/cpp/src/qpid/cluster/Cluster.h
+++ b/qpid/cpp/src/qpid/cluster/Cluster.h
@@ -101,8 +101,6 @@ class Cluster : private Cpg::Handler, public management::Manageable {
broker::Broker& getBroker() const;
Multicaster& getMulticast() { return mcast; }
- void checkQuorum();
-
const ClusterSettings& getSettings() const { return settings; }
void deliverFrame(const EventFrame&);
@@ -169,7 +167,7 @@ class Cluster : private Cpg::Handler, public management::Manageable {
// == Called in CPG dispatch thread
void deliver( // CPG deliver callback.
cpg_handle_t /*handle*/,
- struct cpg_name *group,
+ const struct cpg_name *group,
uint32_t /*nodeid*/,
uint32_t /*pid*/,
void* /*msg*/,
@@ -179,10 +177,10 @@ class Cluster : private Cpg::Handler, public management::Manageable {
void configChange( // CPG config change callback.
cpg_handle_t /*handle*/,
- struct cpg_name */*group*/,
- struct cpg_address */*members*/, int /*nMembers*/,
- struct cpg_address */*left*/, int /*nLeft*/,
- struct cpg_address */*joined*/, int /*nJoined*/
+ const struct cpg_name */*group*/,
+ const struct cpg_address */*members*/, int /*nMembers*/,
+ const struct cpg_address */*left*/, int /*nLeft*/,
+ const struct cpg_address */*joined*/, int /*nJoined*/
);
// == Called in management threads.
diff --git a/qpid/cpp/src/qpid/cluster/ClusterPlugin.cpp b/qpid/cpp/src/qpid/cluster/ClusterPlugin.cpp
index 935a84a7a9..289ec672a8 100644
--- a/qpid/cpp/src/qpid/cluster/ClusterPlugin.cpp
+++ b/qpid/cpp/src/qpid/cluster/ClusterPlugin.cpp
@@ -72,7 +72,7 @@ struct ClusterOptions : public Options {
("cluster-cman", optValue(settings.quorum), "Integrate with Cluster Manager (CMAN) cluster.")
#endif
("cluster-read-max", optValue(settings.readMax,"N"), "Experimental: flow-control limit reads per connection. 0=no limit.")
- // FIXME aconway 2009-05-20: temporary
+ // TODO aconway 2009-05-20: temporary, remove
("cluster-check-errors", optValue(settings.checkErrors, "yes|no"), "Enable/disable cluster error checks. Normally should be enabled.")
;
}
diff --git a/qpid/cpp/src/qpid/cluster/ClusterSettings.h b/qpid/cpp/src/qpid/cluster/ClusterSettings.h
index c82be9d227..8820e8a5ac 100644
--- a/qpid/cpp/src/qpid/cluster/ClusterSettings.h
+++ b/qpid/cpp/src/qpid/cluster/ClusterSettings.h
@@ -37,7 +37,7 @@ struct ClusterSettings {
bool checkErrors;
ClusterSettings() : quorum(false), readMax(10),
- checkErrors(true) // FIXME aconway 2009-05-20: temporary
+ checkErrors(true) // TODO aconway 2009-05-20: remove this option.
{}
Url getUrl(uint16_t port) const {
diff --git a/qpid/cpp/src/qpid/cluster/Cpg.cpp b/qpid/cpp/src/qpid/cluster/Cpg.cpp
index 0878ae94b9..02411e6e9f 100644
--- a/qpid/cpp/src/qpid/cluster/Cpg.cpp
+++ b/qpid/cpp/src/qpid/cluster/Cpg.cpp
@@ -49,6 +49,28 @@ Cpg* Cpg::cpgFromHandle(cpg_handle_t handle) {
// Global callback functions.
void Cpg::globalDeliver (
cpg_handle_t handle,
+ const struct cpg_name *group,
+ uint32_t nodeid,
+ uint32_t pid,
+ void* msg,
+ size_t msg_len)
+{
+ cpgFromHandle(handle)->handler.deliver(handle, group, nodeid, pid, msg, msg_len);
+}
+
+void Cpg::globalConfigChange(
+ cpg_handle_t handle,
+ const struct cpg_name *group,
+ const struct cpg_address *members, size_t nMembers,
+ const struct cpg_address *left, size_t nLeft,
+ const struct cpg_address *joined, size_t nJoined
+)
+{
+ cpgFromHandle(handle)->handler.configChange(handle, group, members, nMembers, left, nLeft, joined, nJoined);
+}
+
+void Cpg::globalDeliver (
+ cpg_handle_t handle,
struct cpg_name *group,
uint32_t nodeid,
uint32_t pid,
@@ -83,7 +105,7 @@ Cpg::Cpg(Handler& h) : IOHandle(new sys::IOHandlePrivate), handler(h), isShutdow
QPID_LOG(info, "Initializing CPG");
cpg_error_t err = cpg_initialize(&handle, &callbacks);
- int retries = 6;
+ int retries = 6; // FIXME aconway 2009-08-06: configure, use same config for cman connection.
while (err == CPG_ERR_TRY_AGAIN && --retries) {
QPID_LOG(notice, "Re-trying CPG initialization.");
sys::sleep(5);
diff --git a/qpid/cpp/src/qpid/cluster/Cpg.h b/qpid/cpp/src/qpid/cluster/Cpg.h
index 624721b560..cffbf0bdb3 100644
--- a/qpid/cpp/src/qpid/cluster/Cpg.h
+++ b/qpid/cpp/src/qpid/cluster/Cpg.h
@@ -20,7 +20,6 @@
*/
#include "qpid/Exception.h"
-#include "qpid/cluster/Dispatchable.h"
#include "qpid/cluster/types.h"
#include "qpid/sys/IOHandle.h"
#include "qpid/sys/Mutex.h"
@@ -68,7 +67,7 @@ class Cpg : public sys::IOHandle {
virtual ~Handler() {};
virtual void deliver(
cpg_handle_t /*handle*/,
- struct cpg_name *group,
+ const struct cpg_name *group,
uint32_t /*nodeid*/,
uint32_t /*pid*/,
void* /*msg*/,
@@ -76,10 +75,10 @@ class Cpg : public sys::IOHandle {
virtual void configChange(
cpg_handle_t /*handle*/,
- struct cpg_name */*group*/,
- struct cpg_address */*members*/, int /*nMembers*/,
- struct cpg_address */*left*/, int /*nLeft*/,
- struct cpg_address */*joined*/, int /*nJoined*/
+ const struct cpg_name */*group*/,
+ const struct cpg_address */*members*/, int /*nMembers*/,
+ const struct cpg_address */*left*/, int /*nLeft*/,
+ const struct cpg_address */*joined*/, int /*nJoined*/
) = 0;
};
@@ -122,6 +121,24 @@ class Cpg : public sys::IOHandle {
static Cpg* cpgFromHandle(cpg_handle_t);
+ // New versions for corosync 1.0 and higher
+ static void globalDeliver(
+ cpg_handle_t handle,
+ const struct cpg_name *group,
+ uint32_t nodeid,
+ uint32_t pid,
+ void* msg,
+ size_t msg_len);
+
+ static void globalConfigChange(
+ cpg_handle_t handle,
+ const struct cpg_name *group,
+ const struct cpg_address *members, size_t nMembers,
+ const struct cpg_address *left, size_t nLeft,
+ const struct cpg_address *joined, size_t nJoined
+ );
+
+ // Old versions for openais
static void globalDeliver(
cpg_handle_t handle,
struct cpg_name *group,
diff --git a/qpid/cpp/src/qpid/cluster/Event.cpp b/qpid/cpp/src/qpid/cluster/Event.cpp
index 30866d3154..9fcf9f5ce1 100644
--- a/qpid/cpp/src/qpid/cluster/Event.cpp
+++ b/qpid/cpp/src/qpid/cluster/Event.cpp
@@ -72,7 +72,7 @@ Event Event::decodeCopy(const MemberId& m, framing::Buffer& buf) {
if (buf.available() < e.size)
throw Exception("Not enough data for multicast event");
e.store = RefCountedBuffer::create(e.size + HEADER_SIZE);
- memcpy(e.getData(), buf.getPointer() + buf.getPosition(), e.size);
+ buf.getRawData((uint8_t*)(e.getData()), e.size);
return e;
}
diff --git a/qpid/cpp/src/qpid/cluster/Multicaster.cpp b/qpid/cpp/src/qpid/cluster/Multicaster.cpp
index 7e97963318..4123e11c92 100644
--- a/qpid/cpp/src/qpid/cluster/Multicaster.cpp
+++ b/qpid/cpp/src/qpid/cluster/Multicaster.cpp
@@ -24,10 +24,14 @@
#include "qpid/log/Statement.h"
#include "qpid/framing/AMQBody.h"
#include "qpid/framing/AMQFrame.h"
+#include <boost/bind.hpp>
+#include <algorithm>
namespace qpid {
namespace cluster {
+static const int MCAST_IOV_MAX=63; // Limit imposed by CPG
+
Multicaster::Multicaster(Cpg& cpg_,
const boost::shared_ptr<sys::Poller>& poller,
boost::function<void()> onError_) :
@@ -36,7 +40,8 @@ Multicaster::Multicaster(Cpg& cpg_,
#endif
onError(onError_), cpg(cpg_),
queue(boost::bind(&Multicaster::sendMcast, this, _1), poller),
- holding(true)
+ holding(true),
+ ioVector(MCAST_IOV_MAX)
{
queue.start();
}
@@ -70,26 +75,29 @@ void Multicaster::mcast(const Event& e) {
queue.push(e);
}
-
-Multicaster::PollableEventQueue::Batch::const_iterator Multicaster::sendMcast(const PollableEventQueue::Batch& values) {
+Multicaster::PollableEventQueue::Batch::const_iterator Multicaster::sendMcast(
+ const PollableEventQueue::Batch& events)
+{
+ PollableEventQueue::Batch::const_iterator i = events.begin();
try {
- PollableEventQueue::Batch::const_iterator i = values.begin();
- while( i != values.end()) {
- iovec iov = i->toIovec();
- if (!cpg.mcast(&iov, 1)) {
- // cpg didn't send because of CPG flow control.
- break;
+ while (i < events.end()) {
+ size_t count = std::min(MCAST_IOV_MAX, int(events.end() - i));
+ std::transform(i, i+count, ioVector.begin(),
+ boost::bind(&Event::toIovec, _1));
+ if (!cpg.mcast(&ioVector.front(), count)) {
+ QPID_LOG(trace, "CPG flow control, will resend "
+ << events.end() - i << " events");
+ break;
}
- ++i;
+ i += count;
}
- return i;
}
catch (const std::exception& e) {
QPID_LOG(critical, "Multicast error: " << e.what());
queue.stop();
onError();
- return values.end();
}
+ return i;
}
void Multicaster::release() {
diff --git a/qpid/cpp/src/qpid/cluster/Multicaster.h b/qpid/cpp/src/qpid/cluster/Multicaster.h
index f2ee5099bb..652900aa0f 100644
--- a/qpid/cpp/src/qpid/cluster/Multicaster.h
+++ b/qpid/cpp/src/qpid/cluster/Multicaster.h
@@ -29,6 +29,7 @@
#include "qpid/sys/LatencyTracker.h"
#include <boost/shared_ptr.hpp>
#include <deque>
+#include <vector>
namespace qpid {
@@ -72,7 +73,7 @@ class Multicaster
PollableEventQueue queue;
bool holding;
PlainEventQueue holdingQueue;
- std::vector<struct ::iovec> ioVector;
+ std::vector< ::iovec> ioVector;
};
}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp b/qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp
index 2025c664da..cb8f01386c 100644
--- a/qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp
+++ b/qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp
@@ -46,7 +46,6 @@ extern sys::LatencyTracker<const AMQBody*> doOutputTracker;
void OutputInterceptor::send(framing::AMQFrame& f) {
LATENCY_TRACK(doOutputTracker.finish(f.getBody()));
- parent.getCluster().checkQuorum();
{
sys::Mutex::ScopedLock l(lock);
next->send(f);
diff --git a/qpid/cpp/src/qpid/cluster/PollerDispatch.cpp b/qpid/cpp/src/qpid/cluster/PollerDispatch.cpp
index 47c1fd0c39..a839ef863b 100644
--- a/qpid/cpp/src/qpid/cluster/PollerDispatch.cpp
+++ b/qpid/cpp/src/qpid/cluster/PollerDispatch.cpp
@@ -33,15 +33,18 @@ PollerDispatch::PollerDispatch(Cpg& c, boost::shared_ptr<sys::Poller> p,
boost::bind(&PollerDispatch::dispatch, this, _1), // read
0, // write
boost::bind(&PollerDispatch::disconnect, this, _1) // disconnect
- )
+ ),
+ started(false)
{}
PollerDispatch::~PollerDispatch() {
- dispatchHandle.stopWatch();
+ if (started)
+ dispatchHandle.stopWatch();
}
void PollerDispatch::start() {
dispatchHandle.startWatch(poller);
+ started = true;
}
// Entry point: called by IO to dispatch CPG events.
diff --git a/qpid/cpp/src/qpid/cluster/PollerDispatch.h b/qpid/cpp/src/qpid/cluster/PollerDispatch.h
index 1c9266aa3c..63801e0de9 100644
--- a/qpid/cpp/src/qpid/cluster/PollerDispatch.h
+++ b/qpid/cpp/src/qpid/cluster/PollerDispatch.h
@@ -51,6 +51,7 @@ class PollerDispatch {
boost::shared_ptr<sys::Poller> poller;
boost::function<void()> onError;
sys::DispatchHandleRef dispatchHandle;
+ bool started;
};
diff --git a/qpid/cpp/src/qpid/cluster/Quorum_cman.cpp b/qpid/cpp/src/qpid/cluster/Quorum_cman.cpp
index 32ed5c1d91..277adaf7b1 100644
--- a/qpid/cpp/src/qpid/cluster/Quorum_cman.cpp
+++ b/qpid/cpp/src/qpid/cluster/Quorum_cman.cpp
@@ -18,28 +18,86 @@
* under the License.
*
*/
+
#include "qpid/cluster/Quorum_cman.h"
+#include "qpid/cluster/Cluster.h"
#include "qpid/log/Statement.h"
#include "qpid/Options.h"
#include "qpid/sys/Time.h"
+#include "qpid/sys/posix/PrivatePosix.h"
namespace qpid {
namespace cluster {
-Quorum::Quorum() : enable(false), cman(0) {}
+namespace {
+
+boost::function<void()> errorFn;
-Quorum::~Quorum() { if (cman) cman_finish(cman); }
+void cmanCallbackFn(cman_handle_t handle, void */*privdata*/, int reason, int arg) {
+ if (reason == CMAN_REASON_STATECHANGE && arg == 0) {
+ QPID_LOG(critical, "Lost contact with cluster quorum.");
+ if (errorFn) errorFn();
+ cman_stop_notification(handle);
+ }
+}
+}
+
+Quorum::Quorum(boost::function<void()> err) : enable(false), cman(0), cmanFd(0) {
+ errorFn = err;
+}
+
+Quorum::~Quorum() {
+ dispatchHandle.reset();
+ if (cman) cman_finish(cman);
+}
-void Quorum::init() {
+void Quorum::start(boost::shared_ptr<sys::Poller> p) {
+ poller = p;
enable = true;
+ QPID_LOG(debug, "Connecting to quorum service.");
cman = cman_init(0);
if (cman == 0) throw ErrnoException("Can't connect to cman service");
if (!cman_is_quorate(cman)) {
QPID_LOG(notice, "Waiting for cluster quorum.");
while(!cman_is_quorate(cman)) sys::sleep(5);
}
+ int err = cman_start_notification(cman, cmanCallbackFn);
+ if (err != 0) throw ErrnoException("Can't register for cman notifications");
+ watch(getFd());
}
-bool Quorum::isQuorate() { return enable ? cman_is_quorate(cman) : true; }
+void Quorum::watch(int fd) {
+ cmanFd = fd;
+ dispatchHandle.reset(
+ new sys::DispatchHandleRef(
+ sys::PosixIOHandle(cmanFd),
+ boost::bind(&Quorum::dispatch, this, _1), // read
+ 0, // write
+ boost::bind(&Quorum::disconnect, this, _1) // disconnect
+ ));
+ dispatchHandle->startWatch(poller);
+}
+
+int Quorum::getFd() {
+ int fd = cman_get_fd(cman);
+ if (fd == 0) throw ErrnoException("Can't get cman file descriptor");
+ return fd;
+}
+
+void Quorum::dispatch(sys::DispatchHandle&) {
+ try {
+ cman_dispatch(cman, CMAN_DISPATCH_ALL);
+ int fd = getFd();
+ if (fd != cmanFd) watch(fd);
+ } catch (const std::exception& e) {
+ QPID_LOG(critical, "Error in quorum dispatch: " << e.what());
+ errorFn();
+ }
+}
+
+void Quorum::disconnect(sys::DispatchHandle&) {
+ QPID_LOG(critical, "Disconnected from quorum service");
+ errorFn();
+}
}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/Quorum_cman.h b/qpid/cpp/src/qpid/cluster/Quorum_cman.h
index d0f8b2c954..130f1baf64 100644
--- a/qpid/cpp/src/qpid/cluster/Quorum_cman.h
+++ b/qpid/cpp/src/qpid/cluster/Quorum_cman.h
@@ -22,26 +22,40 @@
*
*/
+#include <qpid/sys/DispatchHandle.h>
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+#include <memory>
+
extern "C" {
#include <libcman.h>
}
namespace qpid {
-
-class Options;
+namespace sys {
+class Poller;
+}
namespace cluster {
+class Cluster;
class Quorum {
public:
- Quorum();
+ Quorum(boost::function<void ()> onError);
~Quorum();
- void init();
- bool isQuorate();
+ void start(boost::shared_ptr<sys::Poller>);
private:
+ void dispatch(sys::DispatchHandle&);
+ void disconnect(sys::DispatchHandle&);
+ int getFd();
+ void watch(int fd);
+
bool enable;
cman_handle_t cman;
+ int cmanFd;
+ std::auto_ptr<sys::DispatchHandleRef> dispatchHandle;
+ boost::shared_ptr<sys::Poller> poller;
};
diff --git a/qpid/cpp/src/qpid/cluster/Quorum_null.h b/qpid/cpp/src/qpid/cluster/Quorum_null.h
index cbb6c20708..dc27f0a43b 100644
--- a/qpid/cpp/src/qpid/cluster/Quorum_null.h
+++ b/qpid/cpp/src/qpid/cluster/Quorum_null.h
@@ -21,15 +21,20 @@
* under the License.
*
*/
+
+#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
+
namespace qpid {
namespace cluster {
+class Cluster;
/** Null implementation of quorum. */
class Quorum {
public:
- void init() {}
- bool isQuorate() { return true; }
+ Quorum(boost::function<void ()>) {}
+ void start(boost::shared_ptr<sys::Poller>) {}
};
#endif /*!QPID_CLUSTER_QUORUM_NULL_H*/
diff --git a/qpid/cpp/src/qpid/cluster/WatchDogPlugin.cpp b/qpid/cpp/src/qpid/cluster/WatchDogPlugin.cpp
new file mode 100644
index 0000000000..1b813411f6
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/WatchDogPlugin.cpp
@@ -0,0 +1,114 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "qpid/Plugin.h"
+#include "qpid/Options.h"
+#include "qpid/log/Statement.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/sys/Timer.h"
+#include "qpid/sys/Fork.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+namespace qpid {
+namespace cluster {
+
+using broker::Broker;
+
+struct Settings {
+ Settings() : interval(0) {}
+ int interval;
+};
+
+struct WatchDogOptions : public qpid::Options {
+ Settings& settings;
+
+ WatchDogOptions(Settings& s) : settings(s) {
+ addOptions()
+ ("watchdog-interval", optValue(settings.interval, "N"),
+ "broker is automatically killed if it is hung for more than \
+ N seconds. 0 disables watchdog.");
+ }
+};
+
+struct WatchDogTask : public sys::TimerTask {
+ int pid;
+ sys::Timer& timer;
+ int interval;
+
+ WatchDogTask(int pid_, sys::Timer& t, int _interval)
+ : TimerTask(_interval*sys::TIME_SEC/2), pid(pid_), timer(t), interval(_interval) {}
+
+ void fire() {
+ timer.add (new WatchDogTask(pid, timer, interval));
+ QPID_LOG(debug, "Sending keepalive signal to watchdog");
+ ::kill(pid, SIGUSR1);
+ }
+};
+
+struct WatchDogPlugin : public qpid::Plugin, public qpid::sys::Fork {
+ Settings settings;
+ WatchDogOptions options;
+ Broker* broker;
+ int watchdogPid;
+
+ WatchDogPlugin() : options(settings), broker(0), watchdogPid(0) {}
+
+ ~WatchDogPlugin() {
+ if (watchdogPid) ::kill(watchdogPid, SIGTERM);
+ ::waitpid(watchdogPid, 0, 0);
+ }
+
+ Options* getOptions() { return &options; }
+
+ void earlyInitialize(qpid::Plugin::Target& target) {
+ broker = dynamic_cast<Broker*>(&target);
+ if (broker && settings.interval) {
+ QPID_LOG(notice, "Starting watchdog process with interval of " <<
+ settings.interval << " seconds");
+ fork();
+ }
+ }
+
+ void initialize(Target&) {}
+
+ protected:
+
+ void child() { // Child of fork
+ const char* watchdog = ::getenv("QPID_WATCHDOG_EXE"); // For use in tests
+ if (!watchdog) watchdog=QPID_EXEC_DIR "/qpidd_watchdog";
+ std::string interval = boost::lexical_cast<std::string>(settings.interval);
+ ::execl(watchdog, watchdog, interval.c_str(), NULL);
+ QPID_LOG(critical, "Failed to exec watchdog program " << watchdog );
+ ::kill(::getppid(), SIGKILL);
+ exit(1);
+ }
+
+ void parent(int pid) { // Parent of fork
+ watchdogPid = pid;
+ broker->getTimer().add(
+ new WatchDogTask(watchdogPid, broker->getTimer(), settings.interval));
+ // TODO aconway 2009-08-10: to be extra safe, we could monitor
+ // the watchdog child and re-start it if it exits.
+ }
+};
+
+static WatchDogPlugin instance; // Static initialization.
+
+}} // namespace qpid::cluster
diff --git a/qpid/cpp/src/qpid/cluster/qpidd_watchdog.cpp b/qpid/cpp/src/qpid/cluster/qpidd_watchdog.cpp
new file mode 100644
index 0000000000..0e7f4f18fd
--- /dev/null
+++ b/qpid/cpp/src/qpid/cluster/qpidd_watchdog.cpp
@@ -0,0 +1,60 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/types.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+
+long timeout;
+
+void killParent(int) {
+ ::kill(getppid(), SIGKILL);
+ ::fprintf(stderr, "Watchdog killed unresponsive broker, pid=%d\n", ::getppid());
+ ::exit(1);
+}
+
+void resetTimer(int) {
+ struct ::itimerval itval = { { 0, 0 }, { timeout, 0 } };
+ if (::setitimer(ITIMER_REAL, &itval, 0) !=0) {
+ ::perror("Watchdog failed to set timer");
+ killParent(0);
+ ::exit(1);
+ }
+}
+
+/** Simple watchdog program: kill parent process if timeout
+ * expires without a SIGUSR1.
+ * Will be killed with SIGHUP when parent shuts down.
+ * Args: timeout in seconds.
+ */
+int main(int argc, char** argv) {
+ if(argc != 2 || (timeout = atoi(argv[1])) == 0) {
+ ::fprintf(stderr, "Usage: %s <timeout_seconds>\n", argv[0]);
+ ::exit(1);
+ }
+ ::signal(SIGUSR1, resetTimer);
+ ::signal(SIGALRM, killParent);
+ resetTimer(0);
+ while (true) { sleep(INT_MAX); }
+}
diff --git a/qpid/cpp/src/qpid/framing/AMQFrame.cpp b/qpid/cpp/src/qpid/framing/AMQFrame.cpp
index 76bf901bf4..5c5920d786 100644
--- a/qpid/cpp/src/qpid/framing/AMQFrame.cpp
+++ b/qpid/cpp/src/qpid/framing/AMQFrame.cpp
@@ -41,7 +41,7 @@ AMQFrame::AMQFrame(const boost::intrusive_ptr<AMQBody>& b) : body(b) { init(); }
AMQFrame::AMQFrame(const AMQBody& b) : body(b.clone()) { init(); }
-AMQFrame::~AMQFrame() { init(); }
+AMQFrame::~AMQFrame() {}
AMQBody* AMQFrame::getBody() {
// Non-const AMQBody* may be used to modify the body.
diff --git a/qpid/cpp/src/qpid/framing/IsInSequenceSet.h b/qpid/cpp/src/qpid/framing/IsInSequenceSet.h
new file mode 100644
index 0000000000..fe10c1b9fa
--- /dev/null
+++ b/qpid/cpp/src/qpid/framing/IsInSequenceSet.h
@@ -0,0 +1,51 @@
+#ifndef QPID_FRAMING_ISINSEQUENCESET_H
+#define QPID_FRAMING_ISINSEQUENCESET_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/framing/SequenceSet.h"
+
+namespace qpid {
+namespace framing {
+/**
+ * Functor to test whether values are in a sequence set. This is a
+ * stateful functor that requires the values to be supplied in order
+ * and takes advantage of that ordering to avoid multiple scans.
+ */
+class IsInSequenceSet
+{
+ public:
+ IsInSequenceSet(const SequenceSet& s) : set(s), i(set.rangesBegin()) {}
+
+ bool operator()(const SequenceNumber& n) {
+ while (i != set.rangesEnd() && i->end() <= n) ++i;
+ return i != set.rangesEnd() && i->begin() <= n;
+ }
+
+ private:
+ const SequenceSet& set;
+ SequenceSet::RangeIterator i;
+};
+
+}} // namespace qpid::framing
+
+#endif /*!QPID_FRAMING_ISINSEQUENCESET_H*/
diff --git a/qpid/cpp/src/qpid/sys/DispatchHandle.cpp b/qpid/cpp/src/qpid/sys/DispatchHandle.cpp
index d65cd8a427..605edabc64 100644
--- a/qpid/cpp/src/qpid/sys/DispatchHandle.cpp
+++ b/qpid/cpp/src/qpid/sys/DispatchHandle.cpp
@@ -284,10 +284,7 @@ void DispatchHandle::processEvent(Poller::EventType type) {
readableCallback(*this);
writableCallback(*this);
break;
- case Poller::DISCONNECTED: {
- ScopedLock<Mutex> lock(stateLock);
- poller->unmonitorHandle(*this, Poller::INOUT);
- }
+ case Poller::DISCONNECTED:
if (disconnectedCallback) {
disconnectedCallback(*this);
}
diff --git a/qpid/cpp/src/qpid/sys/Timer.cpp b/qpid/cpp/src/qpid/sys/Timer.cpp
index 5ed117db93..d569fb3bb7 100644
--- a/qpid/cpp/src/qpid/sys/Timer.cpp
+++ b/qpid/cpp/src/qpid/sys/Timer.cpp
@@ -55,9 +55,10 @@ void TimerTask::fireTask() {
fire();
}
+// This can only be used to setup the next fire time. After the Timer has already fired
void TimerTask::setupNextFire() {
if (period && readyToFire()) {
- nextFireTime = AbsTime(nextFireTime, period);
+ nextFireTime = max(AbsTime::now(), AbsTime(nextFireTime, period));
cancelled = false;
} else {
QPID_LOG(error, "Couldn't setup next timer firing: " << Duration(nextFireTime, AbsTime::now()) << "[" << period << "]");
@@ -66,13 +67,11 @@ void TimerTask::setupNextFire() {
// Only allow tasks to be delayed
void TimerTask::restart() { nextFireTime = max(nextFireTime, AbsTime(AbsTime::now(), period)); }
-void TimerTask::delayTill(AbsTime time) { period = 0; nextFireTime = max(nextFireTime, time); }
void TimerTask::cancel() {
ScopedLock<Mutex> l(callbackLock);
cancelled = true;
}
-bool TimerTask::isCancelled() const { return cancelled; }
Timer::Timer() :
active(false)
@@ -98,13 +97,13 @@ void Timer::run()
// warn on extreme lateness
AbsTime start(AbsTime::now());
- Duration late(t->sortTime, start);
- if (late > 500 * TIME_MSEC) {
- QPID_LOG(warning, "Timer delayed by " << late / TIME_MSEC << "ms");
- }
+ Duration delay(t->sortTime, start);
{
ScopedLock<Mutex> l(t->callbackLock);
if (t->cancelled) {
+ if (delay > 500 * TIME_MSEC) {
+ QPID_LOG(debug, "cancelled Timer woken up " << delay / TIME_MSEC << "ms late");
+ }
continue;
} else if(Duration(t->nextFireTime, start) >= 0) {
Monitor::ScopedUnlock u(monitor);
@@ -112,7 +111,17 @@ void Timer::run()
// Warn on callback overrun
AbsTime end(AbsTime::now());
Duration overrun(tasks.top()->nextFireTime, end);
- if (overrun > 1 * TIME_MSEC) {
+ bool late = delay > 1 * TIME_MSEC;
+ bool overran = overrun > 1 * TIME_MSEC;
+ if (late)
+ if (overran) {
+ QPID_LOG(warning,
+ "Timer woken up " << delay / TIME_MSEC << "ms late, "
+ "overrunning by " << overrun / TIME_MSEC << "ms [taking "
+ << Duration(start, end) << "]");
+ } else {
+ QPID_LOG(warning, "Timer woken up " << delay / TIME_MSEC << "ms late");
+ } else if (overran) {
QPID_LOG(warning,
"Timer callback overran by " << overrun / TIME_MSEC << "ms [taking "
<< Duration(start, end) << "]");
diff --git a/qpid/cpp/src/qpid/sys/Timer.h b/qpid/cpp/src/qpid/sys/Timer.h
index fc7491d5ed..5748503841 100644
--- a/qpid/cpp/src/qpid/sys/Timer.h
+++ b/qpid/cpp/src/qpid/sys/Timer.h
@@ -58,9 +58,7 @@ public:
QPID_COMMON_EXTERN void setupNextFire();
QPID_COMMON_EXTERN void restart();
- QPID_COMMON_EXTERN void delayTill(AbsTime fireTime);
QPID_COMMON_EXTERN void cancel();
- QPID_COMMON_EXTERN bool isCancelled() const;
protected:
// Must be overridden with callback
diff --git a/qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp b/qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp
index 5a6b8987ec..9fd0602ce9 100644
--- a/qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp
+++ b/qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp
@@ -575,6 +575,11 @@ Poller::Event Poller::wait(Duration timeout) {
// (just not writable), allow us to readable until we get here again
if (epe.events & ::EPOLLHUP) {
if (eh.isHungup()) {
+ eh.setInactive();
+ // Don't set up last Handle so that we don't reset this handle
+ // on re-entering Poller::wait. This means that we will never
+ // be set active again once we've returned disconnected, and so
+ // can never be returned again.
return Event(handle, DISCONNECTED);
}
eh.setHungup();
diff --git a/qpid/cpp/src/qpid/sys/windows/LockFile.cpp b/qpid/cpp/src/qpid/sys/windows/LockFile.cpp
index 6cc710a3df..e9079b6094 100755
--- a/qpid/cpp/src/qpid/sys/windows/LockFile.cpp
+++ b/qpid/cpp/src/qpid/sys/windows/LockFile.cpp
@@ -43,7 +43,8 @@ LockFile::LockFile(const std::string& path_, bool create)
create ? OPEN_ALWAYS : OPEN_EXISTING,
FILE_FLAG_DELETE_ON_CLOSE, /* Delete file when closed */
NULL);
- QPID_WINDOWS_CHECK_NOT(h, INVALID_HANDLE_VALUE);
+ if (h == INVALID_HANDLE_VALUE)
+ throw qpid::Exception(path + qpid::sys::strError(GetLastError()));
impl.reset(new LockFilePrivate(h));
}
diff --git a/qpid/cpp/src/tests/Makefile.am b/qpid/cpp/src/tests/Makefile.am
index 563068a018..db4c8ba914 100644
--- a/qpid/cpp/src/tests/Makefile.am
+++ b/qpid/cpp/src/tests/Makefile.am
@@ -256,6 +256,18 @@ check_PROGRAMS+=datagen
datagen_SOURCES=datagen.cpp
datagen_LDADD=$(lib_common)
+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)
+
TESTS_ENVIRONMENT = \
VALGRIND=$(VALGRIND) \
@@ -305,9 +317,8 @@ CLEANFILES+=valgrind.out *.log *.vglog* dummy_test $(unit_wrappers)
# Not run under valgrind, too slow
LONG_TESTS+=start_broker fanout_perftest shared_perftest multiq_perftest topic_perftest run_ring_queue_test stop_broker \
- run_failover_soak \
+ run_failover_soak reliable_replication_test \
federated_cluster_test_with_node_failure
-# TODO: renable the temporarily disabled the failing reliable_replication_test when QPID-1984 is resolved.
EXTRA_DIST+=fanout_perftest shared_perftest multiq_perftest topic_perftest run_failover_soak reliable_replication_test \
federated_cluster_test_with_node_failure \
diff --git a/qpid/cpp/src/tests/PollerTest.cpp b/qpid/cpp/src/tests/PollerTest.cpp
index 19f641ca36..11337d6be3 100644
--- a/qpid/cpp/src/tests/PollerTest.cpp
+++ b/qpid/cpp/src/tests/PollerTest.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
@@ -50,7 +50,7 @@ int writeALot(int fd, const string& s) {
int lastWrite = ::write(fd, s.c_str(), s.size());
if ( lastWrite >= 0) {
bytesWritten += lastWrite;
- }
+ }
} while (errno != EAGAIN);
return bytesWritten;
}
@@ -58,32 +58,32 @@ int writeALot(int fd, const string& s) {
int readALot(int fd) {
int bytesRead = 0;
char buf[1024];
-
+
do {
errno = 0;
int lastRead = ::read(fd, buf, sizeof(buf));
if ( lastRead >= 0) {
bytesRead += lastRead;
- }
+ }
} while (errno != EAGAIN);
return bytesRead;
}
int main(int /*argc*/, char** /*argv*/)
{
- try
+ try
{
int sv[2];
int rc = ::socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
assert(rc >= 0);
-
+
// Set non-blocking
rc = ::fcntl(sv[0], F_SETFL, O_NONBLOCK);
assert(rc >= 0);
rc = ::fcntl(sv[1], F_SETFL, O_NONBLOCK);
assert(rc >= 0);
-
+
// Make up a large string
string testString = "This is only a test ... 1,2,3,4,5,6,7,8,9,10;";
for (int i = 0; i < 6; i++)
@@ -97,32 +97,32 @@ int main(int /*argc*/, char** /*argv*/)
// Write as much as we can to socket 0
int bytesWritten = writeALot(sv[0], testString);
cout << "Wrote(0): " << bytesWritten << " bytes\n";
-
+
// Read as much as we can from socket 1
bytesRead = readALot(sv[1]);
assert(bytesRead == bytesWritten);
cout << "Read(1): " << bytesRead << " bytes\n";
auto_ptr<Poller> poller(new Poller);
-
+
PosixIOHandle f0(sv[0]);
PosixIOHandle f1(sv[1]);
PollerHandle h0(f0);
PollerHandle h1(f1);
-
+
poller->registerHandle(h0);
poller->monitorHandle(h0, Poller::INOUT);
-
+
// h0 should be writable
Poller::Event event = poller->wait();
assert(event.handle == &h0);
assert(event.type == Poller::WRITABLE);
-
+
// Write as much as we can to socket 0
bytesWritten = writeALot(sv[0], testString);
cout << "Wrote(0): " << bytesWritten << " bytes\n";
-
+
// Wait for 500ms - h0 no longer writable
event = poller->wait(500000000);
assert(event.handle == 0);
@@ -133,7 +133,7 @@ int main(int /*argc*/, char** /*argv*/)
event = poller->wait();
assert(event.handle == &h1);
assert(event.type == Poller::READ_WRITABLE);
-
+
bytesRead = readALot(sv[1]);
assert(bytesRead == bytesWritten);
cout << "Read(1): " << bytesRead << " bytes\n";
@@ -147,11 +147,11 @@ int main(int /*argc*/, char** /*argv*/)
// Test multiple interrupts
assert(poller->interrupt(h0) == true);
assert(poller->interrupt(h1) == true);
-
+
// Make sure we can interrupt them again
assert(poller->interrupt(h0) == true);
assert(poller->interrupt(h1) == true);
-
+
// Make sure that they both come out
event = poller->wait();
assert(event.type == Poller::INTERRUPTED);
@@ -170,25 +170,44 @@ int main(int /*argc*/, char** /*argv*/)
event = poller->wait();
assert(event.handle == &h0);
- assert(event.type == Poller::WRITABLE);
+ assert(event.type == Poller::WRITABLE);
// We didn't write anything so it should still be writable
event = poller->wait();
assert(event.handle == &h0);
- assert(event.type == Poller::WRITABLE);
+ assert(event.type == Poller::WRITABLE);
poller->unmonitorHandle(h0, Poller::INOUT);
event = poller->wait(500000000);
assert(event.handle == 0);
-
+
poller->unregisterHandle(h1);
+ assert(poller->interrupt(h1) == false);
+
+ // close the other end to force a disconnect
+ ::close(sv[1]);
+
+ // Now make sure that we are readable followed by disconnected
+ // and after that we never return again
+ poller->monitorHandle(h0, Poller::INOUT);
+ event = poller->wait(500000000);
+ assert(event.handle == &h0);
+ assert(event.type == Poller::READABLE);
+ event = poller->wait(500000000);
+ assert(event.handle == &h0);
+ assert(event.type == Poller::DISCONNECTED);
+ event = poller->wait(1500000000);
+ assert(event.handle == 0);
+
+ // Now we're disconnected monitoring should have no effect at all
+ poller->unmonitorHandle(h0, Poller::INOUT);
+ event = poller->wait(1500000000);
+ assert(event.handle == 0);
+
poller->unregisterHandle(h0);
-
- // Make sure we can't interrupt them now
assert(poller->interrupt(h0) == false);
- assert(poller->interrupt(h1) == false);
-
+
// Test shutdown
poller->shutdown();
event = poller->wait();
diff --git a/qpid/cpp/src/tests/TimerTest.cpp b/qpid/cpp/src/tests/TimerTest.cpp
index f8ab9fb2f3..2642c980ba 100644
--- a/qpid/cpp/src/tests/TimerTest.cpp
+++ b/qpid/cpp/src/tests/TimerTest.cpp
@@ -19,7 +19,7 @@
* under the License.
*
*/
-#include "qpid/broker/Timer.h"
+#include "qpid/sys/Timer.h"
#include "qpid/sys/Monitor.h"
#include "unit_test.h"
#include <math.h>
@@ -28,7 +28,6 @@
#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
-using namespace qpid::broker;
using namespace qpid::sys;
using boost::intrusive_ptr;
using boost::dynamic_pointer_cast;
diff --git a/qpid/cpp/src/tests/acl.py b/qpid/cpp/src/tests/acl.py
index 0cae148769..b62288a769 100755
--- a/qpid/cpp/src/tests/acl.py
+++ b/qpid/cpp/src/tests/acl.py
@@ -23,36 +23,10 @@ import qpid
from qpid.util import connect
from qpid.connection import Connection
from qpid.datatypes import uuid4
-from qpid.testlib import TestBase010, testrunner
+from qpid.testlib import TestBase010
from qmf.console import Session
from qpid.datatypes import Message
-def scan_args(name, default=None, args=sys.argv[1:]):
- if (name in args):
- pos = args.index(name)
- return args[pos + 1]
- elif default:
- return default
- else:
- print "Please specify extra argument: %s" % name
- sys.exit(2)
-
-def extract_args(name, args):
- if (name in args):
- pos = args.index(name)
- del args[pos:pos+2]
- else:
- return None
-
-def get_broker_port():
- return scan_args("--port", "5672")
-
-def get_session(user, passwd):
- socket = connect('127.0.0.1', int(get_broker_port()))
- connection = Connection (sock=socket, username=user, password=passwd)
- connection.start()
- return connection.session(str(uuid4()))
-
class ACLFile:
def __init__(self):
self.f = open('data_dir/policy.acl','w');
@@ -65,6 +39,12 @@ class ACLFile:
class ACLTests(TestBase010):
+ def get_session(self, user, passwd):
+ socket = connect(self.broker.host, self.broker.port)
+ connection = Connection (sock=socket, username=user, password=passwd)
+ connection.start()
+ return connection.session(str(uuid4()))
+
def reload_acl(self):
acl = self.qmf.getObjects(_class="acl")[0]
return acl.reloadACLFile()
@@ -93,7 +73,7 @@ class ACLTests(TestBase010):
self.reload_acl()
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.queue_declare(queue="deny_queue")
except qpid.session.SessionException, e:
@@ -118,7 +98,7 @@ class ACLTests(TestBase010):
self.reload_acl()
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.queue_declare(queue="allow_queue")
except qpid.session.SessionException, e:
@@ -146,7 +126,7 @@ class ACLTests(TestBase010):
self.reload_acl()
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.queue_declare(queue="allow_queue")
except qpid.session.SessionException, e:
@@ -243,21 +223,21 @@ class ACLTests(TestBase010):
self.reload_acl()
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.queue_declare(queue="q1", durable='true', passive='true')
self.fail("ACL should deny queue create request with name=q1 durable=true passive=true");
except qpid.session.SessionException, e:
self.assertEqual(530,e.args[0].error_code)
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.queue_declare(queue="q2", exclusive='true')
self.fail("ACL should deny queue create request with name=q2 exclusive=true");
except qpid.session.SessionException, e:
self.assertEqual(530,e.args[0].error_code)
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.queue_declare(queue="q3", exclusive='true')
@@ -271,14 +251,14 @@ class ACLTests(TestBase010):
self.fail("ACL should deny queue query request for q3");
except qpid.session.SessionException, e:
self.assertEqual(530,e.args[0].error_code)
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.queue_purge(queue="q3")
self.fail("ACL should deny queue purge request for q3");
except qpid.session.SessionException, e:
self.assertEqual(530,e.args[0].error_code)
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.queue_purge(queue="q4")
@@ -291,7 +271,7 @@ class ACLTests(TestBase010):
self.fail("ACL should deny queue delete request for q4");
except qpid.session.SessionException, e:
self.assertEqual(530,e.args[0].error_code)
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.queue_delete(queue="q3")
@@ -319,21 +299,21 @@ class ACLTests(TestBase010):
self.reload_acl()
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.exchange_declare(exchange='testEx', durable='true', passive='true')
self.fail("ACL should deny exchange create request with name=testEx durable=true passive=true");
except qpid.session.SessionException, e:
self.assertEqual(530,e.args[0].error_code)
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.exchange_declare(exchange='ex1', type='direct')
self.fail("ACL should deny exchange create request with name=ex1 type=direct");
except qpid.session.SessionException, e:
self.assertEqual(530,e.args[0].error_code)
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.exchange_declare(exchange='myXml', type='direct')
@@ -347,14 +327,14 @@ class ACLTests(TestBase010):
self.fail("ACL should deny queue query request for q3");
except qpid.session.SessionException, e:
self.assertEqual(530,e.args[0].error_code)
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.exchange_bind(exchange='myEx', queue='q1', binding_key='rk1')
self.fail("ACL should deny exchange bind request with exchange='myEx' queuename='q1' bindingkey='rk1'");
except qpid.session.SessionException, e:
self.assertEqual(530,e.args[0].error_code)
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.exchange_bind(exchange='myXml', queue='q1', binding_key='x')
@@ -366,7 +346,7 @@ class ACLTests(TestBase010):
self.fail("ACL should deny exchange unbind request with exchange='myEx' queuename='q1' bindingkey='rk1'");
except qpid.session.SessionException, e:
self.assertEqual(530,e.args[0].error_code)
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.exchange_unbind(exchange='myXml', queue='q1', binding_key='x')
@@ -379,7 +359,7 @@ class ACLTests(TestBase010):
self.fail("ACL should deny exchange delete request for myEx");
except qpid.session.SessionException, e:
self.assertEqual(530,e.args[0].error_code)
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.exchange_delete(exchange='myXml')
@@ -404,7 +384,7 @@ class ACLTests(TestBase010):
self.reload_acl()
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
@@ -420,14 +400,14 @@ class ACLTests(TestBase010):
self.fail("ACL should deny message subscriber request for queue='q1'");
except qpid.session.SessionException, e:
self.assertEqual(530,e.args[0].error_code)
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.message_subscribe(queue='q2', destination='myq1')
self.fail("ACL should deny message subscriber request for queue='q2'");
except qpid.session.SessionException, e:
self.assertEqual(530,e.args[0].error_code)
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.message_subscribe(queue='q3', destination='myq1')
@@ -453,7 +433,7 @@ class ACLTests(TestBase010):
self.reload_acl()
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.exchange_declare(exchange='myEx', type='topic')
@@ -468,14 +448,14 @@ class ACLTests(TestBase010):
self.fail("ACL should deny message transfer to name=amq.direct routingkey=rk1");
except qpid.session.SessionException, e:
self.assertEqual(530,e.args[0].error_code)
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.message_transfer(destination="amq.topic", message=Message(props,"Test"))
self.fail("ACL should deny message transfer to name=amq.topic");
except qpid.session.SessionException, e:
self.assertEqual(530,e.args[0].error_code)
- session = get_session('bob','bob')
+ session = self.get_session('bob','bob')
try:
session.message_transfer(destination="myEx", message=Message(props,"Test"))
@@ -489,13 +469,4 @@ class ACLTests(TestBase010):
session.message_transfer(destination="amq.direct", message=Message(props,"Test"))
except qpid.session.SessionException, e:
if (530 == e.args[0].error_code):
- self.fail("ACL should allow message transfer to exchange amq.direct");
-
-
-if __name__ == '__main__':
- args = sys.argv[1:]
- #need to remove the extra options from args as test runner doesn't recognize them
- extract_args("--port", args)
- args.append("acl")
-
- if not testrunner.run(args): sys.exit(1)
+ self.fail("ACL should allow message transfer to exchange amq.direct");
diff --git a/qpid/cpp/src/tests/cli_tests.py b/qpid/cpp/src/tests/cli_tests.py
index a6304e8b3e..2af1a20a87 100755
--- a/qpid/cpp/src/tests/cli_tests.py
+++ b/qpid/cpp/src/tests/cli_tests.py
@@ -20,44 +20,21 @@
import sys
import os
-from qpid.testlib import TestBase010, testrunner
+from qpid.testlib import TestBase010
from qpid.datatypes import Message
from qpid.queue import Empty
from time import sleep
-def add_module(args=sys.argv[1:]):
- for a in args:
- if a.startswith("cli"):
- return False
- return True
-
-def scan_args(name, default=None, args=sys.argv[1:]):
- if (name in args):
- pos = args.index(name)
- return args[pos + 1]
- elif default:
- return default
- else:
- print "Please specify extra argument: %s" % name
- sys.exit(2)
-
-def extract_args(name, args):
- if (name in args):
- pos = args.index(name)
- del args[pos:pos+2]
- else:
- return None
-
-def remote_host():
- return scan_args("--remote-host", "localhost")
+class CliTests(TestBase010):
-def remote_port():
- return int(scan_args("--remote-port"))
+ def remote_host(self):
+ return self.defines.get("remote-host", "localhost")
-def cli_dir():
- return scan_args("--cli-dir")
+ def remote_port(self):
+ return int(self.defines["remote-port"])
-class CliTests(TestBase010):
+ def cli_dir(self):
+ return self.defines["cli-dir"]
def makeQueue(self, qname, arguments):
ret = os.system(self.command(" add queue " + qname + " " + arguments))
@@ -150,15 +127,15 @@ class CliTests(TestBase010):
self.startQmf();
qmf = self.qmf
- command = cli_dir() + "/qpid-route dynamic add guest/guest@localhost:%d %s:%d amq.topic" %\
- (testrunner.port, remote_host(), remote_port())
+ command = self.cli_dir() + "/qpid-route dynamic add guest/guest@localhost:%d %s:%d amq.topic" %\
+ (self.broker.port, self.remote_host(), self.remote_port())
ret = os.system(command)
self.assertEqual(ret, 0)
links = qmf.getObjects(_class="link")
found = False
for link in links:
- if link.port == remote_port():
+ if link.port == self.remote_port():
found = True
self.assertEqual(found, True)
@@ -174,18 +151,4 @@ class CliTests(TestBase010):
return None
def command(self, arg = ""):
- return cli_dir() + "/qpid-config -a localhost:%d" % testrunner.port + " " + arg
-
-
-if __name__ == '__main__':
- args = sys.argv[1:]
- #need to remove the extra options from args as test runner doesn't recognise them
- extract_args("--remote-port", args)
- extract_args("--remote-host", args)
- extract_args("--cli-dir", args)
-
- if add_module():
- #add module(s) to run to testrunners args
- args.append("cli_tests")
-
- if not testrunner.run(args): sys.exit(1)
+ return self.cli_dir() + "/qpid-config -a localhost:%d" % self.broker.port + " " + arg
diff --git a/qpid/cpp/src/tests/cluster.mk b/qpid/cpp/src/tests/cluster.mk
index dc592fa4d5..05e18ab9eb 100644
--- a/qpid/cpp/src/tests/cluster.mk
+++ b/qpid/cpp/src/tests/cluster.mk
@@ -29,44 +29,47 @@ if HAVE_LIBCPG
# ais_check checks pre-requisites for cluster tests and runs them if ok.
-TESTS += \
- ais_check \
- run_cluster_tests \
- federated_cluster_test \
+TESTS += \
+ ais_check \
+ test_watchdog \
+ run_cluster_tests \
+ federated_cluster_test \
clustered_replication_test
-
-EXTRA_DIST += \
- ais_check \
- start_cluster \
- stop_cluster \
- restart_cluster \
- cluster_python_tests \
- cluster_python_tests_failing.txt \
- federated_cluster_test \
- clustered_replication_test \
- run_cluster_tests \
- run_long_cluster_tests \
- testlib.py \
- cluster_tests.py \
- long_cluster_tests.py
-
-LONG_TESTS += \
- run_long_cluster_tests \
- start_cluster \
- cluster_python_tests \
+EXTRA_DIST += \
+ ais_check \
+ test_watchdog \
+ start_cluster \
+ stop_cluster \
+ restart_cluster \
+ cluster_python_tests \
+ cluster_python_tests_failing.txt \
+ federated_cluster_test \
+ clustered_replication_test \
+ run_cluster_tests \
+ run_long_cluster_tests \
+ testlib.py \
+ cluster_tests.py \
+ long_cluster_tests.py
+
+LONG_TESTS += \
+ run_long_cluster_tests \
+ start_cluster \
+ cluster_python_tests \
stop_cluster
qpidtest_PROGRAMS += cluster_test
-cluster_test_SOURCES = \
- cluster_test.cpp \
- unit_test.cpp \
- ClusterFixture.cpp \
- ClusterFixture.h \
- ForkedBroker.h \
- ForkedBroker.cpp \
- PartialFailure.cpp \
- ClusterFailover.cpp
+
+cluster_test_SOURCES = \
+ cluster_test.cpp \
+ unit_test.cpp \
+ ClusterFixture.cpp \
+ ClusterFixture.h \
+ ForkedBroker.h \
+ ForkedBroker.cpp \
+ PartialFailure.cpp \
+ ClusterFailover.cpp
+
cluster_test_LDADD=$(lib_client) $(lib_broker) -lboost_unit_test_framework
qpidtest_SCRIPTS += run_cluster_tests cluster_tests.py run_long_cluster_tests long_cluster_tests.py testlib.py
diff --git a/qpid/cpp/src/tests/cluster_tests.py b/qpid/cpp/src/tests/cluster_tests.py
index a9082dddd9..a0b9c420d9 100755
--- a/qpid/cpp/src/tests/cluster_tests.py
+++ b/qpid/cpp/src/tests/cluster_tests.py
@@ -21,10 +21,10 @@
import os, signal, sys, unittest
from testlib import TestBaseCluster
-class ClusterTests(TestBaseCluster):
+class ShortTests(TestBaseCluster):
"""Basic cluster with async store tests"""
- def test_Cluster_01_Initialization(self):
+ def test_01_Initialization(self):
"""Start a single cluster containing several nodes, and stop it again"""
try:
clusterName = "cluster-01"
@@ -34,7 +34,7 @@ class ClusterTests(TestBaseCluster):
self.killAllClusters(True)
raise
- def test_Cluster_02_MultipleClusterInitialization(self):
+ def test_02_MultipleClusterInitialization(self):
"""Start several clusters each with several nodes and stop them again"""
try:
for i in range(0, 5):
@@ -48,7 +48,7 @@ class ClusterTests(TestBaseCluster):
self.killAllClusters(True)
raise
- def test_Cluster_03_AddRemoveNodes(self):
+ def test_03_AddRemoveNodes(self):
"""Create a multi-node cluster, then kill some nodes and add some new ones (not those killed)"""
try:
clusterName = "cluster-03"
@@ -68,7 +68,7 @@ class ClusterTests(TestBaseCluster):
self.killAllClusters(True)
raise
- def test_Cluster_04_RemoveRestoreNodes(self):
+ def test_04_RemoveRestoreNodes(self):
"""Create a multi-node cluster, then kill some of the nodes and restart them"""
try:
clusterName = "cluster-04"
@@ -95,7 +95,7 @@ class ClusterTests(TestBaseCluster):
self.killAllClusters(True)
raise
- def test_Cluster_05_KillAllNodesThenRecover(self):
+ def test_05_KillAllNodesThenRecover(self):
"""Create a multi-node cluster, then kill *all* nodes, then restart the cluster"""
try:
clusterName = "cluster-05"
@@ -107,7 +107,7 @@ class ClusterTests(TestBaseCluster):
self.killAllClusters(True)
raise
- def test_Cluster_06_PublishConsume(self):
+ def test_06_PublishConsume(self):
"""Publish then consume 100 messages from a single cluster"""
try:
dh = TestBaseCluster.DirectExchangeTestHelper(self, "cluster-06", 3, "test-exchange-06", ["test-queue-06"])
@@ -117,7 +117,7 @@ class ClusterTests(TestBaseCluster):
self.killAllClusters(True)
raise
- def test_Cluster_07_MultiplePublishConsume(self):
+ def test_07_MultiplePublishConsume(self):
"""Staggered publish and consume on a single cluster"""
try:
dh = TestBaseCluster.DirectExchangeTestHelper(self, "cluster-07", 3, "test-exchange-07", ["test-queue-07"])
@@ -135,7 +135,7 @@ class ClusterTests(TestBaseCluster):
self.killAllClusters(True)
raise
- def test_Cluster_08_MsgPublishConsumeAddRemoveNodes(self):
+ def test_08_MsgPublishConsumeAddRemoveNodes(self):
"""Staggered publish and consume interleaved with adding and removing nodes on a single cluster"""
try:
dh = TestBaseCluster.DirectExchangeTestHelper(self, "cluster-08", 3, "test-exchange-08", ["test-queue-08"])
@@ -159,7 +159,7 @@ class ClusterTests(TestBaseCluster):
self.killAllClusters(True)
raise
- def test_Cluster_09_MsgPublishConsumeRemoveRestoreNodes(self):
+ def test_09_MsgPublishConsumeRemoveRestoreNodes(self):
"""Publish and consume messages interleaved with adding and restoring previous nodes on a single cluster"""
try:
dh = TestBaseCluster.DirectExchangeTestHelper(self, "cluster-09", 6, "test-exchange-09", ["test-queue-09"])
@@ -184,7 +184,7 @@ class ClusterTests(TestBaseCluster):
self.killAllClusters(True)
raise
- def test_Cluster_10_LinearNodeKillCreateProgression(self):
+ def test_10_LinearNodeKillCreateProgression(self):
"""Publish and consume messages while linearly killing all original nodes and replacing them with new ones"""
try:
dh = TestBaseCluster.DirectExchangeTestHelper(self, "cluster-10", 4, "test-exchange-10", ["test-queue-10"])
@@ -204,7 +204,7 @@ class ClusterTests(TestBaseCluster):
self.killAllClusters(True)
raise
- def test_Cluster_11_CircularNodeKillRestoreProgression(self):
+ def test_11_CircularNodeKillRestoreProgression(self):
"""Publish and consume messages while circularly killing all original nodes and restoring them again"""
try:
dh = TestBaseCluster.DirectExchangeTestHelper(self, "cluster-11", 4, "test-exchange-11", ["test-queue-11"])
@@ -226,7 +226,7 @@ class ClusterTests(TestBaseCluster):
self.killAllClusters(True)
raise
- def test_Cluster_12_KillAllNodesRecoverMessages(self):
+ def test_12_KillAllNodesRecoverMessages(self):
"""Create a cluster, add and delete messages, kill all nodes then recover cluster and messages"""
if not self._storeEnable:
print " No store loaded, skipped"
@@ -253,7 +253,7 @@ class ClusterTests(TestBaseCluster):
self.killAllClusters(True)
raise
- def test_Cluster_13_TopicExchange(self):
+ def test_13_TopicExchange(self):
"""Create topic exchange in a cluster and make sure it behaves correctly"""
try:
topicQueueNameKeyList = {"test-queue-13-A" : "#.A", "test-queue-13-B" : "*.B", "test-queue-13-C" : "C.#", "test-queue-13-D" : "D.*"}
@@ -290,7 +290,80 @@ class ClusterTests(TestBaseCluster):
self.killAllClusters(True)
raise
- def test_Cluster_14_FanoutExchange(self):
+ def test_14_FanoutExchange(self):
+ """Create fanout exchange in a cluster and make sure it behaves correctly"""
+ try:
+ fanoutQueueNameList = ["test-queue-14-A", "test-queue-14-B", "test-queue-14-C"]
+ fh = TestBaseCluster.FanoutExchangeTestHelper(self, "cluster-14", 4, "test-exchange-14", fanoutQueueNameList)
+ # Place initial 20 messages, retrieve 10
+ fh.sendMsgs(20)
+ fh.receiveMsgs(10)
+ # Kill and add some nodes
+ fh.killNode(0)
+ fh.killNode(2)
+ fh.addNodes(2)
+ # Place another 20 messages, retrieve 20
+ fh.sendMsgs(20)
+ fh.receiveMsgs(20)
+ # Kill and add another node
+ fh.killNode(4)
+ fh.addNodes()
+ # Add another 2 queues
+ fh.addQueues(["test-queue-14-D", "test-queue-14-E"])
+ # Place another 20 messages, retrieve 20
+ fh.sendMsgs(20)
+ fh.receiveMsgs(20)
+ # Kill all nodes but one
+ fh.killNode(1)
+ fh.killNode(3)
+ fh.killNode(6)
+ # Check messages
+ fh.finalizeTest()
+ except:
+ self.killAllClusters(True)
+ raise
+
+class LongTests(TestBaseCluster):
+ """Basic cluster with async store tests"""
+
+ def test_01_TopicExchange(self):
+ """Create topic exchange in a cluster and make sure it behaves correctly"""
+ try:
+ topicQueueNameKeyList = {"test-queue-13-A" : "#.A", "test-queue-13-B" : "*.B", "test-queue-13-C" : "C.#", "test-queue-13-D" : "D.*"}
+ th = TestBaseCluster.TopicExchangeTestHelper(self, "cluster-13", 4, "test-exchange-13", topicQueueNameKeyList)
+ # Place initial messages
+ th.sendMsgs("C.hello.A", 10)
+ th.sendMsgs("hello.world", 10) # matches none of the queues
+ th.sendMsgs("D.hello.A", 10)
+ th.sendMsgs("hello.B", 20)
+ th.sendMsgs("D.hello", 20)
+ # Kill and add some nodes
+ th.killNode(0)
+ th.killNode(2)
+ th.addNodes(2)
+ # Pull 10 messages from each queue
+ th.receiveMsgs(10)
+ # Kill and add another node
+ th.killNode(4)
+ th.addNodes()
+ # Add two more queues
+ th.addQueues({"test-queue-13-E" : "#.bye.A", "test-queue-13-F" : "#.bye.B"})
+ # Place more messages
+ th.sendMsgs("C.bye.A", 10)
+ th.sendMsgs("hello.bye", 20) # matches none of the queues
+ th.sendMsgs("hello.bye.B", 20)
+ th.sendMsgs("D.bye", 20)
+ # Kill all nodes but one
+ th.killNode(1)
+ th.killNode(3)
+ th.killNode(6)
+ # Pull all remaining messages from each queue and check messages
+ th.finalizeTest()
+ except:
+ self.killAllClusters(True)
+ raise
+
+ def test_02_FanoutExchange(self):
"""Create fanout exchange in a cluster and make sure it behaves correctly"""
try:
fanoutQueueNameList = ["test-queue-14-A", "test-queue-14-B", "test-queue-14-C"]
diff --git a/qpid/cpp/src/tests/dlclose_noop.c b/qpid/cpp/src/tests/dlclose_noop.c
index ba2fa75891..b78cf486d8 100644
--- a/qpid/cpp/src/tests/dlclose_noop.c
+++ b/qpid/cpp/src/tests/dlclose_noop.c
@@ -26,5 +26,5 @@
*/
#include <stdio.h>
-void* dlclose(void* handle) {}
+void* dlclose(void* handle) { return 0; }
diff --git a/qpid/cpp/src/tests/federation.py b/qpid/cpp/src/tests/federation.py
index 9b0be8f979..daf831f3ed 100755
--- a/qpid/cpp/src/tests/federation.py
+++ b/qpid/cpp/src/tests/federation.py
@@ -19,48 +19,25 @@
#
import sys
-from qpid.testlib import TestBase010, testrunner
+from qpid.testlib import TestBase010
from qpid.datatypes import Message
from qpid.queue import Empty
from time import sleep
-def add_module(args=sys.argv[1:]):
- for a in args:
- if a.startswith("federation"):
- return False
- return True
-
-def scan_args(name, default=None, args=sys.argv[1:]):
- if (name in args):
- pos = args.index(name)
- return args[pos + 1]
- elif default:
- return default
- else:
- print "Please specify extra argument: %s" % name
- sys.exit(2)
-
-def extract_args(name, args):
- if (name in args):
- pos = args.index(name)
- del args[pos:pos+2]
- else:
- return None
-
-def remote_host():
- return scan_args("--remote-host", "localhost")
+class FederationTests(TestBase010):
-def remote_port():
- return int(scan_args("--remote-port"))
+ def remote_host(self):
+ return self.defines.get("remote-host", "localhost")
-class FederationTests(TestBase010):
+ def remote_port(self):
+ return int(self.defines["remote-port"])
def test_bridge_create_and_close(self):
self.startQmf();
qmf = self.qmf
broker = qmf.getObjects(_class="broker")[0]
- result = broker.connect(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
self.assertEqual(result.status, 0)
link = qmf.getObjects(_class="link")[0]
@@ -84,7 +61,7 @@ class FederationTests(TestBase010):
self.startQmf()
qmf = self.qmf
broker = qmf.getObjects(_class="broker")[0]
- result = broker.connect(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
self.assertEqual(result.status, 0)
link = qmf.getObjects(_class="link")[0]
@@ -101,7 +78,7 @@ class FederationTests(TestBase010):
sleep(6)
#send messages to remote broker and confirm it is routed to local broker
- r_conn = self.connect(host=remote_host(), port=remote_port())
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
r_session = r_conn.session("test_pull_from_exchange")
for i in range(1, 11):
@@ -131,7 +108,7 @@ class FederationTests(TestBase010):
self.startQmf()
qmf = self.qmf
broker = qmf.getObjects(_class="broker")[0]
- result = broker.connect(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
self.assertEqual(result.status, 0)
link = qmf.getObjects(_class="link")[0]
@@ -141,7 +118,7 @@ class FederationTests(TestBase010):
bridge = qmf.getObjects(_class="bridge")[0]
#setup queue to receive messages from remote broker
- r_conn = self.connect(host=remote_host(), port=remote_port())
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
r_session = r_conn.session("test_push_to_exchange")
r_session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
r_session.exchange_bind(queue="fed1", exchange="amq.fanout")
@@ -175,7 +152,7 @@ class FederationTests(TestBase010):
session = self.session
#setup queue on remote broker and add some messages
- r_conn = self.connect(host=remote_host(), port=remote_port())
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
r_session = r_conn.session("test_pull_from_queue")
r_session.queue_declare(queue="my-bridge-queue", auto_delete=True)
for i in range(1, 6):
@@ -191,7 +168,7 @@ class FederationTests(TestBase010):
self.startQmf()
qmf = self.qmf
broker = qmf.getObjects(_class="broker")[0]
- result = broker.connect(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
self.assertEqual(result.status, 0)
link = qmf.getObjects(_class="link")[0]
@@ -227,7 +204,7 @@ class FederationTests(TestBase010):
self.assertEqual(len(qmf.getObjects(_class="link")), 0)
def test_tracing_automatic(self):
- remoteUrl = "%s:%d" % (remote_host(), remote_port())
+ remoteUrl = "%s:%d" % (self.remote_host(), self.remote_port())
self.startQmf()
l_broker = self.qmf_broker
r_broker = self.qmf.addBroker(remoteUrl)
@@ -235,8 +212,8 @@ class FederationTests(TestBase010):
l_brokerObj = self.qmf.getObjects(_class="broker", _broker=l_broker)[0]
r_brokerObj = self.qmf.getObjects(_class="broker", _broker=r_broker)[0]
- l_res = l_brokerObj.connect(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp")
- r_res = r_brokerObj.connect(testrunner.host, testrunner.port, False, "PLAIN", "guest", "guest", "tcp")
+ l_res = l_brokerObj.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ r_res = r_brokerObj.connect(self.broker.host, self.broker.port, False, "PLAIN", "guest", "guest", "tcp")
self.assertEqual(l_res.status, 0)
self.assertEqual(r_res.status, 0)
@@ -268,7 +245,7 @@ class FederationTests(TestBase010):
queue = session.incoming("f1")
#setup queue on remote broker and add some messages
- r_conn = self.connect(host=remote_host(), port=remote_port())
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
r_session = r_conn.session("test_trace")
for i in range(1, 11):
dp = r_session.delivery_properties(routing_key="key")
@@ -291,7 +268,7 @@ class FederationTests(TestBase010):
self.startQmf()
qmf = self.qmf
broker = qmf.getObjects(_class="broker")[0]
- result = broker.connect(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
self.assertEqual(result.status, 0)
link = qmf.getObjects(_class="link")[0]
@@ -308,7 +285,7 @@ class FederationTests(TestBase010):
sleep(6)
#send messages to remote broker and confirm it is routed to local broker
- r_conn = self.connect(host=remote_host(), port=remote_port())
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
r_session = r_conn.session("test_tracing")
trace = [None, "exclude-me", "a,exclude-me,b", "also-exclude-me,c", "dont-exclude-me"]
@@ -341,7 +318,7 @@ class FederationTests(TestBase010):
def test_dynamic_fanout(self):
session = self.session
- r_conn = self.connect(host=remote_host(), port=remote_port())
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
r_session = r_conn.session("test_dynamic_fanout")
session.exchange_declare(exchange="fed.fanout", type="fanout")
@@ -350,7 +327,7 @@ class FederationTests(TestBase010):
self.startQmf()
qmf = self.qmf
broker = qmf.getObjects(_class="broker")[0]
- result = broker.connect(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
self.assertEqual(result.status, 0)
link = qmf.getObjects(_class="link")[0]
@@ -388,7 +365,7 @@ class FederationTests(TestBase010):
def test_dynamic_direct(self):
session = self.session
- r_conn = self.connect(host=remote_host(), port=remote_port())
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
r_session = r_conn.session("test_dynamic_direct")
session.exchange_declare(exchange="fed.direct", type="direct")
@@ -397,7 +374,7 @@ class FederationTests(TestBase010):
self.startQmf()
qmf = self.qmf
broker = qmf.getObjects(_class="broker")[0]
- result = broker.connect(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
self.assertEqual(result.status, 0)
link = qmf.getObjects(_class="link")[0]
@@ -435,7 +412,7 @@ class FederationTests(TestBase010):
def test_dynamic_topic(self):
session = self.session
- r_conn = self.connect(host=remote_host(), port=remote_port())
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
r_session = r_conn.session("test_dynamic_topic")
session.exchange_declare(exchange="fed.topic", type="topic")
@@ -444,7 +421,7 @@ class FederationTests(TestBase010):
self.startQmf()
qmf = self.qmf
broker = qmf.getObjects(_class="broker")[0]
- result = broker.connect(remote_host(), remote_port(), False, "PLAIN", "guest", "guest", "tcp")
+ result = broker.connect(self.remote_host(), self.remote_port(), False, "PLAIN", "guest", "guest", "tcp")
self.assertEqual(result.status, 0)
link = qmf.getObjects(_class="link")[0]
@@ -478,7 +455,131 @@ class FederationTests(TestBase010):
sleep(3)
self.assertEqual(len(qmf.getObjects(_class="bridge")), 0)
self.assertEqual(len(qmf.getObjects(_class="link")), 0)
+
+ def test_dynamic_topic_reorigin(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_topic_reorigin")
+
+ session.exchange_declare(exchange="fed.topic_reorigin", type="topic")
+ r_session.exchange_declare(exchange="fed.topic_reorigin", type="topic")
+
+ session.exchange_declare(exchange="fed.topic_reorigin_2", type="topic")
+ r_session.exchange_declare(exchange="fed.topic_reorigin_2", type="topic")
+
+ 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)
+
+ session.queue_declare(queue="fed2", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed2", exchange="fed.topic_reorigin_2", binding_key="ft-key.one.#")
+ self.subscribe(queue="fed2", destination="f2")
+ queue2 = session.incoming("f2")
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.topic_reorigin", "fed.topic_reorigin", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+ result = link.bridge(False, "fed.topic_reorigin_2", "fed.topic_reorigin_2", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+ bridge2 = qmf.getObjects(_class="bridge")[1]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.topic_reorigin", binding_key="ft-key.#")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="ft-key.one.two")
+ r_session.message_transfer(destination="fed.topic_reorigin", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ 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 = bridge2.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ sleep(3)
+ self.assertEqual(len(qmf.getObjects(_class="bridge")), 0)
+ self.assertEqual(len(qmf.getObjects(_class="link")), 0)
+
+
+ def test_dynamic_direct_reorigin(self):
+ session = self.session
+ r_conn = self.connect(host=self.remote_host(), port=self.remote_port())
+ r_session = r_conn.session("test_dynamic_direct_reorigin")
+
+ session.exchange_declare(exchange="fed.direct_reorigin", type="direct")
+ r_session.exchange_declare(exchange="fed.direct_reorigin", type="direct")
+
+ session.exchange_declare(exchange="fed.direct_reorigin_2", type="direct")
+ r_session.exchange_declare(exchange="fed.direct_reorigin_2", type="direct")
+
+ 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)
+
+ session.queue_declare(queue="fed2", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed2", exchange="fed.direct_reorigin_2", binding_key="ft-key.two")
+ self.subscribe(queue="fed2", destination="f2")
+ queue2 = session.incoming("f2")
+
+ link = qmf.getObjects(_class="link")[0]
+ result = link.bridge(False, "fed.direct_reorigin", "fed.direct_reorigin", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+ result = link.bridge(False, "fed.direct_reorigin_2", "fed.direct_reorigin_2", "", "", "", False, False, True, 0)
+ self.assertEqual(result.status, 0)
+
+ bridge = qmf.getObjects(_class="bridge")[0]
+ bridge2 = qmf.getObjects(_class="bridge")[1]
+ sleep(5)
+
+ session.queue_declare(queue="fed1", exclusive=True, auto_delete=True)
+ session.exchange_bind(queue="fed1", exchange="fed.direct_reorigin", binding_key="ft-key.one")
+ self.subscribe(queue="fed1", destination="f1")
+ queue = session.incoming("f1")
+
+ for i in range(1, 11):
+ dp = r_session.delivery_properties(routing_key="ft-key.one")
+ r_session.message_transfer(destination="fed.direct_reorigin", message=Message(dp, "Message %d" % i))
+
+ for i in range(1, 11):
+ msg = queue.get(timeout=5)
+ self.assertEqual("Message %d" % i, msg.body)
+ 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 = bridge2.close()
+ self.assertEqual(result.status, 0)
+ result = link.close()
+ self.assertEqual(result.status, 0)
+
+ sleep(3)
+ self.assertEqual(len(qmf.getObjects(_class="bridge")), 0)
+ self.assertEqual(len(qmf.getObjects(_class="link")), 0)
+
+
+
def getProperty(self, msg, name):
for h in msg.headers:
if hasattr(h, name): return getattr(h, name)
@@ -489,16 +590,3 @@ class FederationTests(TestBase010):
if headers:
return headers[name]
return None
-
-
-if __name__ == '__main__':
- args = sys.argv[1:]
- #need to remove the extra options from args as test runner doesn't recognise them
- extract_args("--remote-port", args)
- extract_args("--remote-host", args)
-
- if add_module():
- #add module(s) to run to testrunners args
- args.append("federation")
-
- if not testrunner.run(args): sys.exit(1)
diff --git a/qpid/cpp/src/tests/python_tests b/qpid/cpp/src/tests/python_tests
index cae1aefb4a..e3906c1685 100755
--- a/qpid/cpp/src/tests/python_tests
+++ b/qpid/cpp/src/tests/python_tests
@@ -23,12 +23,11 @@
QPID_PORT=${QPID_PORT:-5672}
PYTHON_TESTS=${PYTHON_TESTS:-$*}
QPID_PYTHON_DIR=${QPID_PYTHON_DIR:-`dirname $0`/../../../python}
-SPEC=${SPEC:-0-10-errata}
-FAILING=${FAILING:-cpp_failing_0-10.txt}
+FAILING=${FAILING:-/dev/null}
-if test -d $QPID_PYTHON_DIR; then
+if test -d $QPID_PYTHON_DIR; then
cd $QPID_PYTHON_DIR
- ./run-tests --skip-self-test -v -s $SPEC -I $FAILING -b localhost:$QPID_PORT $PYTHON_TESTS || { echo "FAIL python tests for $SPEC"; exit 1; }
+ ./qpid-python-test -b localhost:$QPID_PORT -I $FAILING $PYTHON_TESTS || { echo "FAIL python tests"; exit 1; }
else
echo "WARNING: No python tests. $QPID_PYTHON_DIR not found."
fi
diff --git a/qpid/cpp/src/tests/qrsh.cpp b/qpid/cpp/src/tests/qrsh.cpp
new file mode 100644
index 0000000000..2d71b600d5
--- /dev/null
+++ b/qpid/cpp/src/tests/qrsh.cpp
@@ -0,0 +1,166 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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;
+
+
+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;
+};
+
+
+
+
+/*
+ * 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/qpid/cpp/src/tests/qrsh_run.cpp b/qpid/cpp/src/tests/qrsh_run.cpp
new file mode 100644
index 0000000000..cfdd0cef80
--- /dev/null
+++ b/qpid/cpp/src/tests/qrsh_run.cpp
@@ -0,0 +1,321 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/qpid/cpp/src/tests/qrsh_server.cpp b/qpid/cpp/src/tests/qrsh_server.cpp
new file mode 100644
index 0000000000..4b80212eae
--- /dev/null
+++ b/qpid/cpp/src/tests/qrsh_server.cpp
@@ -0,0 +1,1062 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 <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;
+
+
+
+
+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 );
+}
+
+
+
+
+
+/*
+ * 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/qpid/cpp/src/tests/qrsh_utils/10_all b/qpid/cpp/src/tests/qrsh_utils/10_all
new file mode 100755
index 0000000000..7b486ea672
--- /dev/null
+++ b/qpid/cpp/src/tests/qrsh_utils/10_all
@@ -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.
+ *
+ */
+
+#! /bin/bash
+
+echo "Asking all servers to say their names... "
+qrsh 127.0.0.1 5813 \
+ all sayName
+
+
+
+
diff --git a/qpid/cpp/src/tests/qrsh_utils/1_remote_run b/qpid/cpp/src/tests/qrsh_utils/1_remote_run
new file mode 100755
index 0000000000..5b9b307bba
--- /dev/null
+++ b/qpid/cpp/src/tests/qrsh_utils/1_remote_run
@@ -0,0 +1,26 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#! /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/qpid/cpp/src/tests/qrsh_utils/2_forever b/qpid/cpp/src/tests/qrsh_utils/2_forever
new file mode 100755
index 0000000000..5528b0e4d8
--- /dev/null
+++ b/qpid/cpp/src/tests/qrsh_utils/2_forever
@@ -0,0 +1,26 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#! /bin/bash
+
+
+./qrsh 127.0.0.1 5813 \
+ mrg23 command_2 /home/mick/redhat/qrsh/qrsh_run/forever foo bar baz
diff --git a/qpid/cpp/src/tests/qrsh_utils/3_kill_it b/qpid/cpp/src/tests/qrsh_utils/3_kill_it
new file mode 100755
index 0000000000..afc7a03c9d
--- /dev/null
+++ b/qpid/cpp/src/tests/qrsh_utils/3_kill_it
@@ -0,0 +1,27 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#! /bin/bash
+
+echo "Killing command 2... "
+./qrsh 127.0.0.1 5813 \
+ mrg23 exec kill -9 command_2
+
diff --git a/qpid/cpp/src/tests/qrsh_utils/4_wait_for_it b/qpid/cpp/src/tests/qrsh_utils/4_wait_for_it
new file mode 100755
index 0000000000..a4dc0da1ce
--- /dev/null
+++ b/qpid/cpp/src/tests/qrsh_utils/4_wait_for_it
@@ -0,0 +1,26 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#! /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/qpid/cpp/src/tests/qrsh_utils/5_exited b/qpid/cpp/src/tests/qrsh_utils/5_exited
new file mode 100755
index 0000000000..4fec1dcc79
--- /dev/null
+++ b/qpid/cpp/src/tests/qrsh_utils/5_exited
@@ -0,0 +1,64 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/qpid/cpp/src/tests/qrsh_utils/6_get b/qpid/cpp/src/tests/qrsh_utils/6_get
new file mode 100755
index 0000000000..4b35ca98e6
--- /dev/null
+++ b/qpid/cpp/src/tests/qrsh_utils/6_get
@@ -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.
+ *
+ */
+
+#! /bin/bash
+
+echo "getting /tmp/foo ..."
+./qrsh 127.0.0.1 5813 \
+ mrg23 get /tmp/foo
+
+
+
diff --git a/qpid/cpp/src/tests/qrsh_utils/7_get_output b/qpid/cpp/src/tests/qrsh_utils/7_get_output
new file mode 100755
index 0000000000..59911089ec
--- /dev/null
+++ b/qpid/cpp/src/tests/qrsh_utils/7_get_output
@@ -0,0 +1,44 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#! /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/qpid/cpp/src/tests/qrsh_utils/8_any b/qpid/cpp/src/tests/qrsh_utils/8_any
new file mode 100755
index 0000000000..2a922ea0e0
--- /dev/null
+++ b/qpid/cpp/src/tests/qrsh_utils/8_any
@@ -0,0 +1,43 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#! /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/qpid/cpp/src/tests/qrsh_utils/9_alias b/qpid/cpp/src/tests/qrsh_utils/9_alias
new file mode 100755
index 0000000000..a4cfdfdf9a
--- /dev/null
+++ b/qpid/cpp/src/tests/qrsh_utils/9_alias
@@ -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.
+ *
+ */
+
+#! /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/qpid/cpp/src/tests/qrsh_utils/qrsh_example_command.cpp b/qpid/cpp/src/tests/qrsh_utils/qrsh_example_command.cpp
new file mode 100644
index 0000000000..386e2f73f0
--- /dev/null
+++ b/qpid/cpp/src/tests/qrsh_utils/qrsh_example_command.cpp
@@ -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.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+
+
+
+main ( int argc, char ** argv )
+{
+ 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;
+}
+
+
+
+
diff --git a/qpid/cpp/src/tests/qrsh_utils/qrsh_forever.cpp b/qpid/cpp/src/tests/qrsh_utils/qrsh_forever.cpp
new file mode 100644
index 0000000000..191a9bca11
--- /dev/null
+++ b/qpid/cpp/src/tests/qrsh_utils/qrsh_forever.cpp
@@ -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.
+ *
+ */
+
+
+#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/qpid/cpp/src/tests/qrsh_utils/qsh_doc.txt b/qpid/cpp/src/tests/qrsh_utils/qsh_doc.txt
new file mode 100644
index 0000000000..ad5990b38b
--- /dev/null
+++ b/qpid/cpp/src/tests/qrsh_utils/qsh_doc.txt
@@ -0,0 +1,309 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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/qpid/cpp/src/tests/run_acl_tests b/qpid/cpp/src/tests/run_acl_tests
index 1aecf4a7ee..d9b654c7cd 100755
--- a/qpid/cpp/src/tests/run_acl_tests
+++ b/qpid/cpp/src/tests/run_acl_tests
@@ -53,9 +53,9 @@ if test -d ${PYTHON_DIR} ; then
cp $srcdir/policy.acl $DATA_DIR
start_brokers
echo "Running acl tests using brokers on ports $LOCAL_PORT"
- PYTHONPATH=$PYTHON_DIR
+ PYTHONPATH=$PYTHON_DIR:$srcdir
export PYTHONPATH
- $srcdir/acl.py -v -s $srcdir/../../../specs/amqp.0-10-qpid-errata.xml -b localhost:$LOCAL_PORT --port $LOCAL_PORT || EXITCODE=1
+ $PYTHON_DIR/qpid-python-test -b localhost:$LOCAL_PORT -m acl || EXITCODE=1
stop_brokers || EXITCODE=1
test_loading_acl_from_absolute_path || EXITCODE=1
rm -rf $DATA_DIR
diff --git a/qpid/cpp/src/tests/run_cli_tests b/qpid/cpp/src/tests/run_cli_tests
index 3d1d1e2868..ea0d591176 100755
--- a/qpid/cpp/src/tests/run_cli_tests
+++ b/qpid/cpp/src/tests/run_cli_tests
@@ -41,9 +41,9 @@ stop_brokers() {
if test -d ${PYTHON_DIR} ; then
start_brokers
echo "Running CLI tests using brokers on ports $LOCAL_PORT $REMOTE_PORT"
- PYTHONPATH=${PYTHON_DIR}
+ PYTHONPATH=${PYTHON_DIR}:${MY_DIR}
export PYTHONPATH
- ${MY_DIR}/cli_tests.py -v -s ${MY_DIR}/../../../specs/amqp.0-10-qpid-errata.xml -b localhost:$LOCAL_PORT --remote-port $REMOTE_PORT --cli-dir $CLI_DIR $@
+ ${PYTHON_DIR}/qpid-python-test -m cli_tests -b localhost:$LOCAL_PORT -Dremote-port=$REMOTE_PORT -Dcli-dir=$CLI_DIR $@
RETCODE=$?
stop_brokers
if test x$RETCODE != x0; then
diff --git a/qpid/cpp/src/tests/run_cluster_tests b/qpid/cpp/src/tests/run_cluster_tests
index d86e3db134..8b039346db 100755
--- a/qpid/cpp/src/tests/run_cluster_tests
+++ b/qpid/cpp/src/tests/run_cluster_tests
@@ -19,13 +19,6 @@
# under the License.
#
-if test -z $1; then
- CLUSTER_TEST=cluster_tests.py
-else
- CLUSTER_TEST=$1
- echo "Running ${CLUSTER_TEST}..."
-fi
-
# Check that top_builddir and srcdir are set
# If not, assume local run from test dir
if [ -z ${top_builddir} -o -z ${srcdir} ]; then
@@ -33,22 +26,34 @@ if [ -z ${top_builddir} -o -z ${srcdir} ]; then
top_builddir=${srcdir}/../../
fi
TEST_DIR=${top_builddir}/src/tests
+PYTHON_DIR=${srcdir}/../../../python
+
+if test -z $1; then
+ CLUSTER_TEST="${PYTHON_DIR}/qpid-python-test -m cluster_tests cluster_tests.ShortTests.\*"
+else
+ CLUSTER_TEST="${PYTHON_DIR}/qpid-python-test -m cluster_tests cluster_tests.LongTests.\*"
+ echo "Running $1..."
+fi
# Check AIS requirements
id -nG | grep '\<ais\>' > /dev/null || NOGROUP="You are not a member of the ais group."
-ps -u root | grep 'aisexec\|corosync' > /dev/null || NOAISEXEC="The aisexec or corosync daemon is not running as root"
+ps -u root | grep 'aisexec\|corosync' > /dev/null || NOAISEXEC="The aisexec or corosync daemon is not running as root."
+if ! test -d ${PYTHON_DIR}; then
+ NO_PYTHON_DIR="PYTHON_DIR=\"${PYTHON_DIR}\" not found or does not exist."
+fi
-if test -n "${NOGROUP}" -o -n "${NOAISEXEC}"; then
+if test -n "${NOGROUP}" -o -n "${NOAISEXEC}" -o -n "${NO_PYTHON_DIR}"; then
cat <<EOF
======== WARNING: PYTHON CLUSTER TESTS DISABLED ===========
Tests that depend on the openais library (used for clustering)
- will not be run because:
+ and python will not be run because:
${NOGROUP}
${NOAISEXEC}
+ ${NO_PYTHON_DIR}
===========================================================
@@ -65,7 +70,7 @@ fi
# Set up environment for python tests
-export PYTHONPATH=${srcdir}:${srcdir}/../../../python
+export PYTHONPATH=${srcdir}:${PYTHON_DIR}
export QPIDD_EXEC=${top_builddir}/src/qpidd
export CLUSTER_LIB=${top_builddir}/src/.libs/cluster.so
export QPID_CONFIG_EXEC=${srcdir}/../../../python/commands/qpid-config
@@ -98,8 +103,9 @@ export TMP_DATA_DIR
# Run the test
-sg ais -c "${srcdir}/${CLUSTER_TEST} -v"
+sg ais -c "${CLUSTER_TEST}"
RETCODE=$?
+
if test x${RETCODE} != x0; then
exit 1;
fi
diff --git a/qpid/cpp/src/tests/run_federation_tests b/qpid/cpp/src/tests/run_federation_tests
index 28bcc012cc..8640fb728f 100755
--- a/qpid/cpp/src/tests/run_federation_tests
+++ b/qpid/cpp/src/tests/run_federation_tests
@@ -40,13 +40,12 @@ stop_brokers() {
if test -d ${PYTHON_DIR} ; then
start_brokers
echo "Running federation tests using brokers on ports $LOCAL_PORT $REMOTE_PORT"
- PYTHONPATH=${PYTHON_DIR}
+ PYTHONPATH=${PYTHON_DIR}:${MY_DIR}
export PYTHONPATH
- ${MY_DIR}/federation.py -v -s ${MY_DIR}/../../../specs/amqp.0-10-qpid-errata.xml -b localhost:$LOCAL_PORT --remote-port $REMOTE_PORT $@
+ ${PYTHON_DIR}/qpid-python-test -m federation -b localhost:$LOCAL_PORT -Dremote-port=$REMOTE_PORT $@
RETCODE=$?
stop_brokers
if test x$RETCODE != x0; then
echo "FAIL federation tests"; exit 1;
fi
fi
-
diff --git a/qpid/cpp/src/tests/run_long_cluster_tests b/qpid/cpp/src/tests/run_long_cluster_tests
index c96a82c92b..bc1ae8a0c1 100755
--- a/qpid/cpp/src/tests/run_long_cluster_tests
+++ b/qpid/cpp/src/tests/run_long_cluster_tests
@@ -19,4 +19,4 @@
# under the License.
#
-./run_cluster_tests long_cluster_tests.py
+./run_cluster_tests long_cluster_tests
diff --git a/qpid/cpp/src/tests/test_watchdog b/qpid/cpp/src/tests/test_watchdog
new file mode 100755
index 0000000000..c2f33501b8
--- /dev/null
+++ b/qpid/cpp/src/tests/test_watchdog
@@ -0,0 +1,16 @@
+#!/bin/sh
+# Tests for the watchdog plug-in
+
+# Start a broker with watchdog, freeze it with kill -STOP, verify that it is killed.
+export QPID_WATCHDOG_EXE=$PWD/../qpidd_watchdog
+PORT=`../qpidd -dp0 --no-data-dir --auth=no --no-module-dir --load-module $PWD/../.libs/watchdog.so --log-to-file=qpidd_watchdog.log --watchdog-interval 1`
+PID=`../qpidd -cp $PORT`
+kill -STOP $PID
+sleep 2
+
+if kill -0 $PID 2>/dev/null; then
+ echo "Hung process did not die."
+ kill $PID
+else
+ true
+fi
diff --git a/qpid/cpp/src/windows/QpiddBroker.cpp b/qpid/cpp/src/windows/QpiddBroker.cpp
index a22ccba63f..5c6eef48f8 100644
--- a/qpid/cpp/src/windows/QpiddBroker.cpp
+++ b/qpid/cpp/src/windows/QpiddBroker.cpp
@@ -223,6 +223,17 @@ int QpiddBroker::execute (QpiddOptions *options) {
options->broker.port = brokerPtr->getPort("");
std::cout << options->broker.port << std::endl;
+ // Make sure the pid directory exists, creating if needed. LockFile
+ // will throw an exception that makes little sense if it can't create
+ // the file.
+ if (!CreateDirectory(myOptions->control.piddir.c_str(), 0)) {
+ DWORD err = GetLastError();
+ if (err != ERROR_ALREADY_EXISTS)
+ throw qpid::Exception(QPID_MSG("Can't create pid-dir " +
+ myOptions->control.piddir +
+ ": " +
+ qpid::sys::strError(err)));
+ }
qpid::sys::LockFile myPid(brokerPidFile(myOptions->control.piddir,
options->broker.port),
true);
diff --git a/qpid/java/broker/build.xml b/qpid/java/broker/build.xml
index 2a1bbb04e8..3c63c459be 100644
--- a/qpid/java/broker/build.xml
+++ b/qpid/java/broker/build.xml
@@ -60,7 +60,7 @@
<target name="check_velocity_deps">
<uptodate property="velocity.notRequired" targetfile="${velocity.timestamp}">
- <srcfiles dir="${velocity.properties.dir}" includes="LogMessages.properties" />
+ <srcfiles dir="${velocity.properties.dir}" includes="LogMessages**" />
<srcfiles dir="src/velocity/" includes="**/*.java **/*.vm" />
</uptodate>
</target>
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java
index 306dce1057..2afd3c1dc3 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java
@@ -39,8 +39,8 @@ package org.apache.qpid.server;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.Collections;
+import java.util.List;
import javax.management.JMException;
import javax.management.MBeanException;
@@ -50,6 +50,7 @@ import javax.management.ObjectName;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.management.common.mbeans.ManagedBroker;
+import org.apache.qpid.management.common.mbeans.ManagedQueue;
import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor;
import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription;
import org.apache.qpid.server.exchange.Exchange;
@@ -60,9 +61,12 @@ import org.apache.qpid.server.management.AMQManagedObject;
import org.apache.qpid.server.management.ManagedObject;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.queue.AMQQueueFactory;
+import org.apache.qpid.server.queue.AMQQueueMBean;
import org.apache.qpid.server.queue.QueueRegistry;
import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.logging.actors.CurrentActor;
+import org.apache.qpid.server.logging.actors.ManagementActor;
/**
* This MBean implements the broker management interface and exposes the
@@ -77,7 +81,7 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr
private final MessageStore _messageStore;
private final VirtualHost.VirtualHostMBean _virtualHostMBean;
-
+
@MBeanConstructor("Creates the Broker Manager MBean")
public AMQBrokerManagerMBean(VirtualHost.VirtualHostMBean virtualHostMBean) throws JMException
{
@@ -114,26 +118,67 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr
}
/**
- * Returns a Map keyed by QueueName, detailing its associated QueueDepth in bytes.
+ * Returns a list containing the names of the attributes available for the Queue mbeans.
* @since Qpid JMX API 1.3
* @throws IOException
*/
- public Map<String,Long> viewQueueNamesDepths() throws IOException
+ public List<String> retrieveQueueAttributeNames() throws IOException
{
- Map<String,Long> queueDepthMap = new HashMap<String,Long>(_queueRegistry.getQueues().size());
+ List<String> attributeList = new ArrayList<String>();
+ for(String attr : ManagedQueue.QUEUE_ATTRIBUTES)
+ {
+ attributeList.add(attr);
+ }
- String queueName;
- Long queueDepth;
+ Collections.sort(attributeList);
+ return attributeList;
+ }
+
+ /**
+ * Returns a List of Object Lists containing the requested attribute values (in the same sequence requested) for each queue in the virtualhost.
+ * If a particular attribute cant be found or raises an mbean/reflection exception whilst being gathered its value is substituted with the String "-".
+ * @since Qpid JMX API 1.3
+ * @throws IOException
+ */
+ public List<List<Object>> retrieveQueueAttributeValues(String[] attributes) throws IOException
+ {
+ if(_queueRegistry.getQueues().size() == 0)
+ {
+ return new ArrayList<List<Object>>();
+ }
+
+ List<List<Object>> queueAttributesList = new ArrayList<List<Object>>(_queueRegistry.getQueues().size());
+
+ int attributesLength = attributes.length;
+
for(AMQQueue queue : _queueRegistry.getQueues())
{
- queueName = queue.getName().toString();
- queueDepth = queue.getQueueDepth();
+ AMQQueueMBean mbean = (AMQQueueMBean) queue.getManagedObject();
+
+ if(mbean == null)
+ {
+ continue;
+ }
- queueDepthMap.put(queueName,queueDepth);
+ List<Object> attributeValues = new ArrayList<Object>(attributesLength);
+
+ for(int i=0; i < attributesLength; i++)
+ {
+ try
+ {
+ attributeValues.add(mbean.getAttribute(attributes[i]));
+ }
+ catch (Exception e)
+ {
+ attributeValues.add(new String("-"));
+ }
+ }
+
+ queueAttributesList.add(attributeValues);
}
-
- return queueDepthMap;
+
+ return queueAttributesList;
}
/**
@@ -146,6 +191,7 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr
*/
public void createNewExchange(String exchangeName, String type, boolean durable) throws JMException
{
+ CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger()));
try
{
synchronized (_exchangeRegistry)
@@ -167,6 +213,10 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr
{
throw new MBeanException(ex, "Error in creating exchange " + exchangeName);
}
+ finally
+ {
+ CurrentActor.remove();
+ }
}
/**
@@ -182,6 +232,7 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr
// boolean inUse = false;
// Check if there are queue-bindings with the exchange and unregister
// when there are no bindings.
+ CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger()));
try
{
_exchangeRegistry.unregisterExchange(new AMQShortString(exchangeName), false);
@@ -190,6 +241,10 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr
{
throw new MBeanException(ex, "Error in unregistering exchange " + exchangeName);
}
+ finally
+ {
+ CurrentActor.remove();
+ }
}
/**
@@ -209,6 +264,7 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr
throw new JMException("The queue \"" + queueName + "\" already exists.");
}
+ CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger()));
try
{
AMQShortString ownerShortString = null;
@@ -232,6 +288,10 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr
jme.initCause(ex);
throw new MBeanException(jme, "Error in creating queue " + queueName);
}
+ finally
+ {
+ CurrentActor.remove();
+ }
}
private VirtualHost getVirtualHost()
@@ -253,6 +313,7 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr
throw new JMException("The Queue " + queueName + " is not a registerd queue.");
}
+ CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger()));
try
{
queue.delete();
@@ -265,6 +326,10 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr
jme.initCause(ex);
throw new MBeanException(jme, "Error in deleting queue " + queueName);
}
+ finally
+ {
+ CurrentActor.remove();
+ }
}
public ManagedObject getParentObject()
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java
index e963fb23ea..c4c73842bc 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java
@@ -910,8 +910,7 @@ public class AMQChannel
public void setCredit(final long prefetchSize, final int prefetchCount)
{
- //fixme
-// _actor.message(ChannelMessages.CHN_100X(prefetchSize, prefetchCount);
+ _actor.message(ChannelMessages.CHN_1004(prefetchSize, prefetchCount));
_creditManager.setCreditLimits(prefetchSize, prefetchCount);
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java
index 90b4590d4c..7ea7738189 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/ServerConfiguration.java
@@ -55,7 +55,6 @@ public class ServerConfiguration implements SignalHandler
public static final int DEFAULT_BUFFER_WRITE_LIMIT_SIZE = 262144;
public static final boolean DEFAULT_BROKER_CONNECTOR_PROTECTIO_ENABLED = false;
public static final String DEFAULT_STATUS_UPDATES = "on";
- public static final String DEFAULT_ADVANCED_LOCALE = Locale.US.toString();
private static final int DEFAULT_FRAME_SIZE = 65536;
private static final int DEFAULT_PORT = 5672;
@@ -150,7 +149,7 @@ public class ServerConfiguration implements SignalHandler
{
XMLConfiguration vhostConfiguration = new XMLConfiguration((String) thing);
List hosts = vhostConfiguration.getList("virtualhost.name");
- for (int j = 0; j < hosts.size(); j++)
+ for (int j = 0; j < hosts.size(); j++)
{
String name = (String) hosts.get(j);
// Add the keys of the virtual host to the main config then bail out
@@ -221,12 +220,18 @@ public class ServerConfiguration implements SignalHandler
public Locale getLocale()
{
- String localeString = getConfig().getString(ADVANCED_LOCALE, DEFAULT_ADVANCED_LOCALE);
+ String localeString = getConfig().getString(ADVANCED_LOCALE);
// Expecting locale of format langauge_country_variant
+ // If the configuration does not have a defined locale use the JVM default
+ if (localeString == null)
+ {
+ return Locale.getDefault();
+ }
+
String[] parts = localeString.split("_");
- Locale locale = null;
+ Locale locale;
switch (parts.length)
{
case 1:
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java
index b40576f258..036ac4ece8 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java
@@ -43,6 +43,8 @@ import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.virtualhost.VirtualHost;
import org.apache.qpid.server.message.InboundMessage;
+import org.apache.qpid.server.logging.actors.CurrentActor;
+import org.apache.qpid.server.logging.actors.ManagementActor;
public class DirectExchange extends AbstractExchange
{
@@ -129,6 +131,7 @@ public class DirectExchange extends AbstractExchange
throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange.");
}
+ CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger()));
try
{
queue.bind(DirectExchange.this, new AMQShortString(binding), null);
@@ -137,6 +140,10 @@ public class DirectExchange extends AbstractExchange
{
throw new MBeanException(ex);
}
+ finally
+ {
+ CurrentActor.remove();
+ }
}
}// End of MBean class
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java
index b1855e0a40..a4cb3ce44f 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java
@@ -31,6 +31,8 @@ import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.virtualhost.VirtualHost;
import org.apache.qpid.server.message.InboundMessage;
+import org.apache.qpid.server.logging.actors.CurrentActor;
+import org.apache.qpid.server.logging.actors.ManagementActor;
import javax.management.JMException;
import javax.management.MBeanException;
@@ -102,6 +104,7 @@ public class FanoutExchange extends AbstractExchange
throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange.");
}
+ CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger()));
try
{
queue.bind(FanoutExchange.this, new AMQShortString(BINDING_KEY_SUBSTITUTE), null);
@@ -110,6 +113,10 @@ public class FanoutExchange extends AbstractExchange
{
throw new MBeanException(ex);
}
+ finally
+ {
+ CurrentActor.remove();
+ }
}
} // End of MBean class
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java
index 71481ec730..1b5e3448e1 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java
@@ -37,6 +37,8 @@ import org.apache.qpid.server.exchange.topic.TopicMatcherResult;
import org.apache.qpid.server.filter.MessageFilter;
import org.apache.qpid.server.filter.JMSSelectorFilter;
import org.apache.qpid.server.message.InboundMessage;
+import org.apache.qpid.server.logging.actors.CurrentActor;
+import org.apache.qpid.server.logging.actors.ManagementActor;
import javax.management.JMException;
import javax.management.MBeanException;
@@ -351,6 +353,7 @@ public class TopicExchange extends AbstractExchange
throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange.");
}
+ CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger()));
try
{
queue.bind(TopicExchange.this, new AMQShortString(binding), null);
@@ -359,6 +362,10 @@ public class TopicExchange extends AbstractExchange
{
throw new MBeanException(ex);
}
+ finally
+ {
+ CurrentActor.remove();
+ }
}
} // End of MBean class
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/CurrentActor.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/CurrentActor.java
index 374550a72b..df4da2a79e 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/CurrentActor.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/CurrentActor.java
@@ -22,8 +22,8 @@ package org.apache.qpid.server.logging.actors;
import org.apache.qpid.server.logging.LogActor;
-import java.util.LinkedList;
-import java.util.Deque;
+import java.util.EmptyStackException;
+import java.util.Stack;
/**
* The CurrentActor is a ThreadLocal wrapper that allows threads in the broker
@@ -31,78 +31,81 @@ import java.util.Deque;
* reasons:
* 1) We do not have to pass a logging actor around the system
* 2) We can set new actors at the point we have enough information. i.e.
- * - Set a low level ConnectionActor when processing bytes from the wire.
- * - Set a ChannelActor when we are processing the frame
- * - Set a SubscriptionActor when we are handling the subscription.
- *
+ * - Set a low level ConnectionActor when processing bytes from the wire.
+ * - Set a ChannelActor when we are processing the frame
+ * - Set a SubscriptionActor when we are handling the subscription.
+ * <p/>
* The code performing the logging need not worry about what type of actor is
* currently set so can perform its logging. The resulting log entry though will
* contain customised details from the the currently set Actor.
- *
+ * <p/>
* The Actor model also allows the pre-creation of fixed messages so the
* performance impact of the additional logging data is minimised.
- *
+ * <p/>
* This class does not perform any checks to ensure that there is an Actor set
* when calling remove or get. As a result the application developer must ensure
* that they have called set before they attempt to use the actor via get or
* remove the set actor.
- *
+ * <p/>
* The checking of the return via get should not be done as the logging is
* desired. It is preferable to cause the NullPointerException to highlight the
* programming error rather than miss a log message.
- *
+ * <p/>
* The same is true for the remove. A NPE will occur if no set has been called
* highlighting the programming error.
- *
*/
public class CurrentActor
{
- /**
- * The ThreadLocal variable with initialiser
- */
- private static final ThreadLocal<Deque<LogActor>> _currentActor = new ThreadLocal<Deque<LogActor>>()
+ /** The ThreadLocal variable with initialiser */
+ private static final ThreadLocal<Stack<LogActor>> _currentActor = new ThreadLocal<Stack<LogActor>>()
{
// Initialise the CurrentActor to be an empty List
- protected Deque<LogActor> initialValue()
+ protected Stack<LogActor> initialValue()
{
- return new LinkedList<LogActor>();
+ return new Stack<LogActor>();
}
};
/**
* Set a new LogActor to be the Current Actor
- *
+ * <p/>
* This pushes the Actor in to the LIFO Queue
*
* @param actor The new LogActor
*/
public static void set(LogActor actor)
{
- Deque<LogActor> stack = _currentActor.get();
- stack.addFirst(actor);
+ Stack<LogActor> stack = _currentActor.get();
+ stack.push(actor);
}
/**
* Remove the current LogActor.
- *
- * Calling remove without calling set will result in a NoSuchElementException.
- *
+ * <p/>
+ * Calling remove without calling set will result in an EmptyStackException.
*/
public static void remove()
{
- Deque<LogActor> stack = _currentActor.get();
- stack.removeFirst();
+ Stack<LogActor> stack = _currentActor.get();
+ stack.pop();
}
/**
* Return the current head of the list of LogActors.
- *
+ * <p/>
* If there has been no set call then this will return Null.
*
* @return Current LogActor
*/
public static LogActor get()
{
- return _currentActor.get().peek();
+ try
+ {
+ return _currentActor.get().peek();
+ }
+ catch (EmptyStackException ese)
+ {
+ return null;
+ }
}
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/ManagementActor.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/ManagementActor.java
index 58d55a13bb..85fbb8f1ac 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/ManagementActor.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/ManagementActor.java
@@ -20,13 +20,27 @@
*/
package org.apache.qpid.server.logging.actors;
+import org.apache.qpid.server.logging.LogMessage;
+import org.apache.qpid.server.logging.LogSubject;
import org.apache.qpid.server.logging.RootMessageLogger;
import java.text.MessageFormat;
-import java.security.Principal;
+/**
+ * NOTE: This actor is not thread safe.
+ *
+ * Sharing of a ManagementActor instance between threads may result in an
+ * incorrect actor value being logged.
+ *
+ * This is due to the fact that calls to message will dynamically query the
+ * thread name and use that to set the log format during each message() call.
+ *
+ * This is currently not an issue as each MBean operation creates a new Actor
+ * that is unique for each operation.
+ */
public class ManagementActor extends AbstractActor
{
+ String _lastThreadName = null;
/**
* LOG FORMAT for the ManagementActor,
@@ -37,21 +51,68 @@ public class ManagementActor extends AbstractActor
* 1 - User ID
* 2 - IP
*/
- public static final String MANAGEMENT_FORMAT = "mng:{0}({1}@{2})";
+ public static final String MANAGEMENT_FORMAT = "mng:{0}({1})";
- /**
- * //todo Correct interface to provide connection details
- * @param user
- * @param rootLogger The RootLogger to use for this Actor
- */
- public ManagementActor(Principal user, RootMessageLogger rootLogger)
+ /** @param rootLogger The RootLogger to use for this Actor */
+ public ManagementActor(RootMessageLogger rootLogger)
{
super(rootLogger);
- _logString = "["+ MessageFormat.format(MANAGEMENT_FORMAT,
- "<MNG:ConnectionID>",
- user.getName(),
- "<MNG:RemoteAddress>")
- + "] ";
}
+
+ private void updateLogString()
+ {
+ String currentName = Thread.currentThread().getName();
+
+ String actor;
+ // Record the last thread name so we don't have to recreate the log string
+ if (!currentName.equals(_lastThreadName))
+ {
+ _lastThreadName = currentName;
+
+ // Management Thread names have this format.
+ //RMI TCP Connection(2)-169.24.29.116
+ // This is true for both LocalAPI and JMX Connections
+ // However to be defensive lets test.
+
+ String[] split = currentName.split("\\(");
+ if (split.length == 2)
+ {
+ String connectionID = split[1].split("\\)")[0];
+ String ip = currentName.split("-")[1];
+
+ actor = MessageFormat.format(MANAGEMENT_FORMAT,
+ connectionID,
+ ip);
+ }
+ else
+ {
+ // This is a precautionary path as it is not expected to occur
+ // however rather than adjusting the thread name of the two
+ // tests that will use this it is safer all round to do this.
+ // it is also currently used by tests :
+ // AMQBrokerManagerMBeanTest
+ // ExchangeMBeanTest
+ actor = currentName;
+ }
+
+ _logString = "[" + actor + "] ";
+
+ }
+ }
+
+ @Override
+ public void message(LogSubject subject, LogMessage message)
+ {
+ updateLogString();
+ super.message(subject, message);
+ }
+
+ @Override
+ public void message(LogMessage message)
+ {
+ updateLogString();
+ super.message(message);
+ }
+
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/QueueActor.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/QueueActor.java
new file mode 100644
index 0000000000..acac447ff6
--- /dev/null
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/actors/QueueActor.java
@@ -0,0 +1,52 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.logging.actors;
+
+import org.apache.qpid.server.logging.RootMessageLogger;
+import org.apache.qpid.server.logging.subjects.QueueLogSubject;
+import org.apache.qpid.server.queue.AMQQueue;
+
+import java.text.MessageFormat;
+
+/**
+ * This Actor is used when while the queue is performing an asynchronous process
+ * of its queue.
+ */
+public class QueueActor extends AbstractActor
+{
+
+ /**
+ * Create an QueueLogSubject that Logs in the following format.
+ *
+ * @param queue The queue that this Actor is working for
+ * @param rootLogger the Root logger to use.
+ */
+ public QueueActor(AMQQueue queue, RootMessageLogger rootLogger)
+ {
+ super(rootLogger);
+
+ _logString = "[" + MessageFormat.format(QueueLogSubject.LOG_FORMAT,
+ queue.getVirtualHost().getName(),
+ queue.getName()) + "] ";
+
+ }
+}
+ \ No newline at end of file
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/LogMessages.properties b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/LogMessages.properties
new file mode 100644
index 0000000000..eafcb43cc0
--- /dev/null
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/LogMessages.properties
@@ -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.
+#
+# Default File used for all non-defined locales.
+#Broker
+# 0 - Version
+# 1 = Build
+BRK-1001 = Startup : Version: {0} Build: {1}
+# 0 - Transport
+# 1 - Port
+BRK-1002 = Starting : Listening on {0} port {1,number,#}
+# 0 - Transport
+# 1 - Port
+BRK-1003 = Shuting down : {0} port {1,number,#}
+BRK-1004 = Ready
+BRK-1005 = Stopped
+# 0 - path
+BRK-1006 = Using configuration : {0}
+# 0 - path
+BRK-1007 = Using logging configuration : {0}
+
+#ManagementConsole
+MNG-1001 = Startup
+# 0 - Service
+# 1 - Port
+MNG-1002 = Starting : {0} : Listening on port {1,number,#}
+# 0 - Service
+# 1 - Port
+MNG-1003 = Shuting down : {0} : port {1,number,#}
+MNG-1004 = Ready
+MNG-1005 = Stopped
+# 0 - Path
+MNG-1006 = Using SSL Keystore : {0}
+MNG-1007 = Open : User {0}
+MNG-1008 = Close
+
+
+#VirtualHost
+# 0 - name
+VHT-1001 = Created : {0}
+VHT-1002 = Closed
+
+#MessageStore
+# 0 - name
+MST-1001 = Created : {0}
+# 0 - path
+MST-1002 = Store location : {0}
+MST-1003 = Closed
+# 0 - queue name
+MST-1004 = Recovery Start[ : {0}]
+# 0 - count
+# 1 - queue count
+MST-1005 = Recovered {0,number} messages for queue {1}
+# 0 - queue name
+MST-1006 = Recovery Complete[ : {0}]
+
+#Connection
+# 0 - Client id
+# 1 - Protocol Version
+CON-1001 = Open[ : Client ID : {0}][ : Protocol Version : {1}]
+CON-1002 = Close
+
+#Channel
+CHN-1001 = Create
+# 0 - flow
+CHN-1002 = Flow {0}
+CHN-1003 = Close
+# 0 - bytes allowed in prefetch
+# 1 - number of messagse.
+CHN-1004 = Prefetch Size (bytes) {0,number} : Count {1,number}
+
+#Queue
+# 0 - owner
+# 1 - priority
+QUE-1001 = Create :[ Owner: {0}][ AutoDelete][ Durable][ Transient][ Priority: {1,number,#}]
+QUE-1002 = Deleted
+
+#Exchange
+# 0 - type
+# 1 - name
+EXH-1001 = Create :[ Durable] Type: {0} Name: {1}
+EXH-1002 = Deleted
+
+#Binding
+BND-1001 = Create[ : Arguments : {0}]
+BND-1002 = Deleted
+
+#Subscription
+SUB-1001 = Create[ : Durable][ : Arguments : {0}]
+SUB-1002 = Close
+# 0 - The current subscription state
+SUB-1003 = State : {0} \ No newline at end of file
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/LogMessages_en_US.properties b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/LogMessages_en_US.properties
index 24df17683f..5ced7cc0b9 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/LogMessages_en_US.properties
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/LogMessages_en_US.properties
@@ -212,6 +212,9 @@ MNG-1004 = Ready
MNG-1005 = Stopped
# 0 - Path
MNG-1006 = Using SSL Keystore : {0}
+MNG-1007 = Open : User {0}
+MNG-1008 = Close
+
#VirtualHost
# 0 - name
@@ -240,15 +243,17 @@ CON-1002 = Close
#Channel
CHN-1001 = Create
-# : Prefetch Size {0,number} : Count {1,number}
# 0 - flow
CHN-1002 = Flow {0}
CHN-1003 = Close
+# 0 - bytes allowed in prefetch
+# 1 - number of messagse.
+CHN-1004 = Prefetch Size (bytes) {0,number} : Count {1,number}
#Queue
# 0 - owner
# 1 - priority
-QUE-1001 = Create : Owner: {0}[ AutoDelete][ Durable][ Transient][ Priority: {1,number,#}]
+QUE-1001 = Create :[ Owner: {0}][ AutoDelete][ Durable][ Transient][ Priority: {1,number,#}]
QUE-1002 = Deleted
#Exchange
@@ -264,3 +269,4 @@ BND-1002 = Deleted
#Subscription
SUB-1001 = Create[ : Durable][ : Arguments : {0}]
SUB-1002 = Close
+SUB-1003 = State : {0} \ No newline at end of file
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/QueueLogSubject.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/QueueLogSubject.java
index 89f31ef477..b132d9e93f 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/QueueLogSubject.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/subjects/QueueLogSubject.java
@@ -33,12 +33,12 @@ public class QueueLogSubject extends AbstractLogSubject
* 0 - Virtualhost name
* 1 - queue name
*/
- protected static String BINDING_FORMAT = "vh(/{0})/qu({1})";
+ public static String LOG_FORMAT = "vh(/{0})/qu({1})";
/** Create an QueueLogSubject that Logs in the following format. */
public QueueLogSubject(AMQQueue queue)
{
- setLogStringWithFormat(BINDING_FORMAT,
+ setLogStringWithFormat(LOG_FORMAT,
queue.getVirtualHost().getName(),
queue.getName());
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java
index c6e07f6f48..594ae24502 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/AMQManagedObject.java
@@ -20,6 +20,10 @@
*/
package org.apache.qpid.server.management;
+import org.apache.qpid.server.logging.actors.ManagementActor;
+import org.apache.qpid.server.logging.actors.CurrentActor;
+import org.apache.qpid.server.logging.LogActor;
+
import javax.management.ListenerNotFoundException;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
@@ -50,10 +54,15 @@ public abstract class AMQManagedObject extends DefaultManagedObject
protected MBeanInfo _mbeanInfo;
+ protected LogActor _logActor;
+
protected AMQManagedObject(Class<?> managementInterface, String typeName, int version)
throws NotCompliantMBeanException
{
super(managementInterface, typeName, version);
+ // CurrentActor will be defined as these objects are created during
+ // broker startup.
+ _logActor = new ManagementActor(CurrentActor.get().getRootMessageLogger());
buildMBeanInfo();
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java
index 38272db845..5ffcee77f2 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/JMXManagedObjectRegistry.java
@@ -34,9 +34,16 @@ import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
+import javax.management.NotificationListener;
+import javax.management.MalformedObjectNameException;
+import javax.management.NotificationFilter;
+import javax.management.NotificationFilterSupport;
+import javax.management.InstanceNotFoundException;
+import javax.management.relation.MBeanServerNotificationFilter;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.MBeanServerForwarder;
+import javax.management.remote.JMXConnectionNotification;
import javax.management.remote.rmi.RMIConnectorServer;
import javax.management.remote.rmi.RMIJRMPServerImpl;
import javax.management.remote.rmi.RMIServerImpl;
@@ -47,6 +54,7 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.management.ManagementFactory;
+import java.lang.reflect.Proxy;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
@@ -275,8 +283,18 @@ public class JMXManagedObjectRegistry implements ManagedObjectRegistry
//Add the custom invoker as an MBeanServerForwarder, and start the RMIConnectorServer.
MBeanServerForwarder mbsf = MBeanInvocationHandlerImpl.newProxyInstance();
cs.setMBeanServerForwarder(mbsf);
+
+ NotificationFilterSupport filter = new NotificationFilterSupport();
+ filter.enableType(JMXConnectionNotification.OPENED);
+ filter.enableType(JMXConnectionNotification.CLOSED);
+ filter.enableType(JMXConnectionNotification.FAILED);
+ // Get the handler that is used by the above MBInvocationHandler Proxy.
+ // which is the MBeanInvocationHandlerImpl and so also a NotificationListener
+ cs.addNotificationListener((NotificationListener) Proxy.getInvocationHandler(mbsf), filter, null);
+
cs.start();
+
CurrentActor.get().message(ManagementConsoleMessages.MNG_1004());
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java
index a68934d358..20410ba5ce 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/MBeanInvocationHandlerImpl.java
@@ -23,15 +23,23 @@ package org.apache.qpid.server.management;
import org.apache.qpid.management.common.mbeans.ConfigurationManagement;
import org.apache.qpid.management.common.mbeans.LoggingManagement;
import org.apache.qpid.management.common.mbeans.UserManagement;
+import org.apache.qpid.server.logging.actors.CurrentActor;
+import org.apache.qpid.server.logging.actors.ManagementActor;
+import org.apache.qpid.server.logging.messages.ManagementConsoleMessages;
+import org.apache.qpid.server.logging.messages.ConnectionMessages;
+import org.apache.qpid.server.logging.LogActor;
import org.apache.log4j.Logger;
import javax.management.remote.MBeanServerForwarder;
import javax.management.remote.JMXPrincipal;
+import javax.management.remote.JMXConnectionNotification;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.JMException;
+import javax.management.NotificationListener;
+import javax.management.Notification;
import javax.security.auth.Subject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
@@ -49,7 +57,7 @@ import java.util.Properties;
* the logic for allowing the users to invoke MBean operations and implements the restrictions for readOnly, readWrite
* and admin users.
*/
-public class MBeanInvocationHandlerImpl implements InvocationHandler
+public class MBeanInvocationHandlerImpl implements InvocationHandler, NotificationListener
{
private static final Logger _logger = Logger.getLogger(MBeanInvocationHandlerImpl.class);
@@ -59,6 +67,7 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler
private final static String DELEGATE = "JMImplementation:type=MBeanServerDelegate";
private MBeanServer _mbs;
private static Properties _userRoles = new Properties();
+ private static ManagementActor _logActor;
private static HashSet<String> _adminOnlyMethods = new HashSet<String>();
{
@@ -72,6 +81,9 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler
final InvocationHandler handler = new MBeanInvocationHandlerImpl();
final Class[] interfaces = new Class[]{MBeanServerForwarder.class};
+
+ _logActor = new ManagementActor(CurrentActor.get().getRootMessageLogger());
+
Object proxy = Proxy.newProxyInstance(MBeanServerForwarder.class.getClassLoader(), interfaces, handler);
return MBeanServerForwarder.class.cast(proxy);
}
@@ -254,4 +266,23 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler
return false;
}
+
+ public void handleNotification(Notification notification, Object handback)
+ {
+ // only RMI Connections are serviced here, Local API atta
+ // rmi://169.24.29.116 guest 3
+ String[] connectionData = ((JMXConnectionNotification) notification).getConnectionId().split(" ");
+ String user = connectionData[1];
+
+ if (notification.getType().equals(JMXConnectionNotification.OPENED))
+ {
+ _logActor.message(ManagementConsoleMessages.MNG_1007(user));
+ }
+ else if (notification.getType().equals(JMXConnectionNotification.CLOSED) ||
+ notification.getType().equals(JMXConnectionNotification.FAILED))
+ {
+ _logActor.message(ManagementConsoleMessages.MNG_1008());
+ }
+ }
}
+
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java
index 2a6cab6048..1235d5ffe5 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java
@@ -657,10 +657,6 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable
{
if (!_closed)
{
- _closed = true;
-
- _actor.message(ConnectionMessages.CON_1002());
-
if (_virtualHost != null)
{
_virtualHost.getConnectionRegistry().deregisterConnection(this);
@@ -676,6 +672,10 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable
{
task.doTask(this);
}
+
+ _closed = true;
+
+ CurrentActor.get().message(_logSubject, ConnectionMessages.CON_1002());
}
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java
index 0dbefd8798..16b85e67b3 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java
@@ -48,6 +48,7 @@ import org.apache.qpid.framing.ProtocolVersion;
import org.apache.qpid.server.configuration.ServerConfiguration;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.registry.IApplicationRegistry;
+import org.apache.qpid.server.logging.actors.CurrentActor;
import org.apache.qpid.ssl.SSLContextFactory;
/**
@@ -175,12 +176,17 @@ public class AMQPFastProtocolHandler extends IoHandlerAdapter
{
try
{
+ CurrentActor.set(amqProtocolSession.getLogActor());
amqProtocolSession.closeSession();
}
catch (AMQException e)
{
_logger.error("Caught AMQException whilst closingSession:" + e);
}
+ finally
+ {
+ CurrentActor.remove();
+ }
}
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java
index e86c85e0e4..5415f69dc1 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBean.java
@@ -66,7 +66,9 @@ import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription;
import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.server.AMQChannel;
import org.apache.qpid.server.logging.actors.CurrentActor;
+import org.apache.qpid.server.logging.actors.ManagementActor;
import org.apache.qpid.server.logging.LogActor;
+import org.apache.qpid.server.logging.RootMessageLogger;
import org.apache.qpid.server.management.AMQManagedObject;
import org.apache.qpid.server.management.ManagedObject;
@@ -187,7 +189,7 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed
*/
public void commitTransactions(int channelId) throws JMException
{
- CurrentActor.set(getLogActor());
+ CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger()));
try
{
AMQChannel channel = _session.getChannel(channelId);
@@ -216,7 +218,7 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed
*/
public void rollbackTransactions(int channelId) throws JMException
{
- CurrentActor.set(getLogActor());
+ CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger()));
try
{
AMQChannel channel = _session.getChannel(channelId);
@@ -281,7 +283,22 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed
0,
0);
- CurrentActor.set(getLogActor());
+ // This seems ugly but because we use closeConnection in both normal
+ // broker operation and as part of the management interface it cannot
+ // be avoided. The Current Actor will be null when this method is
+ // called via the Management interface. This is because we allow the
+ // Local API connection with JConsole. If we did not allow that option
+ // then the CurrentActor could be set in our JMX Proxy object.
+ // As it is we need to set the CurrentActor on all MBean methods
+ // Ideally we would not have a single method that can be called from
+ // two contexts.
+ boolean removeActor = false;
+ if (CurrentActor.get() == null)
+ {
+ removeActor = true;
+ CurrentActor.set(new ManagementActor(_logActor.getRootMessageLogger()));
+ }
+
try
{
_session.writeFrame(responseBody.generateFrame(0));
@@ -298,21 +315,13 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed
}
finally
{
- CurrentActor.remove();
+ if (removeActor)
+ {
+ CurrentActor.remove();
+ }
}
}
- /**
- * Return the LogActor for this MBean Session
- * //fixme currently simply returning the managed sessions LogActor, should
- * be the ManagementActor
- * @return
- */
- private LogActor getLogActor()
- {
- return _session.getLogActor();
- }
-
@Override
public MBeanNotificationInfo[] getNotificationInfo()
{
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java
index 164db6d679..b5f6711c93 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java
@@ -21,6 +21,7 @@
package org.apache.qpid.server.queue;
import org.apache.qpid.server.management.Managable;
+import org.apache.qpid.server.management.ManagedObject;
import org.apache.qpid.server.store.StoreContext;
import org.apache.qpid.server.configuration.QueueConfiguration;
import org.apache.qpid.server.exchange.Exchange;
@@ -242,4 +243,6 @@ public interface AMQQueue extends Managable, Comparable<AMQQueue>
}
void configure(QueueConfiguration config);
+
+ ManagedObject getManagedObject();
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java
index ac927f2821..d6309955a2 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java
@@ -312,13 +312,16 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que
}
/**
+ * Clears the queue of non-acquired messages
+ *
+ * @return the number of messages deleted
* @see AMQQueue#clearQueue
*/
- public void clearQueue() throws JMException
+ public Long clearQueue() throws JMException
{
try
{
- _queue.clearQueue(_storeContext);
+ return _queue.clearQueue(_storeContext);
}
catch (AMQException ex)
{
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBinding.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBinding.java
index 7584a3b7cc..6e87cfbb76 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBinding.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBinding.java
@@ -45,7 +45,7 @@ public class ExchangeBinding
_arguments = arguments == null ? EMPTY_ARGUMENTS : arguments;
_logSubject = new BindingLogSubject(routingKey,exchange,queue);
- CurrentActor.get().message(_logSubject, BindingMessages.BND_1001(_arguments.toString(), arguments != null));
+ CurrentActor.get().message(_logSubject, BindingMessages.BND_1001(String.valueOf(_arguments), arguments != null));
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java
index b0899b62d4..b040993d0b 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java
@@ -25,8 +25,10 @@ import org.apache.qpid.server.virtualhost.VirtualHost;
import org.apache.qpid.server.message.ServerMessage;
import org.apache.qpid.server.PrincipalHolder;
import org.apache.qpid.server.logging.actors.CurrentActor;
+import org.apache.qpid.server.logging.actors.QueueActor;
import org.apache.qpid.server.logging.subjects.QueueLogSubject;
import org.apache.qpid.server.logging.LogSubject;
+import org.apache.qpid.server.logging.LogActor;
import org.apache.qpid.server.logging.messages.QueueMessages;
/*
@@ -145,6 +147,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
private AtomicInteger _deliveredMessages = new AtomicInteger();
private AtomicBoolean _stopped = new AtomicBoolean(false);
private LogSubject _logSubject;
+ private LogActor _logActor;
protected SimpleAMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost)
throws AMQException
@@ -181,6 +184,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
_asyncDelivery = ReferenceCountingExecutorService.getInstance().acquireExecutorService();
_logSubject = new QueueLogSubject(this);
+ _logActor = new QueueActor(this, CurrentActor.get().getRootMessageLogger());
// Log the correct creation message
@@ -197,6 +201,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
CurrentActor.get().message(_logSubject,
QueueMessages.QUE_1001(String.valueOf(_owner),
priorities,
+ _owner != null,
autoDelete,
durable, !durable,
priorities > 0));
@@ -910,30 +915,23 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
*/
public List<QueueEntry> getMessagesRangeOnTheQueue(final long fromPosition, final long toPosition)
{
- List<QueueEntry> queueEntries = new ArrayList<QueueEntry>();
-
- QueueEntryIterator it = _entries.iterator();
-
- long index = 1;
- for ( ; index < fromPosition && !it.atTail(); index++)
- {
- it.advance();
- }
-
- if(index < fromPosition)
- {
- //The queue does not contain enough entries to reach our range.
- //return the empty list.
- return queueEntries;
- }
-
- for ( ; index <= toPosition && !it.atTail(); index++)
+ List<QueueEntry> entries = getMessagesOnTheQueue(new QueueEntryFilter()
{
- it.advance();
- queueEntries.add(it.getNode());
- }
+ private long position = 0;
+
+ public boolean accept(QueueEntry entry)
+ {
+ position++;
+ return (position >= fromPosition) && (position <= toPosition);
+ }
- return queueEntries;
+ public boolean filterComplete()
+ {
+ return position >= toPosition;
+ }
+ });
+
+ return entries;
}
public void moveMessagesToAnotherQueue(final long fromMessageId,
@@ -1258,12 +1256,18 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
{
try
{
+ CurrentActor.set(_logActor);
processQueue(this);
}
catch (AMQException e)
{
_logger.error(e);
}
+ finally
+ {
+ CurrentActor.remove();
+ }
+
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java
index f630d04c95..1b07cbdf65 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java
@@ -615,6 +615,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage
if(_state.compareAndSet(State.SUSPENDED, State.ACTIVE))
{
_stateListener.stateChange(this, State.SUSPENDED, State.ACTIVE);
+ CurrentActor.get().message(_logSubject,SubscriptionMessages.SUB_1003(_state.get().toString()));
}
else
{
@@ -627,6 +628,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage
if(_state.compareAndSet(State.ACTIVE, State.SUSPENDED))
{
_stateListener.stateChange(this, State.ACTIVE, State.SUSPENDED);
+ CurrentActor.get().message(_logSubject,SubscriptionMessages.SUB_1003(_state.get().toString()));
}
}
}
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java
index ad1e8a580e..fa6b2285eb 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java
@@ -29,6 +29,9 @@ import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.framing.abstraction.ContentChunk;
import org.apache.qpid.server.AMQBrokerManagerMBean;
+import org.apache.qpid.server.logging.actors.CurrentActor;
+import org.apache.qpid.server.logging.LogSubject;
+import org.apache.qpid.server.logging.messages.VirtualHostMessages;
import org.apache.qpid.server.configuration.ExchangeConfiguration;
import org.apache.qpid.server.configuration.QueueConfiguration;
import org.apache.qpid.server.configuration.VirtualHostConfiguration;
@@ -155,6 +158,8 @@ public class VirtualHost implements Accessable
_configuration = hostConfig;
_name = hostConfig.getName();
+ CurrentActor.get().message(VirtualHostMessages.VHT_1001(_name));
+
if (_name == null || _name.length() == 0)
{
throw new IllegalArgumentException("Illegal name (" + _name + ") for virtualhost.");
@@ -427,6 +432,8 @@ public class VirtualHost implements Accessable
{
_messageStore.close();
}
+
+ CurrentActor.get().message(VirtualHostMessages.VHT_1002());
}
public ManagedObject getBrokerMBean()
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java
index 4e20f537f1..8cb0837b39 100644
--- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/ServerConfigurationTest.java
@@ -263,9 +263,8 @@ public class ServerConfigurationTest extends TestCase
// Check default
ServerConfiguration serverConfig = new ServerConfiguration(_config);
- String defaultParts[] = ServerConfiguration.DEFAULT_ADVANCED_LOCALE.split("_");
- // The Default is en_US so will split well
- Locale defaultLocale = new Locale(defaultParts[0],defaultParts[1]);
+ // The Default is what ever the VMs default is
+ Locale defaultLocale = Locale.getDefault();
assertEquals(defaultLocale, serverConfig.getLocale());
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/LogMessageTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/LogMessageTest.java
new file mode 100644
index 0000000000..0f3f7bd2b5
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/LogMessageTest.java
@@ -0,0 +1,71 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.logging;
+
+import junit.framework.TestCase;
+
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+public class LogMessageTest extends TestCase
+{
+
+ /**
+ * Test that the US local is loadable.
+ */
+ public void testUSLocale()
+ {
+ Locale usLocal = Locale.US;
+ Locale.setDefault(usLocal);
+ ResourceBundle _messages = ResourceBundle.getBundle("org.apache.qpid.server.logging.messages.LogMessages",
+ usLocal);
+
+ assertNotNull("Unable to load ResourceBundle", _messages);
+
+ assertEquals("Loaded bundle has incorrect locale.", usLocal, _messages.getLocale());
+ }
+
+ /**
+ * Test that loading an undefined locale will result in loadig of the
+ * default US locale.
+ */
+ public void testUndefinedLocale()
+ {
+ Locale japanese = Locale.JAPANESE;
+
+ Locale.setDefault(japanese);
+ try
+ {
+ ResourceBundle _messages = ResourceBundle.getBundle("org.apache.qpid.server.logging.messages.LogMessages",
+ japanese);
+
+ assertNotNull("Unable to load ResourceBundle", _messages);
+
+ // If we attempt to load an undefined locale it should default to the Root locale.
+ assertEquals("Loaded bundle has incorrect locale.", Locale.ROOT, _messages.getLocale());
+ }
+ catch (Throwable t)
+ {
+ fail(t.getMessage());
+ }
+ }
+
+}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/ManagementActorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/ManagementActorTest.java
index fa0bb6529e..340c0ae837 100644
--- a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/ManagementActorTest.java
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/ManagementActorTest.java
@@ -32,7 +32,6 @@ import org.apache.qpid.server.logging.RootMessageLogger;
import org.apache.qpid.server.logging.RootMessageLoggerImpl;
import org.apache.qpid.server.logging.rawloggers.UnitTestMessageLogger;
-import java.security.Principal;
import java.util.List;
/**
@@ -49,6 +48,9 @@ public class ManagementActorTest extends TestCase
LogActor _amqpActor;
UnitTestMessageLogger _rawLogger;
+ private static final String IP = "127.0.0.1";
+ private static final String CONNECTION_ID = "1";
+ private String _threadName;
public void setUp() throws ConfigurationException
{
@@ -59,17 +61,16 @@ public class ManagementActorTest extends TestCase
RootMessageLogger rootLogger =
new RootMessageLoggerImpl(serverConfig, _rawLogger);
- _amqpActor = new ManagementActor(new Principal()
- {
- public String getName()
- {
- return "ManagementActorTest";
- }
- }, rootLogger);
+ _amqpActor = new ManagementActor(rootLogger);
+
+ // Set the thread name to be the same as a RMI JMX Connection would use
+ _threadName = Thread.currentThread().getName();
+ Thread.currentThread().setName("RMI TCP Connection(" + CONNECTION_ID + ")-" + IP);
}
public void tearDown()
{
+ Thread.currentThread().setName(_threadName);
_rawLogger.clearLogMessages();
}
@@ -120,6 +121,11 @@ public class ManagementActorTest extends TestCase
// Verify that the logged message does not contains the 'ch:' marker
assertFalse("Message was logged with a channel identifier." + logs.get(0),
logs.get(0).toString().contains("/ch:"));
+
+ // Verify that the message has the right values
+ assertTrue("Message contains the [mng: prefix",
+ logs.get(0).toString().contains("[mng:" + CONNECTION_ID + "(" + IP + ")"));
+
}
}
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/QueueActorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/QueueActorTest.java
new file mode 100644
index 0000000000..5d2fe26707
--- /dev/null
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/actors/QueueActorTest.java
@@ -0,0 +1,119 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.server.logging.actors;
+
+import junit.framework.TestCase;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.apache.qpid.server.configuration.ServerConfiguration;
+import org.apache.qpid.server.logging.LogActor;
+import org.apache.qpid.server.logging.LogMessage;
+import org.apache.qpid.server.logging.LogSubject;
+import org.apache.qpid.server.logging.RootMessageLogger;
+import org.apache.qpid.server.logging.RootMessageLoggerImpl;
+import org.apache.qpid.server.logging.rawloggers.UnitTestMessageLogger;
+import org.apache.qpid.server.queue.MockAMQQueue;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+
+import java.util.List;
+
+public class QueueActorTest extends TestCase
+{
+ LogActor _amqpActor;
+ UnitTestMessageLogger _rawLogger;
+
+ public void setUp() throws ConfigurationException
+ {
+ Configuration config = new PropertiesConfiguration();
+ ServerConfiguration serverConfig = new ServerConfiguration(config);
+
+ _rawLogger = new UnitTestMessageLogger();
+ RootMessageLogger rootLogger =
+ new RootMessageLoggerImpl(serverConfig, _rawLogger);
+
+ MockAMQQueue queue = new MockAMQQueue(getName());
+
+ queue.setVirtualHost(ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHosts().iterator().next());
+
+ _amqpActor = new QueueActor(queue, rootLogger);
+ }
+
+ public void tearDown()
+ {
+ _rawLogger.clearLogMessages();
+ ApplicationRegistry.remove();
+ }
+
+ /**
+ * Test the QueueActor as a logger.
+ *
+ * The test logs a message then verifies that it entered the logs correctly
+ *
+ * The log message should be fully repalaced (no '{n}' values) and should
+ * contain the correct queue identification.
+ */
+ public void testQueueActor()
+ {
+ final String message = "test logging";
+
+ _amqpActor.message(new LogSubject()
+ {
+ public String toString()
+ {
+ return "[AMQPActorTest]";
+ }
+
+ }, new LogMessage()
+ {
+ public String toString()
+ {
+ return message;
+ }
+ });
+
+ List<Object> logs = _rawLogger.getLogMessages();
+
+ assertEquals("Message log size not as expected.", 1, logs.size());
+
+ String log = logs.get(0).toString();
+
+ // Verify that the logged message is present in the output
+ assertTrue("Message was not found in log message",
+ log.contains(message));
+
+ // Verify that all the values were presented to the MessageFormatter
+ // so we will not end up with '{n}' entries in the log.
+ assertFalse("Verify that the string does not contain any '{':" + log,
+ log.contains("{"));
+
+ // Verify that the message has the correct type
+ assertTrue("Message contains the [vh: prefix:" + log,
+ log.contains("[vh("));
+
+ // Verify that the logged message contains the 'qu(' marker
+ String expected = "qu(" + getName() + ")";
+ assertTrue("Message was not logged with a queue identifer '"+expected+"' actual:" + log,
+ log.contains(expected));
+ }
+
+}
+
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/QueueMessagesTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/QueueMessagesTest.java
index 2f53d0aff5..c5d544ba84 100644
--- a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/QueueMessagesTest.java
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/QueueMessagesTest.java
@@ -29,7 +29,7 @@ public class QueueMessagesTest extends AbstractTestMessages
String owner = "guest";
Integer priority = 3;
- _logMessage = QueueMessages.QUE_1001(owner, priority, true, true, true, true);
+ _logMessage = QueueMessages.QUE_1001(owner, priority, true, true, true, true, true);
List<Object> log = performLog();
String[] expected = {"Create :", "Owner:", owner, "AutoDelete",
@@ -39,11 +39,11 @@ public class QueueMessagesTest extends AbstractTestMessages
validateLogMessage(log, "QUE-1001", expected);
}
- public void testMessage1001AutoDelete()
+ public void testMessage1001OwnerAutoDelete()
{
String owner = "guest";
- _logMessage = QueueMessages.QUE_1001(owner, null, true, false, false, false);
+ _logMessage = QueueMessages.QUE_1001(owner, null, true, true, false, false, false);
List<Object> log = performLog();
String[] expected = {"Create :", "Owner:", owner, "AutoDelete"};
@@ -51,12 +51,12 @@ public class QueueMessagesTest extends AbstractTestMessages
validateLogMessage(log, "QUE-1001", expected);
}
- public void testMessage1001Priority()
+ public void testMessage1001OwnerPriority()
{
String owner = "guest";
Integer priority = 3;
- _logMessage = QueueMessages.QUE_1001(owner, priority, false, false, false, true);
+ _logMessage = QueueMessages.QUE_1001(owner, priority, true, false, false, false, true);
List<Object> log = performLog();
String[] expected = {"Create :", "Owner:", owner, "Priority:",
@@ -65,12 +65,12 @@ public class QueueMessagesTest extends AbstractTestMessages
validateLogMessage(log, "QUE-1001", expected);
}
- public void testMessage1001AutoDeletePriority()
+ public void testMessage1001OwnerAutoDeletePriority()
{
String owner = "guest";
Integer priority = 3;
- _logMessage = QueueMessages.QUE_1001(owner, priority, true, false, false, true);
+ _logMessage = QueueMessages.QUE_1001(owner, priority, true, true, false, false, true);
List<Object> log = performLog();
String[] expected = {"Create :", "Owner:", owner, "AutoDelete",
@@ -80,25 +80,25 @@ public class QueueMessagesTest extends AbstractTestMessages
validateLogMessage(log, "QUE-1001", expected);
}
- public void testMessage1001AutoDeleteTransient()
+ public void testMessage1001OwnerAutoDeleteTransient()
{
String owner = "guest";
- _logMessage = QueueMessages.QUE_1001(owner, null, true, false, true, false);
+ _logMessage = QueueMessages.QUE_1001(owner, null, true, true, false, true, false);
List<Object> log = performLog();
String[] expected = {"Create :", "Owner:", owner, "AutoDelete",
- "Transient"};
+ "Transient"};
validateLogMessage(log, "QUE-1001", expected);
}
- public void testMessage1001AutoDeleteTransientPriority()
+ public void testMessage1001OwnerAutoDeleteTransientPriority()
{
String owner = "guest";
Integer priority = 3;
- _logMessage = QueueMessages.QUE_1001(owner, priority, true, false, true, true);
+ _logMessage = QueueMessages.QUE_1001(owner, priority, true, true, false, true, true);
List<Object> log = performLog();
String[] expected = {"Create :", "Owner:", owner, "AutoDelete",
@@ -108,11 +108,11 @@ public class QueueMessagesTest extends AbstractTestMessages
validateLogMessage(log, "QUE-1001", expected);
}
- public void testMessage1001AutoDeleteDurable()
+ public void testMessage1001OwnerAutoDeleteDurable()
{
String owner = "guest";
- _logMessage = QueueMessages.QUE_1001(owner, null, true, true, false, false);
+ _logMessage = QueueMessages.QUE_1001(owner, null, true, true, true, false, false);
List<Object> log = performLog();
String[] expected = {"Create :", "Owner:", owner, "AutoDelete",
@@ -121,12 +121,12 @@ public class QueueMessagesTest extends AbstractTestMessages
validateLogMessage(log, "QUE-1001", expected);
}
- public void testMessage1001AutoDeleteDurablePriority()
+ public void testMessage1001OwnerAutoDeleteDurablePriority()
{
String owner = "guest";
Integer priority = 3;
- _logMessage = QueueMessages.QUE_1001(owner, priority, true, true, false, true);
+ _logMessage = QueueMessages.QUE_1001(owner, priority, true, true, true, false, true);
List<Object> log = performLog();
String[] expected = {"Create :", "Owner:", owner, "AutoDelete",
@@ -136,6 +136,93 @@ public class QueueMessagesTest extends AbstractTestMessages
validateLogMessage(log, "QUE-1001", expected);
}
+ public void testMessage1001AutoDelete()
+ {
+ _logMessage = QueueMessages.QUE_1001(null, null, false, true, false, false, false);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "AutoDelete"};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testMessage1001Priority()
+ {
+ Integer priority = 3;
+
+ _logMessage = QueueMessages.QUE_1001(null, priority, false, false, false, false, true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "Priority:",
+ String.valueOf(priority)};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testMessage1001AutoDeletePriority()
+ {
+ Integer priority = 3;
+
+ _logMessage = QueueMessages.QUE_1001(null, priority, false, true, false, false, true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "AutoDelete",
+ "Priority:",
+ String.valueOf(priority)};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testMessage1001AutoDeleteTransient()
+ {
+ _logMessage = QueueMessages.QUE_1001(null, null, false, true, false, true, false);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "AutoDelete",
+ "Transient"};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testMessage1001AutoDeleteTransientPriority()
+ {
+ Integer priority = 3;
+
+ _logMessage = QueueMessages.QUE_1001(null, priority, false, true, false, true, true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "AutoDelete",
+ "Transient", "Priority:",
+ String.valueOf(priority)};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testMessage1001AutoDeleteDurable()
+ {
+ _logMessage = QueueMessages.QUE_1001(null, null, false, true, true, false, false);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "AutoDelete",
+ "Durable"};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
+ public void testMessage1001AutoDeleteDurablePriority()
+ {
+ Integer priority = 3;
+
+ _logMessage = QueueMessages.QUE_1001(null, priority, false, true, true, false, true);
+ List<Object> log = performLog();
+
+ String[] expected = {"Create :", "AutoDelete",
+ "Durable", "Priority:",
+ String.valueOf(priority)};
+
+ validateLogMessage(log, "QUE-1001", expected);
+ }
+
public void testMessage1002()
{
_logMessage = QueueMessages.QUE_1002();
diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/SubscriptionMessagesTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/SubscriptionMessagesTest.java
index 80ebcc79cd..7752b873b6 100644
--- a/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/SubscriptionMessagesTest.java
+++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/logging/messages/SubscriptionMessagesTest.java
@@ -68,4 +68,16 @@ public class SubscriptionMessagesTest extends AbstractTestMessages
validateLogMessage(log, "SUB-1002", expected);
}
+
+ public void testMessage1003()
+ {
+ String state = "ACTIVE";
+
+ _logMessage = SubscriptionMessages.SUB_1003(state);
+ List<Object> log = performLog();
+
+ String[] expected = {"State :", state};
+
+ validateLogMessage(log, "SUB-1003", expected);
+ }
}
diff --git a/qpid/java/build.xml b/qpid/java/build.xml
index ccca115c9d..f40da460b0 100644
--- a/qpid/java/build.xml
+++ b/qpid/java/build.xml
@@ -29,8 +29,8 @@
<property name="modules.plugin" value="broker-plugins"/>
<property name="modules.management.tools" value="management/tools/qpid-cli"/>
<property name="modules" value="${modules.core}
- ${modules.plugin} ${modules.examples} ${modules.tests}
- ${modules.management} ${modules.management.tools}"/>
+ ${modules.plugin} ${modules.examples}
+ ${modules.management} ${modules.management.tools} ${modules.tests}"/>
<property name="qpid.jar" location="${build.lib}/qpid-all.jar"/>
<basename property="qpid.jar.name" file="${qpid.jar}"/>
diff --git a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedBroker.java b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedBroker.java
index e376033bad..dcf77ca2ed 100644
--- a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedBroker.java
+++ b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedBroker.java
@@ -22,12 +22,10 @@
package org.apache.qpid.management.common.mbeans;
import java.io.IOException;
-import java.util.Map;
+import java.util.List;
import javax.management.JMException;
import javax.management.MBeanOperationInfo;
-import javax.management.openmbean.OpenDataException;
-import javax.management.openmbean.TabularData;
import org.apache.qpid.management.common.mbeans.annotations.MBeanAttribute;
import org.apache.qpid.management.common.mbeans.annotations.MBeanOperation;
@@ -55,13 +53,23 @@ public interface ManagedBroker
String[] getExchangeTypes() throws IOException;
/**
- * Returns a Map keyed by QueueName, detailing its associated QueueDepth in bytes.
+ * Returns a list containing the names of the attributes available for the Queue mbeans.
* @since Qpid JMX API 1.3
* @throws IOException
*/
- @MBeanOperation(name = "viewQueueNamesDepths", description = "View the queue names and depths in this virtualhost",
- impact = MBeanOperationInfo.INFO)
- Map<String,Long> viewQueueNamesDepths() throws IOException;
+ @MBeanOperation(name = "retrieveQueueAttributeNames", description = "Retrieve the attribute names for queues in this virtualhost",
+ impact = MBeanOperationInfo.INFO)
+ List<String> retrieveQueueAttributeNames() throws IOException;
+
+ /**
+ * Returns a List of Object Lists containing the requested attribute values (in the same sequence requested) for each queue in the virtualhost.
+ * If a particular attribute cant be found or raises an mbean/reflection exception whilst being gathered its value is substituted with the String "-".
+ * @since Qpid JMX API 1.3
+ * @throws IOException
+ */
+ @MBeanOperation(name = "retrieveQueueAttributeValues", description = "Retrieve the indicated attributes for queues in this virtualhost",
+ impact = MBeanOperationInfo.INFO)
+ List<List<Object>> retrieveQueueAttributeValues(@MBeanOperationParameter(name="attributes", description="Attributes to retrieve") String[] attributes) throws IOException;
/**
* Creates a new Exchange.
diff --git a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedQueue.java b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedQueue.java
index 9046d7fcb7..ff4edb4ddd 100644
--- a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedQueue.java
+++ b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedQueue.java
@@ -54,6 +54,37 @@ public interface ManagedQueue
String[] VIEW_MSG_CONTENT_COMPOSITE_ITEM_NAMES = { "AMQ MessageId", "MimeType", "Encoding", "Content" };
String[] VIEW_MSG_CONTENT_COMPOSITE_ITEM_DESCRIPTIONS = { "AMQ MessageId", "MimeType", "Encoding", "Content" };
+ //Individual attribute name constants
+ String ATTR_NAME = "Name";
+ String ATTR_OWNER = "Owner";
+ String ATTR_MAX_MSG_AGE = "MaximumMessageAge";
+ String ATTR_MAX_MSG_COUNT = "MaximumMessageCount";
+ String ATTR_MAX_QUEUE_DEPTH = "MaximumQueueDepth";
+ String ATTR_MAX_MSG_SIZE = "MaximumMessageSize";
+ String ATTR_DURABLE = "Durable";
+ String ATTR_AUTODELETE = "AutoDelete";
+ String ATTR_CONSUMER_COUNT = "ConsumerCount";
+ String ATTR_ACTIVE_CONSUMER_COUNT = "ActiveConsumerCount";
+ String ATTR_MSG_COUNT = "MessageCount";
+ String ATTR_QUEUE_DEPTH = "QueueDepth";
+ String ATTR_RCVD_MSG_COUNT = "ReceivedMessageCount";
+
+ //All attribute names constant
+ String[] QUEUE_ATTRIBUTES = new String[]{
+ ATTR_NAME,
+ ATTR_OWNER,
+ ATTR_MAX_MSG_AGE,
+ ATTR_MAX_MSG_COUNT,
+ ATTR_MAX_QUEUE_DEPTH,
+ ATTR_MAX_MSG_SIZE,
+ ATTR_DURABLE,
+ ATTR_AUTODELETE,
+ ATTR_CONSUMER_COUNT,
+ ATTR_ACTIVE_CONSUMER_COUNT,
+ ATTR_MSG_COUNT,
+ ATTR_QUEUE_DEPTH,
+ ATTR_RCVD_MSG_COUNT
+ };
/**
* Returns the Name of the ManagedQueue.
@@ -260,14 +291,18 @@ public interface ManagedQueue
void deleteMessageFromTop() throws IOException, JMException;
/**
- * Clears the queue by deleting all the undelivered messages from the queue.
+ * Clears the queue by deleting all the messages from the queue that have not been acquired by consumers"
+ *
+ * Since Qpid JMX API 1.3 this returns the number of messages deleted. Prior to this, the return type was void.
+ * @return the number of messages deleted
* @throws IOException
* @throws JMException
*/
@MBeanOperation(name="clearQueue",
- description="Clears the queue by deleting all the undelivered messages from the queue",
+ description="Clears the queue by deleting all the messages from the queue " +
+ "that have not been acquired by consumers",
impact= MBeanOperationInfo.ACTION)
- void clearQueue() throws IOException, JMException;
+ Long clearQueue() throws IOException, JMException;
/**
* Moves the messages in given range of message Ids to given Queue. QPID-170
diff --git a/qpid/java/management/common/src/test/java/org/apache/qpid/management/common/mbeans/ManagedQueueTest.java b/qpid/java/management/common/src/test/java/org/apache/qpid/management/common/mbeans/ManagedQueueTest.java
new file mode 100644
index 0000000000..f449ecb7e5
--- /dev/null
+++ b/qpid/java/management/common/src/test/java/org/apache/qpid/management/common/mbeans/ManagedQueueTest.java
@@ -0,0 +1,84 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.common.mbeans;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.management.MBeanAttributeInfo;
+import javax.management.NotCompliantMBeanException;
+import javax.management.StandardMBean;
+
+import junit.framework.TestCase;
+
+public class ManagedQueueTest extends TestCase
+{
+ public void testAttributesContants()
+ {
+ //Construct a test MBeanInfo that matches what we would get from a real
+ //MBean using the ManagedQueue management interface. Use this to test
+ //that all attributes have a listing in the attribute array constant.
+
+ StubInvocationHandler stubIH = new StubInvocationHandler();
+ Class<ManagedQueue> mq = ManagedQueue.class;
+
+ ManagedQueue impl = mq.cast(Proxy.newProxyInstance(mq.getClassLoader(), new Class<?>[] {mq}, stubIH));
+ try
+ {
+ StandardMBean mbean = new StandardMBean(impl, ManagedQueue.class);
+
+ List<String> attributeList = new ArrayList<String>();
+ for(String attr : ManagedQueue.QUEUE_ATTRIBUTES)
+ {
+ attributeList.add(attr);
+ }
+
+ //retrieve the attributes from the constructed MBeanInfo
+ MBeanAttributeInfo[] attributes = mbean.getMBeanInfo().getAttributes();
+
+ for(MBeanAttributeInfo info : attributes)
+ {
+ if(!attributeList.contains(info.getName()))
+ {
+ fail(mq.getSimpleName() + " attributes constant array does not include the attribute: " + info.getName());
+ }
+ }
+ }
+ catch (NotCompliantMBeanException e)
+ {
+ fail("Unable to create the test proxy mbean to generate the MBeanInfo");
+ }
+
+ }
+
+ private static class StubInvocationHandler implements InvocationHandler
+ {
+ //invocation handler used to present a stub implementation when generating the StandardMBean
+ public Object invoke(Object proxy, Method method, Object[] args)
+ {
+ return null;
+ }
+ }
+
+}
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java
index cb758efa43..cd6f7ff808 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/ApplicationRegistry.java
@@ -20,6 +20,8 @@
*/
package org.apache.qpid.management.ui;
+import java.io.File;
+import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -48,6 +50,8 @@ public abstract class ApplicationRegistry
public static final int SUPPORTED_QPID_JMX_API_MAJOR_VERSION = 1;
public static final int SUPPORTED_QPID_JMX_API_MINOR_VERSION = 3;
+ public static final String DATA_DIR = System.getProperty("user.home") + File.separator + ".qpidmc";
+
static
{
imageRegistry.put(Constants.SUCCESS_IMAGE,
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/MBeanUtility.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/MBeanUtility.java
index bbb6686346..69a466c943 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/MBeanUtility.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/MBeanUtility.java
@@ -23,6 +23,7 @@ package org.apache.qpid.management.ui.jmx;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
@@ -276,6 +277,72 @@ public class MBeanUtility
return value;
}
+
+ /**
+ * Returns a List of Object arrays containing the requested attribute values (in the same sequence requested) for each queue in the virtualhost.
+ * If a particular attribute cant be found or raises an mbean/reflection exception whilst being gathered its value is substituted with the String "-".
+ */
+ public static List<List<Object>> getQueueAttributes(List<ManagedBean> mbeans, String[] attributes)
+ {
+ List<List<Object>> results = new ArrayList<List<Object>>();
+
+ MBeanServerConnection mbsc = null;
+ if(mbeans.isEmpty())
+ {
+ return results;
+ }
+ else
+ {
+ ManagedBean mbean = mbeans.get(0);
+ JMXServerRegistry serverRegistry = (JMXServerRegistry)ApplicationRegistry.getServerRegistry(mbean);
+ mbsc = serverRegistry.getServerConnection();
+ }
+
+ if(mbsc == null)
+ {
+ return results;
+ }
+
+ for(ManagedBean mbean : mbeans)
+ {
+ HashMap<String,Object> tempResults = new HashMap<String,Object>();
+
+ ObjectName objName = ((JMXManagedObject)mbean).getObjectName();
+ try
+ {
+ AttributeList list = mbsc.getAttributes(objName, attributes);
+
+ for (Attribute attr : list.toArray(new Attribute[0]))
+ {
+ tempResults.put(attr.getName(), attr.getValue());
+ }
+
+ List<Object> attributeValues = new ArrayList<Object>(attributes.length);
+
+ for(int i = 0; i <attributes.length; i++)
+ {
+ if(tempResults.containsKey(attributes[i]))
+ {
+ attributeValues.add(tempResults.get(attributes[i]));
+ }
+ else
+ {
+ attributeValues.add(new String("-"));
+ }
+ }
+
+ results.add(attributeValues);
+ }
+ catch (Exception ignore)
+ {
+ continue;
+ }
+ }
+
+ return results;
+ }
+
+
/**
* Retrieves the attribute values from MBeanSever and stores in the server registry.
* @param mbean
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java
index 6563498d41..0d290ab1c4 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/NavigationView.java
@@ -21,6 +21,7 @@
package org.apache.qpid.management.ui.views;
import static org.apache.qpid.management.ui.Constants.*;
+import static org.apache.qpid.management.ui.ApplicationRegistry.DATA_DIR;
import java.io.File;
import java.io.IOException;
@@ -82,8 +83,7 @@ import org.eclipse.ui.part.ViewPart;
public class NavigationView extends ViewPart
{
public static final String ID = "org.apache.qpid.management.ui.navigationView";
- public static final String APP_DIR = System.getProperty("user.home") + File.separator + ".qpidmc";
- public static final String INI_FILENAME = APP_DIR + File.separator + "qpidmc_navigation.ini";
+ public static final String INI_FILENAME = DATA_DIR + File.separator + "qpidmc_navigation.ini";
private static final String INI_SERVERS = "Servers";
private static final String INI_QUEUES = QUEUE + "s";
@@ -322,6 +322,8 @@ public class NavigationView extends ViewPart
// Add the Queue/Exchanges/Connections from config file into the navigation tree
addConfiguredItems(managedServer);
+ _treeViewer.refresh();
+
expandInitialMBeanView(serverNode);
//(re)select the server node now that it is connected to force a selectionEvent
@@ -338,12 +340,12 @@ public class NavigationView extends ViewPart
*/
private void createConfigFile()
{
- File dir = new File(APP_DIR);
+ File dir = new File(DATA_DIR);
if (!dir.exists())
{
if(!dir.mkdir())
{
- System.out.println("Could not create application data directory " + APP_DIR);
+ System.out.println("Could not create application data directory " + DATA_DIR);
System.exit(1);
}
}
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/queue/QueueOperationsTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/queue/QueueOperationsTabControl.java
index c838a3e414..c7c7a1791a 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/queue/QueueOperationsTabControl.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/queue/QueueOperationsTabControl.java
@@ -457,8 +457,21 @@ public class QueueOperationsTabControl extends TabControl
{
try
{
- _qmb.clearQueue();
- ViewUtility.operationResultFeedback(null, "Queue cleared", null);
+ if(_ApiVersion.greaterThanOrEqualTo(1, 3))
+ {
+ //Qpid JMX API 1.3+, returns the number of messages deleted
+ Long numDeleted = _qmb.clearQueue();
+ String message = "Queue cleared of " + numDeleted +
+ " non-acquired message" + (numDeleted == 1 ? "": "s");
+
+ ViewUtility.operationResultFeedback(null, message, null);
+ }
+ else
+ {
+ //Qpid JMX API 1.2 or below, void return
+ _qmb.clearQueue();
+ ViewUtility.operationResultFeedback(null, "Queue cleared of non-acquired messages", null);
+ }
}
catch (Exception e2)
{
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/MBeanTypeTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/MBeanTypeTabControl.java
index 94b69e1231..37af1f8f31 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/MBeanTypeTabControl.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/MBeanTypeTabControl.java
@@ -65,17 +65,18 @@ import org.eclipse.ui.forms.widgets.FormToolkit;
public abstract class MBeanTypeTabControl extends TabControl
{
- private FormToolkit _toolkit;
- private Form _form;
+ protected FormToolkit _toolkit;
+ protected Form _form;
protected Table _table = null;
protected TableViewer _tableViewer = null;
protected List<ManagedBean> _mbeans = null;
- private String _type;
+ protected String _type;
protected ApiVersion _ApiVersion;
protected JMXManagedObject _vhostMbean;
protected String _virtualHost;
protected JMXServerRegistry _serverRegistry;
+ protected Composite _tableComposite;
public MBeanTypeTabControl(TabFolder tabFolder, ManagedServer server, String virtualHost, String type)
{
@@ -88,8 +89,14 @@ public abstract class MBeanTypeTabControl extends TabControl
_toolkit = new FormToolkit(_tabFolder.getDisplay());
_form = _toolkit.createForm(_tabFolder);
_form.getBody().setLayout(new GridLayout());
+ init();
createWidgets();
}
+
+ protected void init()
+ {
+
+ }
/**
* @see TabControl#getControl()
@@ -131,9 +138,9 @@ public abstract class MBeanTypeTabControl extends TabControl
protected abstract List<ManagedBean> getMbeans();
- protected void createTable(Composite tableComposite)
+ protected void createTable()
{
- _table = new Table (tableComposite, SWT.MULTI | SWT.SCROLL_LINE | SWT.BORDER | SWT.FULL_SELECTION);
+ _table = new Table (_tableComposite, SWT.MULTI | SWT.SCROLL_LINE | SWT.BORDER | SWT.FULL_SELECTION);
_table.setLinesVisible (true);
_table.setHeaderVisible (true);
GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
@@ -222,12 +229,12 @@ public abstract class MBeanTypeTabControl extends TabControl
}
});
- Composite tableComposite = _toolkit.createComposite(mainComposite);
+ _tableComposite = _toolkit.createComposite(mainComposite);
gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
- tableComposite.setLayoutData(gridData);
- tableComposite.setLayout(new GridLayout(1,false));
+ _tableComposite.setLayoutData(gridData);
+ _tableComposite.setLayout(new GridLayout(1,false));
- createTable(tableComposite);
+ createTable();
favouritesButton.setEnabled(false);
openButton.setEnabled(false);
@@ -270,6 +277,13 @@ public abstract class MBeanTypeTabControl extends TabControl
public void mouseDown(MouseEvent e){}
public void mouseUp(MouseEvent e){}
});
+
+ createLowerAreaButton(mainComposite);
+ }
+
+ protected void createLowerAreaButton(Composite parent)
+ {
+
}
/**
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/QueueTypeTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/QueueTypeTabControl.java
index 8f80426af8..3e1f198606 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/QueueTypeTabControl.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/type/QueueTypeTabControl.java
@@ -20,27 +20,44 @@
*/
package org.apache.qpid.management.ui.views.type;
+import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Semaphore;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
+import static org.apache.qpid.management.ui.ApplicationRegistry.DATA_DIR;
import static org.apache.qpid.management.ui.Constants.QUEUE;
-import static org.apache.qpid.management.ui.Constants.ATTRIBUTE_QUEUE_DEPTH;
+import static org.apache.qpid.management.common.mbeans.ManagedQueue.ATTR_NAME;
+import static org.apache.qpid.management.common.mbeans.ManagedQueue.ATTR_ACTIVE_CONSUMER_COUNT;
+import static org.apache.qpid.management.common.mbeans.ManagedQueue.ATTR_AUTODELETE;
+import static org.apache.qpid.management.common.mbeans.ManagedQueue.ATTR_CONSUMER_COUNT;
+import static org.apache.qpid.management.common.mbeans.ManagedQueue.ATTR_DURABLE;
+import static org.apache.qpid.management.common.mbeans.ManagedQueue.ATTR_MAX_MSG_AGE;
+import static org.apache.qpid.management.common.mbeans.ManagedQueue.ATTR_MAX_MSG_COUNT;
+import static org.apache.qpid.management.common.mbeans.ManagedQueue.ATTR_MAX_MSG_SIZE;
+import static org.apache.qpid.management.common.mbeans.ManagedQueue.ATTR_MAX_QUEUE_DEPTH;
+import static org.apache.qpid.management.common.mbeans.ManagedQueue.ATTR_MSG_COUNT;
+import static org.apache.qpid.management.common.mbeans.ManagedQueue.ATTR_OWNER;
+import static org.apache.qpid.management.common.mbeans.ManagedQueue.ATTR_QUEUE_DEPTH;
+import static org.apache.qpid.management.common.mbeans.ManagedQueue.ATTR_RCVD_MSG_COUNT;
import org.apache.qpid.management.common.mbeans.ManagedBroker;
+import org.apache.qpid.management.common.mbeans.ManagedQueue;
import org.apache.qpid.management.ui.ApplicationRegistry;
import org.apache.qpid.management.ui.ManagedBean;
import org.apache.qpid.management.ui.ManagedServer;
-import org.apache.qpid.management.ui.ServerRegistry;
import org.apache.qpid.management.ui.jmx.MBeanUtility;
-import org.apache.qpid.management.ui.model.AttributeData;
import org.apache.qpid.management.ui.views.MBeanView;
import org.apache.qpid.management.ui.views.NavigationView;
import org.apache.qpid.management.ui.views.ViewUtility;
+import org.eclipse.jface.preference.PreferenceStore;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
@@ -53,7 +70,10 @@ import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
@@ -64,85 +84,216 @@ public class QueueTypeTabControl extends MBeanTypeTabControl
{
private MBeanServerConnection _mbsc;
private ManagedBroker _vhmb;
- //Map for storing queue depths for servers using Qpid JMX API 1.2 and below
- private Map<ManagedBean,Long> _queueDepths = new HashMap<ManagedBean, Long>();
+
+ private List<String> _selectedAttributes;
+ private PreferenceStore _preferences;
+ private Semaphore _tableViewerSemaphore = new Semaphore(1);
+
+ private static final String APP_DIR = ApplicationRegistry.DATA_DIR;
+ private static final String INI_FILENAME = APP_DIR + File.separator + "qpidmc_queue_attributes.ini";
+ private static final String INI_QUEUE_ATTRIBUES = "QueueAttributesSelection";
+
+ private static final ArrayList<String> FALLBACK_ATTRIBUTES_LIST = new ArrayList<String>();
+ static
+ {
+ FALLBACK_ATTRIBUTES_LIST.add(ATTR_NAME);
+ FALLBACK_ATTRIBUTES_LIST.add(ATTR_ACTIVE_CONSUMER_COUNT);
+ FALLBACK_ATTRIBUTES_LIST.add(ATTR_AUTODELETE);
+ FALLBACK_ATTRIBUTES_LIST.add(ATTR_CONSUMER_COUNT);
+ FALLBACK_ATTRIBUTES_LIST.add(ATTR_DURABLE);
+ FALLBACK_ATTRIBUTES_LIST.add(ATTR_MAX_MSG_AGE);
+ FALLBACK_ATTRIBUTES_LIST.add(ATTR_MAX_MSG_COUNT);
+ FALLBACK_ATTRIBUTES_LIST.add(ATTR_MAX_MSG_SIZE);
+ FALLBACK_ATTRIBUTES_LIST.add(ATTR_MAX_QUEUE_DEPTH);
+ FALLBACK_ATTRIBUTES_LIST.add(ATTR_MSG_COUNT);
+ FALLBACK_ATTRIBUTES_LIST.add(ATTR_OWNER);
+ FALLBACK_ATTRIBUTES_LIST.add(ATTR_QUEUE_DEPTH);
+ FALLBACK_ATTRIBUTES_LIST.add(ATTR_RCVD_MSG_COUNT);
+ }
+
+ private static final Map<String, Integer> DEFAULT_COLUMN_WIDTHS = new HashMap<String,Integer>();
+ static
+ {
+ DEFAULT_COLUMN_WIDTHS.put(ATTR_NAME, 215);
+ DEFAULT_COLUMN_WIDTHS.put(ATTR_OWNER,125);
+ DEFAULT_COLUMN_WIDTHS.put(ATTR_QUEUE_DEPTH,125);
+ }
public QueueTypeTabControl(TabFolder tabFolder, ManagedServer server, String virtualHost)
{
super(tabFolder,server,virtualHost,QUEUE);
_mbsc = (MBeanServerConnection) _serverRegistry.getServerConnection();
- _vhmb = (ManagedBroker) MBeanServerInvocationHandler.newProxyInstance(_mbsc,
+
+ //create a proxy for the VirtualHostManager mbean to use in retrieving the attribute names/values
+ _vhmb = MBeanServerInvocationHandler.newProxyInstance(_mbsc,
_vhostMbean.getObjectName(), ManagedBroker.class, false);
+
}
@Override
- public void refresh(ManagedBean mbean)
+ protected void init()
{
- if(_ApiVersion.greaterThanOrEqualTo(1, 3))
+ createIniFileIfNecessary();
+ loadAttributePreferences();
+ }
+
+ /**
+ * Create the ini file if it doesn't already exist.
+ */
+ public static void createIniFileIfNecessary()
+ {
+ File dir = new File(DATA_DIR);
+ if (!dir.exists())
{
- //Qpid JMX API 1.3+, use this virtualhosts VirtualHostManager MBean
- //to retrieve the Queue Name and Queue Depth
-
- Map<String,Long> queueNamesDepths = null;
- try
+ if(!dir.mkdir())
{
- queueNamesDepths = _vhmb.viewQueueNamesDepths();
+ System.err.println("Could not create application data directory " + DATA_DIR);
+ ViewUtility.popupErrorMessage("Error", "Fatal Error: Unable to create the application data directory: " + DATA_DIR);
+ System.exit(1);
}
- catch(Exception e)
+ }
+
+ File file = new File(INI_FILENAME);
+ try
+ {
+ if (!file.exists())
{
- MBeanUtility.handleException(_vhostMbean, e);
+ file.createNewFile();
}
+ }
+ catch (IOException ex)
+ {
+ System.err.println("Error creating the configuration file " + INI_FILENAME);
+ ViewUtility.popupErrorMessage("Error", "Fatal Error: Unable to create the configuration file: " + INI_FILENAME);
+ System.exit(1);
+ }
+ }
+
+ private void loadAttributePreferences()
+ {
+ _preferences = new PreferenceStore(INI_FILENAME);
+ List<String> attributesList = new ArrayList<String>();
+
+ //ensure the name is present, and first
+ attributesList.add(ManagedQueue.ATTR_NAME);
+
+ //add any others from the file
+ try
+ {
+ _preferences.load();
- _tableViewer.setInput(queueNamesDepths);
+ String selectedAttributes = _preferences.getString(INI_QUEUE_ATTRIBUES);
+ if (selectedAttributes.length() != 0)
+ {
+ String[] attributes = selectedAttributes.split(",");
+ for (String attr : attributes)
+ {
+ if(attr.equals(ManagedQueue.ATTR_NAME))
+ {
+ //the Name attribute is already present
+ continue;
+ }
+
+ attributesList.add(attr);
+ }
+ }
}
- else
+ catch (IOException e)
{
- //Qpid JMX API 1.2 or below, use the ManagedBeans and look
- //up the attribute value for each
- _mbeans = getMbeans();
- _tableViewer.setInput(_mbeans);
+ ViewUtility.popupErrorMessage("Error", "Unable to load previous attribute selections, defaulting to Name only");
+ System.err.println(e);
}
-
- layout();
+
+ _selectedAttributes = attributesList;
}
- @Override
- protected List<ManagedBean> getMbeans()
+ private void saveAttributePreferences()
{
- ServerRegistry serverRegistry = ApplicationRegistry.getServerRegistry(MBeanView.getServer());
+ String chosenAttributes = new String();
+
+ for(String attr : _selectedAttributes)
+ {
+ chosenAttributes = chosenAttributes.concat(attr) + ",";
+ }
+ //cut off last ","
+ int lastIndex = chosenAttributes.lastIndexOf(',');
+ if (lastIndex != -1)
+ {
+ chosenAttributes = chosenAttributes.substring(0,lastIndex);
+ }
+
+ _preferences.putValue(INI_QUEUE_ATTRIBUES, chosenAttributes);
try
{
- return extractQueueDetails(serverRegistry.getQueues(MBeanView.getVirtualHost()));
+ _preferences.save();
}
- catch(Exception e)
+ catch (IOException e)
{
- MBeanUtility.handleException(null, e);
- return null;
+ ViewUtility.popupErrorMessage("Error", "Unable to save the attribute selection, choices will be lost at shutdown");
+ System.err.println(e);
}
-
}
- private List<ManagedBean> extractQueueDetails(List<ManagedBean> list) throws Exception
+ @Override
+ public void refresh(ManagedBean mbean)
{
- _queueDepths.clear();
+ //Try locking. If we cant aquire the lock, dont bother getting new values.
+ //Either the attributes are being changed and these values would be out of date,
+ //or another thread is still in the process of refreshing
+ if(_tableViewerSemaphore.tryAcquire())
+ {
+ try
+ {
+ List<List<Object>> values = null;
- List<ManagedBean> items = new ArrayList<ManagedBean>();
- for (ManagedBean mbean : list)
- {
- AttributeData data = MBeanUtility.getAttributeData(mbean, ATTRIBUTE_QUEUE_DEPTH);
- _queueDepths.put(mbean, Long.valueOf(data.getValue().toString()));
+ if(_ApiVersion.greaterThanOrEqualTo(1, 3))
+ {
+ //Qpid JMX API 1.3+, use this virtualhosts VirtualHostManager MBean
+ //to retrieve the attributes values requested for all queues at once
+ try
+ {
+ values = _vhmb.retrieveQueueAttributeValues(_selectedAttributes.toArray(new String[0]));
+ }
+ catch(Exception e)
+ {
+ MBeanUtility.handleException(_vhostMbean, e);
+ }
+ }
+ else
+ {
+ //Qpid JMX API 1.2 or below, use the local ManagedBeans and look
+ //up the attribute values for each queue individually
+ _mbeans = getMbeans();
+ values = MBeanUtility.getQueueAttributes(_mbeans, _selectedAttributes.toArray(new String[0]));
+ }
- items.add(mbean);
+ _tableViewer.setInput(values);
+ layout();
+ }
+ finally
+ {
+ _tableViewerSemaphore.release();
+ }
}
-
- return items;
+
+ }
+
+ @Override
+ protected List<ManagedBean> getMbeans()
+ {
+ return _serverRegistry.getQueues(_virtualHost);
+ }
+
+ private void clearTableComposite()
+ {
+ ViewUtility.disposeChildren(_tableComposite);
}
@Override
- protected void createTable(Composite tableComposite)
+ protected void createTable()
{
- _table = new Table (tableComposite, SWT.MULTI | SWT.SCROLL_LINE | SWT.BORDER | SWT.FULL_SELECTION);
+ _table = new Table (_tableComposite, SWT.MULTI | SWT.SCROLL_LINE | SWT.BORDER | SWT.FULL_SELECTION);
_table.setLinesVisible (true);
_table.setHeaderVisible (true);
GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
@@ -150,31 +301,28 @@ public class QueueTypeTabControl extends MBeanTypeTabControl
_tableViewer = new TableViewer(_table);
- String[] titles = new String[]{"Queue Name", "Queue Depth"};
- int[] bounds = new int[]{325, 200};
-
- final TableSorter tableSorter;
-
- if(_ApiVersion.greaterThanOrEqualTo(1, 3))
- {
- //QpidJMX API 1.3+ using the new getQueueNamesDepths method in VHostManager MBean.
- //requires sorting Map.Entry elements
- tableSorter = new NewerTableSorter();
- }
- else
- {
- //QpidJMX API 1.2 or below. Requires sorting ManagedBeans and using the _queueDepths map
- tableSorter = new OlderTableSorter();
- }
+ final QueueTableSorter tableSorter = new QueueTableSorter();
- for (int i = 0; i < titles.length; i++)
+ for (int i = 0; i < _selectedAttributes.size(); i++)
{
final int index = i;
final TableViewerColumn viewerColumn = new TableViewerColumn(_tableViewer, SWT.NONE);
final TableColumn column = viewerColumn.getColumn();
- column.setText(titles[i]);
- column.setWidth(bounds[i]);
+ String attrName = _selectedAttributes.get(i);
+ column.setMoveable(true);
+ column.setText(attrName);
+ column.pack();
+ if(DEFAULT_COLUMN_WIDTHS.containsKey(attrName))
+ {
+ //retrieve the desired default width
+ column.setWidth(DEFAULT_COLUMN_WIDTHS.get(attrName));
+ }
+ else
+ {
+ //add padding for sort direction indicator
+ column.setWidth(column.getWidth() + 15);
+ }
column.setResizable(true);
//Setting the right sorter
@@ -201,75 +349,166 @@ public class QueueTypeTabControl extends MBeanTypeTabControl
});
}
-
- if(_ApiVersion.greaterThanOrEqualTo(1, 3))
- {
- _tableViewer.setContentProvider(new NewerContentProviderImpl());
- _tableViewer.setLabelProvider(new NewerLabelProviderImpl());
- }
- else
- {
- _tableViewer.setContentProvider(new OlderContentProviderImpl());
- _tableViewer.setLabelProvider(new OlderLabelProviderImpl());
- }
-
+
+ _tableViewer.setContentProvider(new QueueContentProviderImpl());
+ _tableViewer.setLabelProvider(new QueueLabelProviderImpl());
+
_tableViewer.setUseHashlookup(true);
_tableViewer.setSorter(tableSorter);
_table.setSortColumn(_table.getColumn(0));
_table.setSortDirection(SWT.UP);
}
- /**
- * Content Provider class for the table viewer for Qpid JMX API 1.2 and below.
- */
- private class OlderContentProviderImpl implements IStructuredContentProvider
+ protected void createLowerAreaButton(Composite parent)
{
+ Composite lowerButtonComposite = _toolkit.createComposite(parent, SWT.NONE);
+ GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, false);
+ lowerButtonComposite.setLayoutData(gridData);
+ lowerButtonComposite.setLayout(new GridLayout());
- public void inputChanged(Viewer v, Object oldInput, Object newInput)
+ final Button attributesButton = _toolkit.createButton(lowerButtonComposite, "Select Attributes ...", SWT.PUSH);
+ gridData = new GridData(SWT.RIGHT, SWT.CENTER, true, false);
+ attributesButton.setLayoutData(gridData);
+ attributesButton.addSelectionListener(new SelectionAdapter()
{
-
- }
-
- public void dispose()
+ public void widgetSelected(SelectionEvent e)
+ {
+ chooseAttributes(attributesButton.getShell());
+ }
+ });
+ }
+
+ private void chooseAttributes(final Shell parent)
+ {
+
+ List<String> availableAttributes;
+ if(_ApiVersion.greaterThanOrEqualTo(1, 3))
{
-
+ //Qpid JMX API 1.3+, request the current queue attributes names from the broker
+ try
+ {
+ availableAttributes = _vhmb.retrieveQueueAttributeNames();
+ }
+ catch (IOException e)
+ {
+ availableAttributes = new ArrayList<String>(FALLBACK_ATTRIBUTES_LIST);
+ }
}
-
- @SuppressWarnings("unchecked")
- public Object[] getElements(Object parent)
+ else
{
- return ((List<ManagedBean>) parent).toArray();
+ //Qpid JMX API 1.2 or below, use the falllback list of names.
+ availableAttributes = new ArrayList<String>(FALLBACK_ATTRIBUTES_LIST);
}
- }
-
- /**
- * Label Provider class for the table viewer for Qpid JMX API 1.2 and below.
- */
- private class OlderLabelProviderImpl extends LabelProvider implements ITableLabelProvider
- {
- @Override
- public String getColumnText(Object element, int columnIndex)
+
+
+ final List<String> chosenAttributes = new ArrayList<String>();
+
+ final Shell shell = ViewUtility.createModalDialogShell(parent, "Select Attributes");
+
+ Composite attributesComposite = _toolkit.createComposite(shell, SWT.NONE);
+ attributesComposite.setBackground(shell.getBackground());
+ attributesComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ attributesComposite.setLayout(new GridLayout(2,false));
+
+ //add a selected-but-disabled check box for the Name attribute (its a mandatory attribute)
+ final Button nameCheckbox = new Button(attributesComposite, SWT.CHECK);
+ nameCheckbox.setText(ManagedQueue.ATTR_NAME);
+ nameCheckbox.setSelection(true);
+ nameCheckbox.setEnabled(false);
+
+ for(String attr : availableAttributes)
{
- ManagedBean mbean = (ManagedBean) element;
+ if(attr.equals(ManagedQueue.ATTR_NAME))
+ {
+ //Name attribute is mandatory and gets added to the front of the list later
+ continue;
+ }
+
+ final Button attrButton = new Button(attributesComposite, SWT.CHECK);
+ attrButton.setText(attr);
- switch (columnIndex)
+ //if it was checked before, select it again now
+ if(_selectedAttributes.contains(attr))
{
- case 0 : // name column
- return mbean.getName();
- case 1 : // queue depth column
- return getQueueDepthString(_queueDepths.get(mbean));
- default:
- return "-";
+ attrButton.setSelection(true);
+ chosenAttributes.add(attr);
}
+
+ //add a selection listener to update the selected attribute list
+ attrButton.addSelectionListener(new SelectionAdapter()
+ {
+ public void widgetSelected(SelectionEvent e)
+ {
+ if(attrButton.getSelection())
+ {
+ chosenAttributes.add(attrButton.getText());
+ }
+ else
+ {
+ chosenAttributes.remove(attrButton.getText());
+ }
+ }
+ });
}
- @Override
- public Image getColumnImage(Object element, int columnIndex)
+ Composite okCancelButtonsComp = _toolkit.createComposite(shell);
+ okCancelButtonsComp.setBackground(shell.getBackground());
+ okCancelButtonsComp.setLayoutData(new GridData(SWT.RIGHT, SWT.FILL, true, true));
+ okCancelButtonsComp.setLayout(new GridLayout(2,false));
+
+ Button okButton = _toolkit.createButton(okCancelButtonsComp, "OK", SWT.PUSH);
+ okButton.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false));
+ Button cancelButton = _toolkit.createButton(okCancelButtonsComp, "Cancel", SWT.PUSH);
+ cancelButton.setLayoutData(new GridData(SWT.RIGHT, SWT.TOP, false, false));
+
+ okButton.addSelectionListener(new SelectionAdapter()
{
- return null;
- }
+ public void widgetSelected(SelectionEvent e)
+ {
+ shell.dispose();
+
+ //The Name attribute is mandatory, add it now, also
+ //ensuring it is left-most by placing it first in the list
+ List<String> newSelection = new ArrayList<String>();
+ newSelection.add(ManagedQueue.ATTR_NAME);
+
+ //now add all remaining choices in alphabetical order
+ Collections.sort(chosenAttributes);
+ newSelection.addAll(chosenAttributes);
+
+ _tableViewerSemaphore.acquireUninterruptibly();
+ try
+ {
+ _selectedAttributes = newSelection;
+
+ clearTableComposite();
+ createTable();
+ saveAttributePreferences();
+ }
+ finally
+ {
+ _tableViewerSemaphore.release();
+ }
+
+ refresh(_mbean);
+ }
+ });
+
+ cancelButton.addSelectionListener(new SelectionAdapter()
+ {
+ public void widgetSelected(SelectionEvent e)
+ {
+ shell.dispose();
+ }
+ });
+
+ shell.setDefaultButton(okButton);
+ shell.pack();
+ ViewUtility.centerChildInParentShell(parent, shell);
+
+ shell.open();
}
-
+
private String getQueueDepthString(Long value)
{
if(value == null)
@@ -277,7 +516,12 @@ public class QueueTypeTabControl extends MBeanTypeTabControl
return "-";
}
- if (_ApiVersion.lessThanOrEqualTo(1,1))
+ if (_ApiVersion.greaterThanOrEqualTo(1,2))
+ {
+ //Qpid JMX API 1.2 or above, returns Bytes
+ return convertLongBytesToText(value);
+ }
+ else
{
//Qpid JMX API 1.1 or below, returns KB
double mb = 1024.0;
@@ -291,33 +535,37 @@ public class QueueTypeTabControl extends MBeanTypeTabControl
return value + " KB";
}
}
- else
+ }
+
+ private String convertLongBytesToText(Long value)
+ {
+ if(value == null)
{
- //Qpid JMX API 1.2 or above, returns Bytes
- double mb = 1024.0 * 1024.0;
- double kb = 1024.0;
+ return "-";
+ }
- if(value >= mb) //MB
- {
- return String.format("%.3f", (Double)(value / mb)) + " MB";
- }
- else if (value >= kb) //KB
- {
- return String.format("%.3f", (Double)(value / kb)) + " KB";
- }
- else //Bytes
- {
- return value + " Bytes";
- }
+ double mb = 1024.0 * 1024.0;
+ double kb = 1024.0;
+
+ if(value >= mb) //MB
+ {
+ return String.format("%.3f", (Double)((double)value / mb)) + " MB";
+ }
+ else if (value >= kb) //KB
+ {
+ return String.format("%.3f", (Double)((double)value / kb)) + " KB";
+ }
+ else //Bytes
+ {
+ return value + " Bytes";
}
}
-
/**
- * Abstract sorter class for the table viewer.
+ * sorter class for the table viewer.
*
*/
- private abstract class TableSorter extends ViewerSorter
+ private class QueueTableSorter extends ViewerSorter
{
protected int column;
protected static final int ASCENDING = 0;
@@ -325,7 +573,7 @@ public class QueueTypeTabControl extends MBeanTypeTabControl
protected int direction;
- public TableSorter()
+ public QueueTableSorter()
{
this.column = 0;
direction = ASCENDING;
@@ -346,76 +594,21 @@ public class QueueTypeTabControl extends MBeanTypeTabControl
}
}
- @Override
- public abstract int compare(Viewer viewer, Object e1, Object e2);
- }
-
- /**
- * sorter class for the table viewer for Qpid JMX API 1.2 and below.
- *
- */
- private class OlderTableSorter extends TableSorter
- {
- public OlderTableSorter()
- {
- super();
- }
-
- @Override
- public int compare(Viewer viewer, Object e1, Object e2)
- {
- ManagedBean mbean1 = (ManagedBean) e1;
- ManagedBean mbean2 = (ManagedBean) e2;
-
- int comparison = 0;
- switch(column)
- {
- case 0: //name
- comparison = mbean1.getName().compareTo(mbean2.getName());
- break;
- case 1: //queue depth
- comparison = _queueDepths.get(mbean1).compareTo(_queueDepths.get(mbean2));
- default:
- comparison = 0;
- }
- // If descending order, flip the direction
- if(direction == DESCENDING)
- {
- comparison = -comparison;
- }
- return comparison;
- }
- }
-
- /**
- * sorter class for the table viewer for Qpid JMX API 1.3 and above.
- *
- */
- private class NewerTableSorter extends TableSorter
- {
- public NewerTableSorter()
- {
- super();
- }
-
@SuppressWarnings("unchecked")
@Override
public int compare(Viewer viewer, Object e1, Object e2)
{
- Map.Entry<String, Long> queue1 = (Map.Entry<String, Long>) e1;
- Map.Entry<String, Long> queue2 = (Map.Entry<String, Long>) e2;
+ List<Object> queue1 = (List<Object>) e1;
+ List<Object> queue2 = (List<Object>) e2;
int comparison = 0;
switch(column)
{
- case 0://name
- comparison = (queue1.getKey()).compareTo(queue2.getKey());
- break;
- case 1://queue depth
- comparison = (queue1.getValue()).compareTo(queue2.getValue());;
- break;
default:
- comparison = 0;
+ if(queue1.get(column) instanceof Comparable)
+ {
+ comparison = ((Comparable)queue1.get(column)).compareTo((Comparable) queue2.get(column));
+ }
}
// If descending order, flip the direction
if(direction == DESCENDING)
@@ -429,7 +622,7 @@ public class QueueTypeTabControl extends MBeanTypeTabControl
/**
* Content Provider class for the table viewer for Qpid JMX API 1.3 and above.
*/
- private class NewerContentProviderImpl implements IStructuredContentProvider
+ private class QueueContentProviderImpl implements IStructuredContentProvider
{
public void inputChanged(Viewer v, Object oldInput, Object newInput)
@@ -445,30 +638,41 @@ public class QueueTypeTabControl extends MBeanTypeTabControl
@SuppressWarnings("unchecked")
public Object[] getElements(Object parent)
{
- Map<String, Long> map = (Map<String, Long>) parent;
- return map.entrySet().toArray(new Map.Entry[0]);
+ return ((List<List<Object>>) parent).toArray();
}
}
/**
* Label Provider class for the table viewer for for Qpid JMX API 1.3 and above.
*/
- private class NewerLabelProviderImpl extends LabelProvider implements ITableLabelProvider
+ private class QueueLabelProviderImpl extends LabelProvider implements ITableLabelProvider
{
@SuppressWarnings("unchecked")
@Override
public String getColumnText(Object element, int columnIndex)
{
- Map.Entry<String, Long> queue = (Map.Entry<String, Long>) element;
+ List<Object> attributes = (List<Object>) element;
switch (columnIndex)
{
- case 0 : // name column
- return queue.getKey();
- case 1 : // depth column
- return getQueueDepthString(queue.getValue());
default :
- return "-";
+ String attrName = _selectedAttributes.get(columnIndex);
+
+ if(ATTR_QUEUE_DEPTH.equals(attrName))
+ {
+ return getQueueDepthString((Long) attributes.get(columnIndex));
+ }
+ else if(ATTR_MAX_QUEUE_DEPTH.equals(attrName) || ATTR_MAX_MSG_SIZE.equals(attrName))
+ {
+ Number val = (Number)attributes.get(columnIndex);
+ return convertLongBytesToText(val.longValue());
+ }
+ else if(ATTR_MAX_MSG_AGE.equals(attrName))
+ {
+ return String.valueOf(attributes.get(columnIndex) + "ms");
+ }
+
+ return String.valueOf(attributes.get(columnIndex));
}
}
@@ -479,6 +683,7 @@ public class QueueTypeTabControl extends MBeanTypeTabControl
}
}
+ @SuppressWarnings("unchecked")
@Override
protected void addMBeanToFavourites()
{
@@ -492,25 +697,13 @@ public class QueueTypeTabControl extends MBeanTypeTabControl
int[] selectedIndices = _table.getSelectionIndices();
ArrayList<ManagedBean> selectedMBeans = new ArrayList<ManagedBean>();
-
- if(_ApiVersion.greaterThanOrEqualTo(1, 3))
- {
- //if we have Qpid JMX API 1.3+ the entries are created from Map.Entry<String,Long>
- for(int index = 0; index < selectedIndices.length ; index++)
- {
- Map.Entry<String, Long> queueEntry = (Map.Entry<String, Long>) _table.getItem(selectedIndices[index]).getData();
- String queueName = queueEntry.getKey();
- selectedMBeans.add(_serverRegistry.getQueue(queueName, _virtualHost));
- }
- }
- else
+
+ //the entries are created from an List<Object> with the attribute values (name first)
+ for(int index = 0; index < selectedIndices.length ; index++)
{
- //if we have a Qpid JMX API 1.2 or less server, entries are created from ManagedBeans directly
- for(int index = 0; index < selectedIndices.length ; index++)
- {
- ManagedBean mbean = (ManagedBean) _table.getItem(selectedIndices[index]).getData();
- selectedMBeans.add(mbean);
- }
+ List<Object> queueEntry = (List<Object>) _table.getItem(selectedIndices[index]).getData();
+ String queueName = (String) queueEntry.get(0);
+ selectedMBeans.add(_serverRegistry.getQueue(queueName, _virtualHost));
}
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
@@ -531,6 +724,7 @@ public class QueueTypeTabControl extends MBeanTypeTabControl
}
}
+ @SuppressWarnings("unchecked")
@Override
protected void openMBean()
{
@@ -542,20 +736,11 @@ public class QueueTypeTabControl extends MBeanTypeTabControl
}
ManagedBean selectedMBean;
-
- if(_ApiVersion.greaterThanOrEqualTo(1, 3))
- {
- //if we have Qpid JMX API 1.3+ the entries are created from Map.Entry<String,Long>
- Map.Entry<String, Long> queueEntry = (Map.Entry<String, Long>) _table.getItem(selectionIndex).getData();
- String queueName = queueEntry.getKey();
- selectedMBean = _serverRegistry.getQueue(queueName, _virtualHost);
- }
- else
- {
- //if we have a Qpid JMX API 1.2 or less server, entries are created from ManagedBeans directly
- selectedMBean = (ManagedBean)_table.getItem(selectionIndex).getData();
- }
+ //the entries are created from an List<Object> with the attribute values (name first)
+ List<Object> queueEntry = (List<Object>) _table.getItem(selectionIndex).getData();
+ String queueName = (String) queueEntry.get(0);
+ selectedMBean = _serverRegistry.getQueue(queueName, _virtualHost);
if(selectedMBean == null)
{
diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/users/UserManagementTabControl.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/users/UserManagementTabControl.java
index b474827493..2051beafac 100644
--- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/users/UserManagementTabControl.java
+++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/users/UserManagementTabControl.java
@@ -294,9 +294,8 @@ public class UserManagementTabControl extends TabControl
{
char[] password = id.getValue().toCharArray();
- // Retrieve the MBean version. If we have a version 1 UMMBean then
- // it expects the password to be sent as a hashed value.
- if (_mbean.getVersion() == 1)
+ // Qpid JMX API 1.1 and below expects the password to be sent as a hashed value.
+ if (_ApiVersion.lessThanOrEqualTo(1,1))
{
try
{
@@ -768,6 +767,24 @@ public class UserManagementTabControl extends TabControl
return;
}
+ char[] passwordChars = password.toCharArray();
+
+ // Qpid JMX API 1.1 and below expects the password to be sent as a hashed value.
+ if (_ApiVersion.lessThanOrEqualTo(1,1))
+ {
+ try
+ {
+ passwordChars = ViewUtility.getHash(password);
+ }
+ catch (Exception hashException)
+ {
+ ViewUtility.popupErrorMessage("Set Password",
+ "Unable to calculate hash for Password:"
+ + hashException.getMessage());
+ return;
+ }
+ }
+
boolean read = readButton.getSelection();
boolean write = writeButton.getSelection();
boolean admin = adminButton.getSelection();
@@ -775,7 +792,7 @@ public class UserManagementTabControl extends TabControl
shell.dispose();
try
{
- boolean result = _ummb.createUser(username, password.toCharArray(), read, write, admin);
+ boolean result = _ummb.createUser(username, passwordChars, read, write, admin);
ViewUtility.operationResultFeedback(result, "Created user", "Failed to create user");
}
catch(Exception e5)
diff --git a/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/AllObjects.java b/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/AllObjects.java
index 253119f057..8b2099f3e0 100644
--- a/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/AllObjects.java
+++ b/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/AllObjects.java
@@ -28,7 +28,7 @@ public class AllObjects extends ObjectNames
public AllObjects(MBeanServerConnection mbsc)
{
- ObjectNames(mbsc);
+ super(mbsc);
}
public void setQueryString(String object, String name)
diff --git a/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/ConnectionObject.java b/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/ConnectionObject.java
index cb1891481c..2746b2016f 100644
--- a/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/ConnectionObject.java
+++ b/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/ConnectionObject.java
@@ -28,7 +28,7 @@ public class ConnectionObject extends ObjectNames
public ConnectionObject(MBeanServerConnection mbsc)
{
/* calling parent classes constructor */
- ObjectNames(mbsc);
+ super(mbsc);
}
public void setQueryString(String object, String name, String vhost)
diff --git a/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/ExchangeObject.java b/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/ExchangeObject.java
index 2c1c4c47ff..96d7e6e5dc 100644
--- a/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/ExchangeObject.java
+++ b/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/ExchangeObject.java
@@ -27,8 +27,7 @@ public class ExchangeObject extends ObjectNames
{
public ExchangeObject(MBeanServerConnection mbsc)
{
- ObjectNames(mbsc);
-
+ super(mbsc);
}
public void setQueryString(String object, String name, String vhost)
diff --git a/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/ObjectNames.java b/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/ObjectNames.java
index dc676ebfe3..7791b84a4f 100644
--- a/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/ObjectNames.java
+++ b/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/ObjectNames.java
@@ -35,17 +35,16 @@ public class ObjectNames
{
public String querystring = null;
public MBeanServerConnection mbsc;
- public Set set = null;
+ public Set<ObjectName> set = null;
public String attributes = "";
public String attributevalues = "";// = null;
- /* method return the Set objects according to the Object type */
- public void ObjectNames(MBeanServerConnection mbsc)
+ public ObjectNames(MBeanServerConnection mbsc)
{
this.mbsc = mbsc;
}
- public Set returnObjects()
+ public Set<ObjectName> returnObjects()
{
try
{
diff --git a/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/QueueObject.java b/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/QueueObject.java
index e61d1e626c..7c82682c27 100644
--- a/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/QueueObject.java
+++ b/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/QueueObject.java
@@ -30,7 +30,7 @@ public class QueueObject extends ObjectNames
{
public QueueObject(MBeanServerConnection mbsc)
{
- ObjectNames(mbsc);
+ super(mbsc);
}
public void setQueryString(String object, String name, String vhost)
diff --git a/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/UserManagementObject.java b/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/UserManagementObject.java
index 1635877cea..fd5bc0ca72 100644
--- a/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/UserManagementObject.java
+++ b/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/UserManagementObject.java
@@ -27,7 +27,7 @@ public class UserManagementObject extends ObjectNames
{
public UserManagementObject(MBeanServerConnection mbsc)
{
- ObjectNames(mbsc);
+ super(mbsc);
}
public void setQueryString(String object, String name, String vhost)
diff --git a/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/VirtualHostObject.java b/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/VirtualHostObject.java
index c4df7a7093..77f8b66ac0 100644
--- a/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/VirtualHostObject.java
+++ b/qpid/java/management/tools/qpid-cli/src/org/apache/qpid/commands/objects/VirtualHostObject.java
@@ -28,7 +28,7 @@ public class VirtualHostObject extends ObjectNames
public VirtualHostObject(MBeanServerConnection mbsc)
{
- ObjectNames(mbsc);
+ super(mbsc);
}
public void setQueryString(String object, String name, String vhost)
diff --git a/qpid/java/systests/build.xml b/qpid/java/systests/build.xml
index 1e44116119..ac3c77e4a3 100644
--- a/qpid/java/systests/build.xml
+++ b/qpid/java/systests/build.xml
@@ -20,7 +20,7 @@ nn - or more contributor license agreements. See the NOTICE file
-->
<project name="System Tests" default="build">
- <property name="module.depends" value="client management/common broker broker/test common junit-toolkit"/>
+ <property name="module.depends" value="client management/tools/qpid-cli management/eclipse-plugin management/common broker broker/test common junit-toolkit"/>
<property name="module.test.src" location="src/main/java"/>
<property name="module.test.excludes"
value="**/TTLTest.java,**/DropInTest.java,**/TestClientControlledTest.java"/>
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java
new file mode 100644
index 0000000000..7a2266902b
--- /dev/null
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/ManagementActorLoggingTest.java
@@ -0,0 +1,669 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+package org.apache.qpid.management.jmx;
+
+import org.apache.qpid.commands.objects.AllObjects;
+import org.apache.qpid.management.common.JMXConnnectionFactory;
+import org.apache.qpid.management.common.mbeans.ManagedBroker;
+import org.apache.qpid.management.common.mbeans.ManagedConnection;
+import org.apache.qpid.management.common.mbeans.ManagedExchange;
+import org.apache.qpid.server.logging.AbstractTestLogging;
+import org.apache.qpid.server.logging.subjects.AbstractTestLogSubject;
+
+import javax.jms.Connection;
+import javax.management.JMException;
+import javax.management.MBeanException;
+import javax.management.MBeanServerConnection;
+import javax.management.MBeanServerInvocationHandler;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Test class to test if any change in the broker JMX code is affesting the management console
+ * There are some hardcoding of management feature names and parameter names to create a customized
+ * look in the console.
+ */
+public class ManagementActorLoggingTest extends AbstractTestLogging
+{
+ MBeanServerConnection _mbsc;
+ JMXConnector _jmxc;
+ private static final String USER = "admin";
+
+ @Override
+ public void setUp() throws Exception
+ {
+ setConfigurationProperty("management.enabled", "true");
+ super.setUp();
+
+ if (isExternalBroker())
+ {
+ _jmxc = JMXConnnectionFactory.getJMXConnection(
+ 5000, "127.0.0.1",
+ getManagementPort(getPort()), USER, USER);
+
+ _mbsc = _jmxc.getMBeanServerConnection();
+ }
+ }
+
+ @Override
+ public void tearDown() throws Exception
+ {
+ if (isExternalBroker())
+ {
+ _jmxc.close();
+ }
+ super.tearDown();
+ }
+
+ /**
+ * Description:
+ * When a JMX Management connection is made then this will be logged out.
+ *
+ * Input:
+ *
+ * 1. Running Broker
+ * 2. Connect Management client via JMX
+ * Output:
+ *
+ * <date> MNG-1007 : Open <user>
+ *
+ * Validation Steps:
+ * 1. The MNG ID is correct
+ * 2. The user is correct
+ *
+ * On connection close a MNG-1008 is expected
+ *
+ * * <date> MNG-1008 : Close
+ *
+ * Validation Steps:
+ * 1. The MNG ID is correct
+ *
+ * @throws java.io.IOException - if there is a problem reseting the log monitor
+ */
+ public void testJMXManagementConsoleConnection() throws IOException
+ {
+ if (isExternalBroker())
+ {
+ List<String> results = _monitor.findMatches("MNG-1007");
+
+ assertEquals("Unexpected Management Connection count", 1, results.size());
+
+ String log = getLog(results.get(0));
+
+ validateMessageID("MNG-1007", log);
+
+ assertTrue("User not in log message:" + log, log.endsWith(USER));
+ // Extract the id from the log string
+ // MESSAGE [mng:1(rmi://169.24.29.116)] MNG-1007 : Open : User admin
+ int connectionID = Integer.parseInt(fromActor(getLog(results.get(0))).charAt(4) + "");
+
+ results = _monitor.findMatches("MNG-1008");
+
+ assertEquals("Unexpected Management Connection close count", 0, results.size());
+
+ _jmxc.close();
+
+ results = _monitor.findMatches("MNG-1008");
+
+ assertEquals("Unexpected Management Connection count", 1, results.size());
+
+ assertEquals("Close does not have same id as open,", connectionID,
+ Integer.parseInt(fromActor(getLog(results.get(0))).charAt(4) + ""));
+ }
+ }
+
+ /**
+ * Description:
+ * When a connected client has its connection closed via the Management Console this will be logged as a CON-1002 message.
+ * Input:
+ *
+ * 1. Running Broker
+ * 2. Connected Client
+ * 3. Connection is closed via Management Console
+ * Output:
+ *
+ * <date> CON-1002 : Close
+ *
+ * Validation Steps:
+ * 4. The CON ID is correct
+ * 5. This must be the last CON message for the Connection
+ * 6. It must be preceded by a CON-1001 for this Connection
+ *
+ * @throws Exception - {@see ManagedConnection.closeConnection and #getConnection}
+ * @throws java.io.IOException - if there is a problem reseting the log monitor
+ */
+ public void testConnectionCloseViaManagement() throws IOException, Exception
+ {
+ if (isExternalBroker())
+ {
+
+ //Create a connection to the broker
+ Connection connection = getConnection();
+
+ // Get all active AMQP connections
+ AllObjects allObject = new AllObjects(_mbsc);
+ allObject.querystring = "org.apache.qpid:type=VirtualHost.Connection,*";
+
+ Set<ObjectName> objectNames = allObject.returnObjects();
+
+ assertEquals("More than one test connection returned", 1, objectNames.size());
+
+ ObjectName connectionName = objectNames.iterator().next();
+
+ ManagedConnection mangedConnection = MBeanServerInvocationHandler.
+ newProxyInstance(_mbsc, connectionName,
+ ManagedConnection.class, false);
+
+ //Remove the connection close from any 0-10 connections
+ _monitor.reset();
+
+ //Close the connection
+ mangedConnection.closeConnection();
+
+ //Validate results
+ List<String> results = _monitor.findMatches("CON-1002");
+
+
+ assertEquals("Unexpected Connection Close count", 1, results.size());
+ }
+ }
+
+ /**
+ * Description:
+ * Exchange creation is possible from the Management Console.
+ * When an exchanged is created in this way then a EXH-1001 create message
+ * is expected to be logged.
+ * Input:
+ *
+ * 1. Running broker
+ * 2. Connected Management Console
+ * 3. Exchange Created via Management Console
+ * Output:
+ *
+ * EXH-1001 : Create : [Durable] Type:<value> Name:<value>
+ *
+ * Validation Steps:
+ * 4. The EXH ID is correct
+ * 5. The correct tags are present in the message based on the create options
+ *
+ * @throws java.io.IOException - if there is a problem reseting the log monitor
+ * @throws javax.management.JMException - {@see #createQueue and ManagedExchange.deleteQueue}
+ */
+ public void testCreateExchangeDirectTransientViaManagementConsole() throws IOException, JMException
+ {
+ if (isExternalBroker())
+ {
+ //Remove any previous exchange declares
+ _monitor.reset();
+
+ createExchange("direct");
+
+ // Validate
+
+ //1 - ID is correct
+ List<String> results = _monitor.findMatches("EXH-1001");
+
+ assertEquals("More than one exchange creation found", 1, results.size());
+
+ String log = getLog(results.get(0));
+
+ // Validate correct exchange name
+ assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName()));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+
+ }
+ }
+
+ public void testCreateExchangeTopicTransientViaManagementConsole() throws IOException, JMException
+ {
+ if (isExternalBroker())
+ {
+ //Remove any previous exchange declares
+ _monitor.reset();
+
+ createExchange("topic");
+
+ // Validate
+
+ //1 - ID is correct
+ List<String> results = _monitor.findMatches("EXH-1001");
+
+ assertEquals("More than one exchange creation found", 1, results.size());
+
+ String log = getLog(results.get(0));
+
+ // Validate correct exchange name
+ assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName()));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+
+ }
+ }
+
+ public void testCreateExchangeFanoutTransientViaManagementConsole() throws IOException, JMException
+ {
+ if (isExternalBroker())
+ {
+ //Remove any previous exchange declares
+ _monitor.reset();
+
+ createExchange("fanout");
+
+ // Validate
+
+ //1 - ID is correct
+ List<String> results = _monitor.findMatches("EXH-1001");
+
+ assertEquals("More than one exchange creation found", 1, results.size());
+
+ String log = getLog(results.get(0));
+
+ // Validate correct exchange name
+ assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName()));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+
+ }
+ }
+
+ public void testCreateExchangeHeadersTransientViaManagementConsole() throws IOException, JMException
+ {
+ if (isExternalBroker())
+ {
+ //Remove any previous exchange declares
+ _monitor.reset();
+
+ createExchange("headers");
+
+ // Validate
+
+ //1 - ID is correct
+ List<String> results = _monitor.findMatches("EXH-1001");
+
+ assertEquals("More than one exchange creation found", 1, results.size());
+
+ String log = getLog(results.get(0));
+
+ // Validate correct exchange name
+ assertTrue("Incorrect exchange name created:" + log, log.endsWith(getName()));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+
+ }
+ }
+
+ /**
+ * Description:
+ * Queue creation is possible from the Management Console. When a queue is created in this way then a QUE-1001 create message is expected to be logged.
+ * Input:
+ *
+ * 1. Running broker
+ * 2. Connected Management Console
+ * 3. Queue Created via Management Console
+ * Output:
+ *
+ * <date> QUE-1001 : Create : Transient Owner:<name>
+ *
+ * Validation Steps:
+ * 4. The QUE ID is correct
+ * 5. The correct tags are present in the message based on the create options
+ *
+ * @throws java.io.IOException - if there is a problem reseting the log monitor
+ * @throws javax.management.JMException - {@see #createQueue and ManagedExchange.deleteQueue}
+ */
+ public void testCreateQueueTransientViaManagementConsole() throws IOException, JMException
+ {
+ if (isExternalBroker())
+ {
+ //Remove any previous queue declares
+ _monitor.reset();
+
+ createQueue();
+
+ // Validate
+
+ List<String> results = _monitor.findMatches("QUE-1001");
+
+ assertEquals("More than one queue creation found", 1, results.size());
+
+ String log = getLog(results.get(0));
+
+ // Validate correct queue name
+ String subject = fromSubject(log);
+ assertEquals("Incorrect queue name created", getName(), AbstractTestLogSubject.getSlice("qu", subject));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+ }
+ }
+
+ /**
+ * Description:
+ * The ManagementConsole can be used to delete a queue. When this is done a QUE-1002 Deleted message must be logged.
+ * Input:
+ *
+ * 1. Running Broker
+ * 2. Queue created on the broker with no subscribers
+ * 3. Management Console connected
+ * 4. Queue is deleted via Management Console
+ * Output:
+ *
+ * <date> QUE-1002 : Deleted
+ *
+ * Validation Steps:
+ * 5. The QUE ID is correct
+ *
+ * @throws java.io.IOException - if there is a problem reseting the log monitor
+ * @throws javax.management.JMException - {@see #createQueue and ManagedExchange.deleteQueue}
+ */
+ public void testQueueDeleteViaManagementConsole() throws IOException, JMException
+ {
+ if (isExternalBroker())
+ {
+ //Remove any previous queue declares
+ _monitor.reset();
+
+ createQueue();
+
+ ManagedBroker managedBroker = MBeanServerInvocationHandler.
+ newProxyInstance(_mbsc, getVirtualHostManagerObjectName(),
+ ManagedBroker.class, false);
+
+ managedBroker.deleteQueue(getName());
+
+ List<String> results = _monitor.findMatches("QUE-1002");
+
+ assertEquals("More than one queue deletion found", 1, results.size());
+
+ String log = getLog(results.get(0));
+
+ // Validate correct binding
+ String subject = fromSubject(log);
+ assertEquals("Incorrect queue named in delete", getName(), AbstractTestLogSubject.getSlice("qu", subject));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+
+ }
+ }
+
+ /**
+ * Description:
+ * The binding of a Queue and an Exchange is done via a Binding. When this Binding is created via the Management Console a BND-1001 Create message will be logged.
+ * Input:
+ *
+ * 1. Running Broker
+ * 2. Connected Management Console
+ * 3. Use Management Console to perform binding
+ * Output:
+ *
+ * <date> BND-1001 : Create
+ *
+ * Validation Steps:
+ * 4. The BND ID is correct
+ * 5. This will be the first message for the given binding
+ *
+ * @throws java.io.IOException - if there is a problem reseting the log monitor
+ * @throws javax.management.JMException - {@see #createQueue and ManagedExchange.createNewBinding}
+ */
+ public void testBindingCreateOnDirectViaManagementConsole() throws IOException, JMException
+ {
+ if (isExternalBroker())
+ {
+ //Remove any previous queue declares
+ _monitor.reset();
+
+ createQueue();
+
+ ManagedExchange managedExchange = MBeanServerInvocationHandler.
+ newProxyInstance(_mbsc, getExchange("amq.direct"),
+ ManagedExchange.class, false);
+
+ managedExchange.createNewBinding(getName(), getName());
+
+ List<String> results = _monitor.findMatches("BND-1001");
+
+ assertEquals("More than one bind creation found", 1, results.size());
+
+ String log = getLog(results.get(0));
+
+ // Validate correct binding
+ String subject = fromSubject(log);
+ assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject));
+ assertEquals("Incorrect routing key in create", getName(), AbstractTestLogSubject.getSlice("rk", subject));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+ }
+ }
+
+ public void testBindingCreateOnTopicViaManagementConsole() throws IOException, JMException
+ {
+ if (isExternalBroker())
+ {
+ //Remove any previous queue declares
+ _monitor.reset();
+
+ createQueue();
+
+ ManagedExchange managedExchange = MBeanServerInvocationHandler.
+ newProxyInstance(_mbsc, getExchange("amq.topic"),
+ ManagedExchange.class, false);
+
+ managedExchange.createNewBinding(getName(), getName());
+
+ List<String> results = _monitor.findMatches("BND-1001");
+
+ assertEquals("More than one bind creation found", 1, results.size());
+
+ String log = getLog(results.get(0));
+
+ // Validate correct binding
+ String subject = fromSubject(log);
+ assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject));
+ assertEquals("Incorrect routing key in create", getName(), AbstractTestLogSubject.getSlice("rk", subject));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+ }
+ }
+
+ public void testBindingCreateOnFanoutViaManagementConsole() throws IOException, JMException
+ {
+ if (isExternalBroker())
+ {
+ //Remove any previous queue declares
+ _monitor.reset();
+
+ createQueue();
+
+ ManagedExchange managedExchange = MBeanServerInvocationHandler.
+ newProxyInstance(_mbsc, getExchange("amq.fanout"),
+ ManagedExchange.class, false);
+
+ managedExchange.createNewBinding(getName(), getName());
+
+ List<String> results = _monitor.findMatches("BND-1001");
+
+ assertEquals("More than one bind creation found", 1, results.size());
+
+ String log = getLog(results.get(0));
+
+ // Validate correct binding
+ String subject = fromSubject(log);
+ assertEquals("Incorrect queue named in create", getName(), AbstractTestLogSubject.getSlice("qu", subject));
+ assertEquals("Incorrect routing key in create", "*", AbstractTestLogSubject.getSlice("rk", subject));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+ }
+ }
+
+ /**
+ * Description:
+ * Bindings can be deleted so that a queue can be rebound with a different set of values. This can be performed via the Management Console
+ * Input:
+ *
+ * 1. Running Broker
+ * 2. Management Console connected
+ * 3. Management Console is used to perform unbind.
+ * Output:
+ *
+ * <date> BND-1002 : Deleted
+ *
+ * Validation Steps:
+ * 4. The BND ID is correct
+ * 5. There must have been a BND-1001 Create message first.
+ * 6. This will be the last message for the given binding
+ *
+ * @throws java.io.IOException - if there is a problem reseting the log monitor or an issue with the JMX Connection
+ * @throws javax.management.JMException - {@see #createExchange and ManagedBroker.unregisterExchange}
+ */
+ public void testUnRegisterExchangeViaManagementConsole() throws IOException, JMException
+ {
+ if (isExternalBroker())
+ {
+
+ //Remove any previous queue declares
+ _monitor.reset();
+
+ createExchange("direct");
+
+ ManagedBroker managedBroker = MBeanServerInvocationHandler.
+ newProxyInstance(_mbsc, getVirtualHostManagerObjectName(),
+ ManagedBroker.class, false);
+
+ managedBroker.unregisterExchange(getName());
+
+ List<String> results = _monitor.findMatches("EXH-1002");
+
+ assertEquals("More than one exchange deletion found", 1, results.size());
+
+ String log = getLog(results.get(0));
+
+ // Validate correct binding
+ String subject = fromSubject(log);
+ assertEquals("Incorrect exchange named in delete", "direct/" + getName(), AbstractTestLogSubject.getSlice("ex", subject));
+
+ // Validate it was a management actor.
+ String actor = fromActor(log);
+ assertTrue("Actor is not a manangement actor:" + actor, actor.startsWith("mng"));
+ }
+ }
+
+ /**
+ * Create a non-durable test exchange with the current test name
+ *
+ * @throws JMException - is thrown if a exchange with this testName already exists
+ * @throws IOException - if there is a problem with the JMX Connection
+ * @throws javax.management.MBeanException
+ * - if there is another problem creating the exchange
+ */
+ private void createExchange(String type)
+ throws JMException, IOException, MBeanException
+ {
+ ManagedBroker managedBroker = MBeanServerInvocationHandler.
+ newProxyInstance(_mbsc, getVirtualHostManagerObjectName(),
+ ManagedBroker.class, false);
+
+ managedBroker.createNewExchange(getName(), type, false);
+ }
+
+ /**
+ * Create a non-durable queue (with no owner) that is named after the
+ * creating test.
+ *
+ * @throws JMException - is thrown if a queue with this testName already exists
+ * @throws IOException - if there is a problem with the JMX Connection
+ */
+ private void createQueue()
+ throws JMException, IOException
+ {
+ ManagedBroker managedBroker = MBeanServerInvocationHandler.
+ newProxyInstance(_mbsc, getVirtualHostManagerObjectName(),
+ ManagedBroker.class, false);
+
+ managedBroker.createNewQueue(getName(), null, false);
+ }
+
+ /**
+ * Retrive the ObjectName for the test Virtualhost.
+ *
+ * This is then use to create aproxy to the ManagedBroker MBean.
+ *
+ * @return the ObjectName for the 'test' VirtualHost.
+ */
+ private ObjectName getVirtualHostManagerObjectName()
+ {
+ // Get the name of the test manager
+ AllObjects allObject = new AllObjects(_mbsc);
+ allObject.querystring = "org.apache.qpid:type=VirtualHost.VirtualHostManager,VirtualHost=test,*";
+
+ Set<ObjectName> objectNames = allObject.returnObjects();
+
+ assertEquals("Incorrect number test vhosts returned", 1, objectNames.size());
+
+ // We have verified we have only one value in objectNames so return it
+ return objectNames.iterator().next();
+ }
+
+ /**
+ * Retrive the ObjectName for the given Exchange on the test Virtualhost.
+ *
+ * This is then use to create aproxy to the ManagedExchange MBean.
+ *
+ * @param exchange The exchange to retireve e.g. 'direct'
+ *
+ * @return the ObjectName for the given exchange on the test VirtualHost.
+ */
+ private ObjectName getExchange(String exchange)
+ {
+ // Get the name of the test manager
+ AllObjects allObject = new AllObjects(_mbsc);
+ allObject.querystring = "org.apache.qpid:type=VirtualHost.Exchange,VirtualHost=test,name=" + exchange + ",*";
+
+ Set<ObjectName> objectNames = allObject.returnObjects();
+
+ assertEquals("Incorrect number of exchange with name '" + exchange +
+ "' returned", 1, objectNames.size());
+
+ // We have verified we have only one value in objectNames so return it
+ return objectNames.iterator().next();
+ }
+
+}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BrokerLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BrokerLoggingTest.java
index d417349c7f..4f50aba61d 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BrokerLoggingTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/BrokerLoggingTest.java
@@ -149,11 +149,19 @@ public class BrokerLoggingTest extends AbstractTestLogging
{
String TESTID = "BRK-1007";
-// _monitor = new LogMonitor(new File(System.getProperty("QPID_WORK") + "/log/qpid.log"));
-
//Remove test Log4j config from the commandline
_broker = _broker.substring(0, _broker.indexOf("-l"));
+ // As a result of removing the test log4j config
+ // we will pick up the broker default and will write
+ // data to the standard qpid.log file. Which means that the start
+ // broker process will not be monitoring the right file for startup
+ // messages. Therefore:
+
+ // Set the broker.ready string to check for the _log4j default that
+ // is still present on standard out.
+ System.setProperty(BROKER_READY, "Qpid Broker Ready");
+
startBroker();
// Now we can create the monitor as _outputFile will now be defined
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ChannelLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ChannelLoggingTest.java
index da3f458cef..ea0199570c 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ChannelLoggingTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ChannelLoggingTest.java
@@ -20,6 +20,8 @@
*/
package org.apache.qpid.server.logging;
+import org.apache.qpid.client.AMQConnection;
+
import javax.jms.Connection;
import javax.jms.MessageConsumer;
import javax.jms.Queue;
@@ -52,11 +54,12 @@ public class ChannelLoggingTest extends AbstractTestLogging
* 2. New JMS Session/Channel creation
*
* Output:
- * <date> CHN-1001 : Create : Prefetch <count>
+ * <date> CHN-1001 : Create
+ * <date> CHN-1004 : Prefetch Size (bytes) {0,number} : Count {1,number}
*
* Validation Steps:
- * 3. The CHN ID is correct
- * 4. The prefetch value matches that defined by the requesting client.
+ * 1. The CHN ID is correct
+ * 2. The prefetch value matches that defined by the requesting client.
*
* @throws Exception - if an error occurs
*/
@@ -66,14 +69,16 @@ public class ChannelLoggingTest extends AbstractTestLogging
Connection connection = getConnection();
+ int PREFETCH = 12;
+
// Test that calling session.close gives us the expected output
- connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ ((AMQConnection)connection).createSession(false, Session.AUTO_ACKNOWLEDGE,PREFETCH);
List<String> results = _monitor.findMatches(CHANNEL_PREFIX);
// Validation
- assertEquals("CHN messages not logged", 1, results.size());
+ assertEquals("CHN messages not logged", 2, results.size());
String log = getLog(results.get(0));
// MESSAGE [con:0(guest@anonymous(3273383)/test)/ch:1] CHN-1001 : Create
@@ -81,6 +86,13 @@ public class ChannelLoggingTest extends AbstractTestLogging
validateMessageID("CHN-1001", log);
assertEquals("Incorrect Channel in actor:"+fromActor(log), 1, getChannelID(fromActor(log)));
+ log = getLog(results.get(1));
+ // MESSAGE [con:0(guest@anonymous(3273383)/test)/ch:1] CHN-1004 : Prefetch Size (bytes) {0,number} : Count {1,number}
+ //1 & 2
+ validateMessageID("CHN-1004", log);
+ assertEquals("Incorrect Channel in actor:"+fromActor(log), 1, getChannelID(fromActor(log)));
+ assertTrue("Prefetch Count not correct",getMessageString(fromMessage(log)).endsWith("Count "+PREFETCH));
+
connection.close();
}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DerbyMessageStoreLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DerbyMessageStoreLoggingTest.java
index c4e33ade30..254ec9693d 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DerbyMessageStoreLoggingTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DerbyMessageStoreLoggingTest.java
@@ -28,8 +28,6 @@ import javax.jms.Connection;
import javax.jms.Queue;
import javax.jms.Session;
import java.util.List;
-import java.util.LinkedList;
-import java.util.Iterator;
/**
* The MessageStore test suite validates that the follow log messages as
@@ -371,7 +369,6 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest
// exclude them here.
results = filterResultsByVirtualHost(results, "/localhost");
-
assertEquals("Recovered test queue not found.", 1, results.size());
String result = getLog(results.get(0));
@@ -402,6 +399,44 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest
/**
* Description:
+ * A persistent queue must be persisted so that on recovery it can be restored independently of any messages that may be stored on it. This test verifies that the MessageStore will log that it has recovered 0 messages for persistent queues that do not have any messages.
+ * Input:
+ *
+ * 1. Default persistent configuration
+ * 2. Persistent queue with no messages enqueued
+ * Output:
+ *
+ * <date> MST-1005 : Recovered 0 messages for queue <queue.name>
+ *
+ * Validation Steps:
+ * 3. The MST ID is correct
+ * 4. This must occur after the queue recovery start MST-1004 has been logged.
+ * 5. The count is 0
+ * 6. 'messages' is correctly printed
+ * 7. The queue.name is non-empty
+ */
+ public void testMessageStoreQueueRecoveryCountEmpty() throws Exception
+ {
+ assertLoggingNotYetOccured(MESSAGES_STORE_PREFIX);
+
+ String queueName = getTestQueueName();
+
+ startBroker();
+ Connection connetion = getConnection();
+ Session session = connetion.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ Queue queue = session.createQueue("direct://amq.direct/" + queueName + "/" + queueName + "?durable='true'");
+
+ session.createConsumer(queue).close();
+
+ // Stop the broker so that we can test recovery
+ stopBroker();
+
+ int COUNT = 0;
+ testDurableRecoveryCount(COUNT, queueName);
+ }
+
+ /**
+ * Description:
* On recovery all the persistent messages that are stored on disk must be returned to the queue. MST-1005 will report the number of messages that have been recovered from disk.
* Input:
*
@@ -424,6 +459,21 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest
String queueName = getTestQueueName();
+ int COUNT = 10;
+
+ testDurableRecoveryCount(COUNT, queueName);
+ }
+
+ /**
+ * Send a set number of messages to a new durable queue, as specified. Then
+ * restart the broker and validate that they are restored.
+ *
+ * @param COUNT - the count to send
+ * @param queueName - the new queue name
+ * @throws Exception - if a problem occured.
+ */
+ private void testDurableRecoveryCount(int COUNT, String queueName) throws Exception
+ {
startBroker();
Connection connetion = getConnection();
Session session = connetion.createSession(false, Session.AUTO_ACKNOWLEDGE);
@@ -431,8 +481,6 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest
session.createConsumer(queue).close();
- int COUNT = 10;
-
sendMessage(session, queue, COUNT);
try
{
@@ -469,16 +517,23 @@ public class DerbyMessageStoreLoggingTest extends MemoryMessageStoreLoggingTest
results = _monitor.findMatches("MST-1005");
- assertEquals("Recovered test queue not found.", 2, results.size());
+ assertTrue("Insufficient MST-1005 logged.", results.size()>0);
- result = getLog(results.get(0));
+ result = null;
// If the first message is not our queue the second one will be
- if (!result.contains(queueName))
+ for(String resultEntry : results)
{
- result = getLog(results.get(1));
+ // Look for first match and set that to result
+ if (resultEntry.contains(queueName))
+ {
+ result = getLog(resultEntry);
+ break;
+ }
}
+ assertNotNull("MST-1005 entry for queue:" + queueName + ". Not found", result);
+
// getSlize will return extract the vhost from vh(/test) -> '/test'
// so remove the '/' to get the name
String vhostName = AbstractTestLogSubject.getSlice("vh", result).substring(1);
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DurableQueueLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DurableQueueLoggingTest.java
index 5ba7dffcdc..287a3fe412 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DurableQueueLoggingTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/DurableQueueLoggingTest.java
@@ -162,8 +162,8 @@ public class DurableQueueLoggingTest extends AbstractTestLogging
assertEquals(TRANSIENT + " keyword not correct in log entry.",
!_durable, fromMessage(log).contains(TRANSIENT));
- assertTrue("Queue does not have correct owner value:" + fromMessage(log),
- fromMessage(log).contains("Owner: null"));
+ assertFalse("Queue should not contain Owner tag:" + fromMessage(log),
+ fromMessage(log).contains("Owner"));
}
/**
@@ -217,8 +217,8 @@ public class DurableQueueLoggingTest extends AbstractTestLogging
assertTrue("Queue does not have the AutoDelete keyword in log:" + fromMessage(log),
fromMessage(log).contains("AutoDelete"));
- assertTrue("Queue does not have correct owner value:" + fromMessage(log),
- fromMessage(log).contains("Owner: null"));
+ assertFalse("Queue should not contain Owner tag:" + fromMessage(log),
+ fromMessage(log).contains("Owner"));
}
/**
@@ -277,8 +277,8 @@ public class DurableQueueLoggingTest extends AbstractTestLogging
assertTrue("Queue does not have the right Priority value keyword in log:" + fromMessage(log),
fromMessage(log).contains("Priority: " + PRIORITIES));
- assertTrue("Queue does not have correct owner value:" + fromMessage(log),
- fromMessage(log).contains("Owner: null"));
+ assertFalse("Queue should not contain Owner tag:" + fromMessage(log),
+ fromMessage(log).contains("Owner"));
}
/**
@@ -342,8 +342,8 @@ public class DurableQueueLoggingTest extends AbstractTestLogging
assertTrue("Queue does not have the AutoDelete keyword in log:" + fromMessage(log),
fromMessage(log).contains("AutoDelete"));
- assertTrue("Queue does not have correct owner value:" + fromMessage(log),
- fromMessage(log).contains("Owner: null"));
+ assertFalse("Queue should not contain Owner tag:" + fromMessage(log),
+ fromMessage(log).contains("Owner"));
}
}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ManagementLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ManagementLoggingTest.java
index 2eeedf2dfe..11c003a2a7 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ManagementLoggingTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/ManagementLoggingTest.java
@@ -259,57 +259,6 @@ public class ManagementLoggingTest extends AbstractTestLogging
}
}
}
-
- /**
- * Description:
- * Using the default configuration validate that the RMI ConnectorServer socket is correctly reported as being opened
- * Input:
- * The default configuration file
- * Output:
- *
- * <date> MESSAGE MNG-1002 : Starting : RMI ConnectorServer : Listening on port 9099
- *
- * Constraints:
- * The RMI ConnectorServer and Registry log messages do not have a prescribed order
- * Validation Steps:
- *
- * 1. The MNG ID is correct
- * 2. The specified port is the correct '9099'
- */
- public void testManagementStartupRMIConnectorServer() throws Exception
- {
- // This test only works on external java brokers due to the fact that
- // Management is disabled on InVM brokers.
- if (isJavaBroker() && isExternalBroker())
- {
- //Ensure management is on
- setConfigurationProperty("management.enabled", "true");
-
- startBroker();
-
- // Now we can create the monitor as _outputFile will now be defined
- _monitor = new LogMonitor(_outputFile);
-
- List<String> results = _monitor.findMatches(MNG_PREFIX);
- try
- {
- // Validation
-
- assertTrue("MNGer message not logged", results.size() > 0);
-
- }
- catch (AssertionFailedError afe)
- {
- System.err.println("Log Dump:");
- for (String log : results)
- {
- System.err.println(log);
- }
- throw afe;
- }
- }
- }
-
/**
* Description:
* Using the default configuration with SSL enabled for the management port the SSL Keystore path should be reported via MNG-1006
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/QueueLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/QueueLoggingTest.java
index e407f2b626..150f462d0f 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/QueueLoggingTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/QueueLoggingTest.java
@@ -20,7 +20,6 @@
*/
package org.apache.qpid.server.logging;
-import org.omg.CORBA.TRANSIENT;
import org.apache.qpid.client.AMQSession;
import org.apache.qpid.client.failover.FailoverException;
import org.apache.qpid.server.logging.subjects.AbstractTestLogSubject;
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java
index 11e345de5e..d97ed71607 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/SubscriptionLoggingTest.java
@@ -20,8 +20,12 @@
*/
package org.apache.qpid.server.logging;
+import junit.framework.AssertionFailedError;
+import org.apache.qpid.client.AMQConnection;
+
import javax.jms.Connection;
import javax.jms.JMSException;
+import javax.jms.MessageConsumer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.Topic;
@@ -37,6 +41,7 @@ import java.util.List;
*
* SUB-1001 : Create : [Durable] [Arguments : <key=value>]
* SUB-1002 : Close
+ * SUB-1003 : State : <state>
*/
public class SubscriptionLoggingTest extends AbstractTestLogging
{
@@ -69,7 +74,7 @@ public class SubscriptionLoggingTest extends AbstractTestLogging
*
* 1. Running Broker
* 2. Create a new Subscription to a transient queue/topic.
- * Output:
+ * Output: 6
*
* <date> SUB-1001 : Create
*
@@ -170,7 +175,7 @@ public class SubscriptionLoggingTest extends AbstractTestLogging
assertTrue("AutoClose not on log message:" + message, message.contains("AutoClose"));
// Beacause it is an auto close and we have no messages on the queue we
- // will get a close message
+ // will get a close message
log = getLog(results.get(1));
validateMessageID("SUB-1002", log);
@@ -276,8 +281,6 @@ public class SubscriptionLoggingTest extends AbstractTestLogging
{
_session.createConsumer(_queue).close();
-
-
//Validate
List<String> results = _monitor.findMatches(SUB_PREFIX);
@@ -296,4 +299,128 @@ public class SubscriptionLoggingTest extends AbstractTestLogging
}
+ /**
+ * Description:
+ * When a Subscription fills its prefetch it will become suspended. This
+ * will be logged as a SUB-1003 message.
+ * Input:
+ *
+ * 1. Running broker
+ * 2. Message Producer to put more data on the queue than the client's prefetch
+ * 3. Client that ensures that its prefetch becomes full
+ * Output:
+ *
+ * <date> SUB-1003 : State : <state>
+ *
+ * Validation Steps:
+ * 1. The SUB ID is correct
+ * 2. The state is correct
+ *
+ * @throws java.io.IOException - if there is a problem getting the matches
+ * @throws javax.jms.JMSException - if there is a problem creating the consumer
+ */
+ public void testSubscriptionSuspend() throws Exception, IOException
+ {
+ //Close session with large prefetch
+ _connection.createSession(false, Session.AUTO_ACKNOWLEDGE).close();
+
+ int PREFETCH = 15;
+
+ //Create new session with small prefetch
+ _session = ((AMQConnection) _connection).createSession(false, Session.AUTO_ACKNOWLEDGE, PREFETCH);
+
+ MessageConsumer consumer = _session.createConsumer(_queue);
+
+ _connection.start();
+
+ //Fill the prefetch and two extra so that our receive bellow allows the
+ // subscription to become active then return to a suspended state.
+ sendMessage(_session, _queue, 17);
+
+ // Retreive the first message, and start the flow of messages
+ assertNotNull("First message not retreived", consumer.receive(1000));
+
+ //Give the internal broker time to respond to the ack that the above
+ // receive will perform.
+ if (!isExternalBroker())
+ {
+ Thread.sleep(1000);
+ }
+
+ _connection.close();
+
+ //Validate
+ List<String> results = _monitor.findMatches("SUB-1003");
+
+ try
+ {
+ // Validation expects three messages.
+ // The first will be logged by the QueueActor as part of the processQueue thread
+// INFO - MESSAGE [vh(/test)/qu(example.queue)] [sub:6(qu(example.queue))] SUB-1003 : State : SUSPENDED
+ // The second will be by the connnection as it acknowledges and activates the subscription
+// INFO - MESSAGE [con:6(guest@anonymous(26562441)/test)/ch:3] [sub:6(qu(example.queue))] SUB-1003 : State : ACTIVE
+ // The final one can be the subscription suspending as part of the SubFlushRunner or the processQueue thread
+ // As a result validating the actor is more complicated and doesn't add anything. The goal of this test is
+ // to ensure the State is correct not that a particular Actor performs the logging.
+// INFO - MESSAGE [sub:6(vh(test)/qu(example.queue))] [sub:6(qu(example.queue))] SUB-1003 : State : SUSPENDED
+// INFO - MESSAGE [vh(/test)/qu(example.queue)] [sub:6(qu(example.queue))] SUB-1003 : State : SUSPENDED
+
+ assertEquals("Result set larger than expected.", 3, results.size());
+
+ // Validate Initial Suspension
+ String expectedState = "SUSPENDED";
+ String log = getLog(results.get(0));
+ validateSubscriptionState(log, expectedState);
+
+ // Validate that the logActor is the the queue
+ String actor = fromActor(log);
+ assertTrue("Actor string does not contain expected queue("
+ + _queue.getQueueName() + ") name." + actor,
+ actor.contains("qu(" + _queue.getQueueName() + ")"));
+
+ // After being suspended the subscription should become active.
+ expectedState = "ACTIVE";
+ log = getLog(results.get(1));
+ validateSubscriptionState(log, expectedState);
+ // Validate we have a connection Actor
+ actor = fromActor(log);
+ assertTrue("The actor is not a connection actor:" + actor, actor.startsWith("con:"));
+
+ // Validate that it was re-suspended
+ expectedState = "SUSPENDED";
+ log = getLog(results.get(2));
+ validateSubscriptionState(log, expectedState);
+ // We only need validate the state.
+ }
+ catch (AssertionFailedError afe)
+ {
+ System.err.println("Log Dump:");
+ for (String log : results)
+ {
+ System.err.println(log);
+ }
+ throw afe;
+ }
+
+ }
+
+ /**
+ * Validate that the given log statement is a well formatted SUB-1003
+ * message. That means the ID and expected state are correct.
+ *
+ * @param log the log to test
+ * @param expectedState the state that should be logged.
+ */
+ private void validateSubscriptionState(String log, String expectedState)
+ {
+ validateMessageID("SUB-1003", log);
+ String logMessage = getMessageString(fromMessage(log));
+ assertTrue("Log Message does not start with 'State'" + logMessage,
+ logMessage.startsWith("State"));
+
+ assertTrue("Log Message does not have expected State of '"
+ + expectedState + "'" + logMessage,
+ logMessage.endsWith(expectedState));
+ }
+
}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/VirtualHostLoggingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/VirtualHostLoggingTest.java
new file mode 100644
index 0000000000..7bf644508e
--- /dev/null
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/logging/VirtualHostLoggingTest.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *
+ */
+
+package org.apache.qpid.server.logging;
+
+import junit.framework.AssertionFailedError;
+import org.apache.commons.configuration.Configuration;
+import org.apache.qpid.server.configuration.ServerConfiguration;
+
+import java.util.List;
+
+/**
+ * Virtualhost Test Cases
+ * The virtualhost test suite validates that the follow log messages as specified in the Functional Specification.
+ * <p/>
+ * This suite of tests validate that the management console messages occur correctly and according to the following format:
+ * <p/>
+ * VHT-1001 : Created : <name>
+ * VHT-1002 : Work directory : <path>
+ * VHT-1003 : Closed
+ */
+public class VirtualHostLoggingTest extends AbstractTestLogging
+{
+ private static final String VHT_PREFIX = "VHT-";
+
+ /**
+ * Description:
+ * Testing can be performed using the default configuration. The goal is to validate that for each virtualhost defined in the configuration file a VHT-1001 Created message is provided.
+ * Input:
+ * The default configuration file
+ * Output:
+ * <p/>
+ * <date> VHT-1001 : Created : <name>
+ * Validation Steps:
+ * <p/>
+ * The VHT ID is correct
+ * A VHT-1001 is printed for each virtualhost defined in the configuration file.
+ * This must be the first message for the specified virtualhost.
+ *
+ * @throws Exception caused by broker startup
+ */
+ public void testVirtualhostCreation() throws Exception
+ {
+
+ List<String> results = _monitor.findMatches(VHT_PREFIX);
+ try
+ {
+ // Validation
+ Configuration configuration = ServerConfiguration.flatConfig(_configFile);
+ List<String> vhosts = configuration.getList("virtualhosts.virtualhost.name");
+
+ //Validate each vhost logs a creation
+ results = _monitor.findMatches("VHT-1001");
+
+ assertEquals("Each vhost did not create a store.", vhosts.size(), results.size());
+
+ for (int index = 0; index < results.size(); index++)
+ {
+ String result = getLog(results.get(index));
+
+ // Retrieve the vhostname from the log entry message 'Created : <vhostname>'
+ String vhostName = getMessageString(fromMessage(result)).split(" ")[2];
+
+ assertTrue("Virualhost named in log not found in config file:" + vhostName + ":" + vhosts, vhosts.contains(vhostName));
+ }
+ }
+ catch (AssertionFailedError afe)
+ {
+ System.err.println("Log Dump:");
+ for (String log : results)
+ {
+ System.err.println(log);
+ }
+ throw afe;
+ }
+ }
+
+ /**
+ * Description:
+ * Testing can be performed using the default configuration. During broker shutdown a VHT-1002 Closed message will be printed for each of the configured virtualhosts. For every virtualhost that was started a close must be logged. After the close message has been printed no further logging will be performed by this virtualhost.
+ * Input:
+ * The default configuration file
+ * Output:
+ * <p/>
+ * <date> VHT-1002 : Closed
+ * Validation Steps:
+ * <p/>
+ * The VHT ID is correct
+ * This is the last VHT message for the given virtualhost.
+ *
+ * @throws Exception caused by broker startup
+ */
+ public void testVirtualhostClosure() throws Exception
+ {
+ stopBroker();
+
+ List<String> results = _monitor.findMatches(VHT_PREFIX);
+ try
+ {
+ // Validation
+
+ Configuration configuration = ServerConfiguration.flatConfig(_configFile);
+ List<String> vhosts = configuration.getList("virtualhosts.virtualhost.name");
+
+ //Validate each vhost logs a creation
+ results = _monitor.findMatches("VHT-1002");
+
+ assertEquals("Each vhost did not create a store.", vhosts.size(), results.size());
+ }
+ catch (AssertionFailedError afe)
+ {
+ System.err.println("Log Dump:");
+ for (String log : results)
+ {
+ System.err.println(log);
+ }
+ throw afe;
+ }
+ }
+
+}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/server/store/PersistentStoreTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/server/store/PersistentStoreTest.java
index cd5a48d0af..ac07372c68 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/server/store/PersistentStoreTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/server/store/PersistentStoreTest.java
@@ -45,7 +45,7 @@ public class PersistentStoreTest extends QpidTestCase
_con = getConnection();
_con.start();
_session = _con.createSession(true, Session.AUTO_ACKNOWLEDGE);
- _destination = _session.createQueue(this.getClass().getName());
+ _destination = _session.createQueue(getTestQueueName());
_consumer = _session.createConsumer(_destination);
_consumer.close();
@@ -70,16 +70,16 @@ public class PersistentStoreTest extends QpidTestCase
assertNull("No more messages should be received", _consumer.receive(100));
}
- /**
- * starts the server, sends 100 messages, restarts the server and gets 100 messages back
- * the test formerly referred to as BDB-Qpid-1
- * @throws Exception
- */
- public void testStartStop() throws Exception
- {
- restartBroker();
- checkMessages();
- }
+// /**
+// * starts the server, sends 100 messages, restarts the server and gets 100 messages back
+// * the test formerly referred to as BDB-Qpid-1
+// * @throws Exception
+// */
+// public void testStartStop() throws Exception
+// {
+// restartBroker(); -- Not Currently a gracefull restart so not BDB-Qpid-1
+// checkMessages();
+// }
/**
@@ -89,24 +89,23 @@ public class PersistentStoreTest extends QpidTestCase
*/
public void testForcibleStartStop() throws Exception
{
- nukeBroker();
- startBroker();
- checkMessages();
- }
-
- /**
- * starts the server, sends 100 committed messages, 5 uncommited ones,
- * restarts the server and gets 100 messages back
- * the test formerly referred to as BDB-Qpid-5
- * @throws Exception
- */
- public void testStartStopMidTransaction() throws Exception
- {
- sendMessage(_session, _destination, 5);
restartBroker();
checkMessages();
}
+// /**
+// * starts the server, sends 100 committed messages, 5 uncommited ones,
+// * restarts the server and gets 100 messages back
+// * the test formerly referred to as BDB-Qpid-5
+// * @throws Exception
+// */
+// public void testStartStopMidTransaction() throws Exception
+// {
+// sendMessage(_session, _destination, 5);
+// restartBroker(); -- Not Currently a gracefull restart so not BDB-Qpid-1
+// checkMessages();
+// }
+
/**
* starts the server, sends 100 committed messages, 5 uncommited ones,
* nukes and starts the server and gets 100 messages back
@@ -116,8 +115,7 @@ public class PersistentStoreTest extends QpidTestCase
public void testForcibleStartStopMidTransaction() throws Exception
{
sendMessage(_session, _destination, 5);
- nukeBroker();
- startBroker();
+ restartBroker();
checkMessages();
}
@@ -136,14 +134,14 @@ public class PersistentStoreTest extends QpidTestCase
checkMessages();
}
- /**
- * starts the server, sends 50 committed messages, copies $QPID_WORK to a new location,
- * sends 10 messages, stops the server, nukes the store, restores the copy, starts the server
- * checks that we get the first 50 back.
- */
- public void testHotBackup()
- {
-
- }
+// /**
+// * starts the server, sends 50 committed messages, copies $QPID_WORK to a new location,
+// * sends 10 messages, stops the server, nukes the store, restores the copy, starts the server
+// * checks that we get the first 50 back.
+// */
+// public void testHotBackup()
+// {
+// -- removing as this will leave 100msgs on a queue
+// }
}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/FlowControlTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/FlowControlTest.java
index 91ed9766f6..95808e454f 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/FlowControlTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/FlowControlTest.java
@@ -196,13 +196,17 @@ public class FlowControlTest extends QpidTestCase
{
System.err.println("Test Run:" + ++run);
Thread.sleep(1000);
-
- test.startBroker();
- test.testBasicBytesFlowControl();
-
- Thread.sleep(1000);
-
- test.stopBroker();
+ try
+ {
+ test.startBroker();
+ test.testBasicBytesFlowControl();
+
+ Thread.sleep(1000);
+ }
+ finally
+ {
+ test.stopBroker();
+ }
}
}
}
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java
index cd8071527c..e7218d6975 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/QpidTestCase.java
@@ -145,7 +145,7 @@ public class QpidTestCase extends TestCase
private static final String BROKER = "broker";
private static final String BROKER_CLEAN = "broker.clean";
private static final String BROKER_VERSION = "broker.version";
- private static final String BROKER_READY = "broker.ready";
+ protected static final String BROKER_READY = "broker.ready";
private static final String BROKER_STOPPED = "broker.stopped";
private static final String TEST_OUTPUT = "test.output";
@@ -362,6 +362,16 @@ public class QpidTestCase extends TestCase
}
/**
+ * Return the management portin use by the broker on this main port
+ * @param mainPort the broker's main port.
+ * @return the management port that corresponds to the broker on the given port
+ */
+ protected int getManagementPort(int mainPort)
+ {
+ return mainPort + (DEFAULT_MANAGEMENT_PORT - DEFAULT_PORT);
+ }
+
+ /**
* Get the Port that is use by the current broker
*
* @return the current port
@@ -392,7 +402,7 @@ public class QpidTestCase extends TestCase
return _broker
.replace("@PORT", "" + port)
.replace("@SSL_PORT", "" + (port - 1))
- .replace("@MPORT", "" + (port + (DEFAULT_MANAGEMENT_PORT - DEFAULT_PORT)))
+ .replace("@MPORT", "" + getManagementPort(port))
.replace("@CONFIG_FILE", _configFile.toString());
}
@@ -511,34 +521,6 @@ public class QpidTestCase extends TestCase
}
}
- public void nukeBroker() throws Exception
- {
- nukeBroker(0);
- }
-
- public void nukeBroker(int port) throws Exception
- {
- Process proc = _brokers.get(getPort(port));
- if (proc == null)
- {
- stopBroker(port);
- }
- else
- {
- String command = "pkill -KILL -f " + getBrokerCommand(getPort(port));
- try
- {
- Runtime.getRuntime().exec(command);
- }
- catch (Exception e)
- {
- // Can't do that, try the old fashioned way
- _logger.warn("Could not run " + command + ", killing with stopBroker()");
- stopBroker(port);
- }
- }
- }
-
/**
* Attempt to set the Java Broker to use the BDBMessageStore for persistence
* Falling back to the DerbyMessageStore if
diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitorTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitorTest.java
index f4dade5660..d1a0df30a9 100644
--- a/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitorTest.java
+++ b/qpid/java/systests/src/main/java/org/apache/qpid/util/LogMonitorTest.java
@@ -212,9 +212,7 @@ public class LogMonitorTest extends TestCase
monitor.reset();
- validateLogContainsMessage(monitor, LOG_RESET_TEXT);
-
- assertEquals(LOG_RESET_TEXT + "\n", monitor.readFile());
+ assertEquals("", monitor.readFile());
}
public void testRead() throws IOException
diff --git a/qpid/java/test-profiles/010Excludes b/qpid/java/test-profiles/010Excludes
index 643a26bb9d..7577ad8da1 100644
--- a/qpid/java/test-profiles/010Excludes
+++ b/qpid/java/test-profiles/010Excludes
@@ -79,3 +79,6 @@ org.apache.qpid.server.security.acl.SimpleACLTest#testClientPublishInvalidQueueS
// CPP Broker does not follow the same Logging convention as the Java broker
org.apache.qpid.server.logging.*
+
+// CPP Broker does not have a JMX interface to test
+org.apache.qpid.management.jmx.*
diff --git a/qpid/java/test-profiles/Excludes b/qpid/java/test-profiles/Excludes
index b4b3fb7cb9..7ef2a15e51 100644
--- a/qpid/java/test-profiles/Excludes
+++ b/qpid/java/test-profiles/Excludes
@@ -11,6 +11,7 @@ org.apache.qpid.server.queue.QueueCreateTest#testCreateFlowToDiskInvalidSize
org.apache.qpid.server.logging.BrokerLoggingTest#testBrokerShutdownListeningTCPDefault
org.apache.qpid.server.logging.BrokerLoggingTest#testBrokerShutdownListeningTCPSSL
org.apache.qpid.server.logging.BrokerLoggingTest#testBrokerShutdownStopped
+org.apache.qpid.server.logging.VirtualHostLoggingTest#testVirtualhostClosure
org.apache.qpid.server.logging.MemoryMessageStoreLoggingTest#testMessageStoreClose
org.apache.qpid.server.logging.DerbyMessageStoreLoggingTest#testMessageStoreClose
diff --git a/qpid/java/test-profiles/java-derby.testprofile b/qpid/java/test-profiles/java-derby.testprofile
index 0fa6a73a32..e7c1dc1fd6 100644
--- a/qpid/java/test-profiles/java-derby.testprofile
+++ b/qpid/java/test-profiles/java-derby.testprofile
@@ -1,7 +1,7 @@
broker.language=java
broker=${project.root}/build/bin/qpid-server -p @PORT -m @MPORT -c @CONFIG_FILE -l ${test.profiles}/log4j-test.xml
broker.clean=${test.profiles}/clean-dir ${build.data} ${project.root}/build/work/derbyDB
-broker.ready=Ready
+broker.ready=BRK-1004
broker.stopped=Exception
broker.config=${project.root}/build/etc/config-systests-derby.xml
diff --git a/qpid/java/test-profiles/java.testprofile b/qpid/java/test-profiles/java.testprofile
index 52f1013cf1..05a801ee06 100644
--- a/qpid/java/test-profiles/java.testprofile
+++ b/qpid/java/test-profiles/java.testprofile
@@ -1,7 +1,7 @@
broker.language=java
broker=${project.root}/build/bin/qpid-server -p @PORT -m @MPORT -c @CONFIG_FILE -l ${test.profiles}/log4j-test.xml
broker.clean=${test.profiles}/clean-dir ${build.data} ${project.root}/build/work/derbyDB
-broker.ready=Ready
+broker.ready=BRK-1004
broker.stopped=Exception
profile.excludes=08TransientExcludes 08StandaloneExcludes
diff --git a/qpid/python/README.txt b/qpid/python/README.txt
index bae9f6ab0b..772271cffe 100644
--- a/qpid/python/README.txt
+++ b/qpid/python/README.txt
@@ -27,30 +27,24 @@ python client. The "tests_0-10", "tests_0-9", and "tests_0-8"
directories contain protocol level conformance tests for AMQP brokers
of the specified version.
-Simplest way to run the tests:
+The qpid-python-test script may be used to run these tests. It will by
+default run the python unit tests and the 0-10 conformance tests:
1. Run a broker on the default port
- 2. ./run-tests -s <version>
+ 2. ./qpid-python-test
-Where <version> is one of "0-8", "0-9", or "0-10-errata".
+If you wish to run the 0-8 or 0-9 conformence tests, they may be
+selected as follows:
-See the run-tests usage for for additional options:
-
- ./run-tests -h
+ 1. Run a broker on the default port
-== Expected failures ==
+ 2. ./qpid-python-test tests_0-8.*
-Certain tests are expected to fail due to incomplete functionality or
-unresolved interop issues. To skip expected failures for the C++ or
-Java brokers:
+ -- or --
- ./run-tests -I <file-name>
+ ./qpid-python-test tests_0-9.*
-Where <file-name> is one of the following files:
+See the qpid-python-test usage for for additional options:
- * cpp_failing_0-10.txt
- * cpp_failing_0-9.txt
- * cpp_failing_0-8.txt
- * java_failing_0-9.txt
- * java_failing_0-8.txt
+ ./qpid-python-test -h
diff --git a/qpid/python/cpp_failing_0-10.txt b/qpid/python/cpp_failing_0-10.txt
deleted file mode 100644
index e69de29bb2..0000000000
--- a/qpid/python/cpp_failing_0-10.txt
+++ /dev/null
diff --git a/qpid/python/cpp_failing_0-8.txt b/qpid/python/cpp_failing_0-8.txt
deleted file mode 100644
index e69de29bb2..0000000000
--- a/qpid/python/cpp_failing_0-8.txt
+++ /dev/null
diff --git a/qpid/python/cpp_failing_0-9.txt b/qpid/python/cpp_failing_0-9.txt
deleted file mode 100644
index 06c31080fb..0000000000
--- a/qpid/python/cpp_failing_0-9.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-tests_0-9.message.MessageTests.test_checkpoint
-tests_0-9.message.MessageTests.test_reject
-tests_0-9.basic.BasicTests.test_get
-
diff --git a/qpid/python/java_failing_0-8.txt b/qpid/python/java_failing_0-8.txt
deleted file mode 100644
index c13b40a42c..0000000000
--- a/qpid/python/java_failing_0-8.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-tests_0-8.exchange.RecommendedTypesRuleTests.testTopic
-tests_0-8.exchange.RequiredInstancesRuleTests.testAmqTopic
diff --git a/qpid/python/java_failing_0-9.txt b/qpid/python/java_failing_0-9.txt
deleted file mode 100644
index 7252d0f496..0000000000
--- a/qpid/python/java_failing_0-9.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-ntests.basic.BasicTests.test_qos_prefetch_count
-tests.basic.BasicTests.test_ack
-tests.basic.BasicTests.test_cancel
-tests.basic.BasicTests.test_consume_exclusive
-tests.basic.BasicTests.test_consume_no_local
-tests.basic.BasicTests.test_consume_queue_errors
-tests.basic.BasicTests.test_consume_unique_consumers
-tests.basic.BasicTests.test_get
-tests.basic.BasicTests.test_qos_prefetch_size
-tests.basic.BasicTests.test_recover_requeue
-
-tests.exchange.RecommendedTypesRuleTests.testTopic
-tests.exchange.RequiredInstancesRuleTests.testAmqTopic
-
-tests.message.MessageTests.test_checkpoint
-tests.message.MessageTests.test_reject
-
-tests.broker.BrokerTests.test_ping_pong
diff --git a/qpid/python/mllib/dom.py b/qpid/python/mllib/dom.py
index df2b88322a..486f7082e1 100644
--- a/qpid/python/mllib/dom.py
+++ b/qpid/python/mllib/dom.py
@@ -148,6 +148,21 @@ class Tag(Node):
if name == k:
return v
+ def _idx(self, attr):
+ idx = 0
+ for k, v in self.attrs:
+ if k == attr:
+ return idx
+ idx += 1
+ return None
+
+ def set_attr(self, name, value):
+ idx = self._idx(name)
+ if idx is None:
+ self.attrs.append((name, value))
+ else:
+ self.attrs[idx] = (name, value)
+
def dispatch(self, f):
try:
attr = "do_" + self.name
diff --git a/qpid/python/pal2py b/qpid/python/pal2py
deleted file mode 100755
index 544151bf76..0000000000
--- a/qpid/python/pal2py
+++ /dev/null
@@ -1,274 +0,0 @@
-#!/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, os, xml
-
-from qpid.spec import load, pythonize
-from textwrap import TextWrapper
-from xml.sax.handler import ContentHandler
-
-class Block:
-
- def __init__(self, children):
- self.children = children
-
- def emit(self, out):
- for child in self.children:
- if not hasattr(child, "emit"):
- raise ValueError(child)
- child.emit(out)
-
- if not self.children:
- out.line("pass")
-
-class If:
-
- def __init__(self, expr, cons, alt = None):
- self.expr = expr
- self.cons = cons
- self.alt = alt
-
- def emit(self, out):
- out.line("if ")
- self.expr.emit(out)
- out.write(":")
- out.level += 1
- self.cons.emit(out)
- out.level -= 1
- if self.alt:
- out.line("else:")
- out.level += 1
- self.alt.emit(out)
- out.level -= 1
-
-class Stmt:
-
- def __init__(self, code):
- self.code = code
-
- def emit(self, out):
- out.line(self.code)
-
-class Expr:
-
- def __init__(self, code):
- self.code = code
-
- def emit(self, out):
- out.write(self.code)
-
-class Abort:
-
- def __init__(self, expr):
- self.expr = expr
-
- def emit(self, out):
- out.line("assert False, ")
- self.expr.emit(out)
-
-WRAPPER = TextWrapper()
-
-def wrap(text):
- return WRAPPER.wrap(" ".join(text.split()))
-
-class Doc:
-
- def __init__(self, text):
- self.text = text
-
- def emit(self, out):
- out.line('"""')
- for line in wrap(self.text):
- out.line(line)
- out.line('"""')
-
-class Frame:
-
- def __init__(self, attrs):
- self.attrs = attrs
- self.children = []
- self.text = None
-
- def __getattr__(self, attr):
- return self.attrs[attr]
-
-def isunicode(s):
- if isinstance(s, str):
- return False
- for ch in s:
- if ord(ch) > 127:
- return True
- return False
-
-def string_literal(s):
- if s == None:
- return None
- if isunicode(s):
- return "%r" % s
- else:
- return "%r" % str(s)
-
-TRUTH = {
- "1": True,
- "0": False,
- "true": True,
- "false": False
- }
-
-LITERAL = {
- "shortstr": string_literal,
- "longstr": string_literal,
- "bit": lambda s: TRUTH[s.lower()],
- "longlong": lambda s: "%r" % long(s)
- }
-
-def literal(s, field):
- return LITERAL[field.type](s)
-
-def palexpr(s, field):
- if s.startswith("$"):
- return "msg.%s" % s[1:]
- else:
- return literal(s, field)
-
-class Translator(ContentHandler):
-
- def __init__(self, spec):
- self.spec = spec
- self.stack = []
- self.content = None
- self.root = Frame(None)
- self.push(self.root)
-
- def emit(self, out):
- blk = Block(self.root.children)
- blk.emit(out)
- out.write("\n")
-
- def peek(self):
- return self.stack[-1]
-
- def pop(self):
- return self.stack.pop()
-
- def push(self, frame):
- self.stack.append(frame)
-
- def startElement(self, name, attrs):
- self.push(Frame(attrs))
-
- def endElement(self, name):
- frame = self.pop()
- if hasattr(self, name):
- child = getattr(self, name)(frame)
- else:
- child = self.handle(name, frame)
-
- if child:
- self.peek().children.append(child)
-
- def characters(self, text):
- frame = self.peek()
- if frame.text:
- frame.text += text
- else:
- frame.text = text
-
- def handle(self, name, frame):
- for klass in self.spec.classes:
- pyklass = pythonize(klass.name)
- if name.startswith(pyklass):
- name = name[len(pyklass) + 1:]
- break
- else:
- raise ValueError("unknown class: %s" % name)
-
- for method in klass.methods:
- pymethod = pythonize(method.name)
- if name == pymethod:
- break
- else:
- raise ValueError("unknown method: %s" % name)
-
- args = ["%s = %s" % (key, palexpr(val, method.fields.bypyname[key]))
- for key, val in frame.attrs.items()]
- if method.content and self.content:
- args.append("content = %r" % string_literal(self.content))
- code = "ssn.%s_%s(%s)" % (pyklass, pymethod, ", ".join(args))
- if pymethod == "consume":
- code = "consumer_tag = %s.consumer_tag" % code
- return Stmt(code)
-
- def pal(self, frame):
- return Block([Doc(frame.text)] + frame.children)
-
- def include(self, frame):
- base, ext = os.path.splitext(frame.filename)
- return Stmt("from %s import *" % base)
-
- def session(self, frame):
- return Block([Stmt("cli = open()"), Stmt("ssn = cli.channel(0)"),
- Stmt("ssn.channel_open()")] + frame.children)
-
- def empty(self, frame):
- return If(Expr("msg == None"), Block(frame.children))
-
- def abort(self, frame):
- return Abort(Expr(string_literal(frame.text)))
-
- def wait(self, frame):
- return Stmt("msg = ssn.queue(consumer_tag).get(timeout=%r)" %
- (int(frame.timeout)/1000))
-
- def basic_arrived(self, frame):
- if frame.children:
- return If(Expr("msg != None"), Block(frame.children))
-
- def basic_content(self, frame):
- self.content = frame.text
-
-class Emitter:
-
- def __init__(self, out):
- self.out = out
- self.level = 0
-
- def write(self, code):
- self.out.write(code)
-
- def line(self, code):
- self.write("\n%s%s" % (" "*self.level, code))
-
- def flush(self):
- self.out.flush()
-
- def close(self):
- self.out.close()
-
-
-for f in sys.argv[2:]:
- base, ext = os.path.splitext(f)
- spec = load(sys.argv[1])
- t = Translator(spec)
- xml.sax.parse(f, t)
-# out = Emitter(open("%s.py" % base))
- out = Emitter(sys.stdout)
- t.emit(out)
- out.close()
diff --git a/qpid/python/perftest b/qpid/python/perftest
deleted file mode 100755
index f867566fd0..0000000000
--- a/qpid/python/perftest
+++ /dev/null
@@ -1,113 +0,0 @@
-#!/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.
-#
-
-def publisher(n):
- import qpid
- import sys
- from qpid.client import Client
- from qpid.content import Content
- if len(sys.argv) >= 3:
- n = int(sys.argv[2])
- client = Client("127.0.0.1", 5672)
- client.start({"LOGIN": "guest", "PASSWORD": "guest"})
- channel = client.channel(1)
- channel.session_open()
- message = Content("message")
- message["routing_key"] = "message_queue"
- print "producing ", n, " messages"
- for i in range(n):
- channel.message_transfer(destination="amq.direct", content=message)
-
- print "producing final message"
- message = Content("That's done")
- message["routing_key"] = "message_queue"
- channel.message_transfer(destination="amq.direct", content=message)
-
- print "consuming sync message"
- consumer = "consumer"
- queue = client.queue(consumer)
- channel.message_subscribe(queue="sync_queue", destination=consumer)
- channel.message_flow(consumer, 0, 0xFFFFFFFFL)
- channel.message_flow(consumer, 1, 0xFFFFFFFFL)
- queue.get(block = True)
- print "done"
- channel.session_close()
-
-def consumer():
- import sys
- import qpid
- from qpid.client import Client
- from qpid.content import Content
- client = Client("127.0.0.1", 5672)
- client.start({"LOGIN": "guest", "PASSWORD": "guest"})
- channel = client.channel(1)
- channel.session_open()
- consumer = "consumer"
- queue = client.queue(consumer)
- channel.message_subscribe(queue="message_queue", destination=consumer)
- channel.message_flow(consumer, 0, 0xFFFFFFFFL)
- channel.message_flow(consumer, 1, 0xFFFFFFFFL)
- final = "That's done"
- content = ""
- message = None
- print "getting messages"
- while content != final:
- message = queue.get(block = True)
- content = message.content.body
- message.complete(cumulative=True)
-
- print "consumed all messages"
- message = Content("message")
- message["routing_key"] = "sync_queue"
- channel.message_transfer(destination="amq.direct", content=message)
- print "done"
- channel.session_close()
-
-if __name__=='__main__':
- import sys
- import qpid
- from timeit import Timer
- from qpid.client import Client
- from qpid.content import Content
- client = Client("127.0.0.1", 5672)
- client.start({"LOGIN": "guest", "PASSWORD": "guest"})
- channel = client.channel(1)
- channel.session_open()
- channel.queue_declare(queue="message_queue")
- channel.queue_bind(exchange="amq.direct", queue="message_queue", routing_key="message_queue")
- channel.queue_declare(queue="sync_queue")
- channel.queue_bind(exchange="amq.direct", queue="sync_queue", routing_key="sync_queue")
- channel.session_close()
-
- numMess = 100
- if len(sys.argv) >= 3:
- numMess = int(sys.argv[2])
- if len(sys.argv) == 1:
- print "error: please specify prod or cons"
- elif sys.argv[1] == 'prod':
- tprod = Timer("publisher(100)", "from __main__ import publisher")
- tp = tprod.timeit(1)
- print "produced and consumed" , numMess + 2 ,"messages in: ", tp
- elif sys.argv[1] == 'cons':
- tcons = Timer("consumer()", "from __main__ import consumer")
- tc = tcons.timeit(1)
- print "consumed " , numMess ," in: ", tc
- else:
- print "please specify prod or cons"
diff --git a/qpid/python/qmf/console.py b/qpid/python/qmf/console.py
index 315d581fc6..8674736982 100644
--- a/qpid/python/qmf/console.py
+++ b/qpid/python/qmf/console.py
@@ -275,7 +275,7 @@ class Object(object):
for method in self._schema.getMethods():
if name == method.name:
aIdx = 0
- sendCodec = Codec(self._broker.conn.spec)
+ sendCodec = Codec()
seq = self._session.seqMgr._reserve((method, synchronous))
self._broker._setHeader(sendCodec, 'M', seq)
self._objectId.encode(sendCodec)
@@ -671,7 +671,7 @@ class Session:
self.getResult = []
for agent in agentList:
broker = agent.broker
- sendCodec = Codec(broker.conn.spec)
+ sendCodec = Codec()
try:
self.cv.acquire()
seq = self.seqMgr._reserve(self._CONTEXT_MULTIGET)
@@ -749,7 +749,7 @@ class Session:
# Send a package request
# (effectively inc and dec outstanding by not doing anything)
- sendCodec = Codec(broker.conn.spec)
+ sendCodec = Codec()
seq = self.seqMgr._reserve(self._CONTEXT_STARTUP)
broker._setHeader(sendCodec, 'P', seq)
smsg = broker._message(sendCodec.encoded)
@@ -770,7 +770,7 @@ class Session:
# Send a class request
broker._incOutstanding()
- sendCodec = Codec(broker.conn.spec)
+ sendCodec = Codec()
seq = self.seqMgr._reserve(self._CONTEXT_STARTUP)
broker._setHeader(sendCodec, 'Q', seq)
sendCodec.write_str8(pname)
@@ -815,7 +815,7 @@ class Session:
if unknown:
# Send a schema request for the unknown class
broker._incOutstanding()
- sendCodec = Codec(broker.conn.spec)
+ sendCodec = Codec()
seq = self.seqMgr._reserve(self._CONTEXT_STARTUP)
broker._setHeader(sendCodec, 'S', seq)
classKey.encode(sendCodec)
@@ -955,7 +955,7 @@ class Session:
elif typecode == 19: data = codec.read_int64() # S63
elif typecode == 15: # FTABLE
data = {}
- sc = Codec(codec.spec, codec.read_vbin32())
+ sc = Codec(codec.read_vbin32())
if sc.encoded:
count = sc.read_uint32()
while count > 0:
@@ -986,7 +986,7 @@ class Session:
data = self._decodeValue(codec, inner_type_code, broker)
elif typecode == 21: # List
#taken from codec10.read_list
- sc = Codec(codec.spec, codec.read_vbin32())
+ sc = Codec(codec.read_vbin32())
count = sc.read_uint32()
data = []
while count > 0:
@@ -995,7 +995,7 @@ class Session:
count -= 1
elif typecode == 22: #Array
#taken from codec10.read_array
- sc = Codec(codec.spec, codec.read_vbin32())
+ sc = Codec(codec.read_vbin32())
count = sc.read_uint32()
type = sc.read_uint8()
data = []
@@ -1027,7 +1027,7 @@ class Session:
elif typecode == 19: codec.write_int64 (int(value)) # S64
elif typecode == 20: value._encodeUnmanaged(codec) # OBJECT
elif typecode == 15: # FTABLE
- sc = Codec(codec.spec)
+ sc = Codec()
if value is not None:
sc.write_uint32(len(value))
for k, v in value.items():
@@ -1039,7 +1039,7 @@ class Session:
sc.write_uint32(0)
codec.write_vbin32(sc.encoded)
elif typecode == 21: # List
- sc = Codec(codec.spec)
+ sc = Codec()
self._encodeValue(sc, len(value), 3)
for o in value:
ltype=self.encoding(o)
@@ -1047,7 +1047,7 @@ class Session:
self._encodeValue(sc, o, ltype)
codec.write_vbin32(sc.encoded)
elif typecode == 22: # Array
- sc = Codec(codec.spec)
+ sc = Codec()
self._encodeValue(sc, len(value), 3)
if len(value) > 0:
ltype = self.encoding(value[0])
@@ -1159,7 +1159,7 @@ class Session:
for method in schema.getMethods():
if name == method.name:
aIdx = 0
- sendCodec = Codec(broker.conn.spec)
+ sendCodec = Codec()
seq = self.seqMgr._reserve((method, False))
broker._setHeader(sendCodec, 'M', seq)
objectId.encode(sendCodec)
@@ -1690,7 +1690,7 @@ class Broker:
self.connected = True
self.session._handleBrokerConnect(self)
- codec = Codec(self.conn.spec)
+ codec = Codec()
self._setHeader(codec, 'B')
msg = self._message(codec.encoded)
self._send(msg)
@@ -1809,7 +1809,7 @@ class Broker:
self.cv.release()
def _replyCb(self, msg):
- codec = Codec(self.conn.spec, msg.body)
+ codec = Codec(msg.body)
while True:
opcode, seq = self._checkHeader(codec)
if opcode == None: return
diff --git a/qpid/python/qpid-python-test b/qpid/python/qpid-python-test
index 3bf0e6ccce..528acaa124 100755
--- a/qpid/python/qpid-python-test
+++ b/qpid/python/qpid-python-test
@@ -25,6 +25,7 @@ from fnmatch import fnmatchcase as match
from getopt import GetoptError
from logging import getLogger, StreamHandler, Formatter, Filter, \
WARN, DEBUG, ERROR
+from qpid.harness import Skipped
from qpid.util import URL
levels = {
@@ -50,6 +51,8 @@ parser.add_option("-v", "--log-level", metavar="LEVEL", default="WARN",
parser.add_option("-c", "--log-category", metavar="CATEGORY", action="append",
dest="log_categories", default=[],
help="log only categories matching CATEGORY pattern")
+parser.add_option("-m", "--module", action="append", default=[],
+ dest="modules", help="add module to test search path")
parser.add_option("-i", "--ignore", action="append", default=[],
help="ignore tests matching IGNORE pattern")
parser.add_option("-I", "--ignore-file", metavar="IFILE", action="append",
@@ -101,7 +104,10 @@ for a in args:
includes.append(a.strip())
if not includes:
- includes.append("*")
+ if opts.modules:
+ includes.append("*")
+ else:
+ includes.extend(["qpid.tests.*", "tests.*", "tests_0-10.*"])
def is_ignored(path):
for p in excludes:
@@ -117,15 +123,21 @@ def is_included(path):
return True
return False
+def is_smart():
+ return sys.stdout.isatty() and os.environ.get("TERM", "dumb") != "dumb"
+
def width():
- if sys.stdout.isatty():
+ if is_smart():
s = struct.pack("HHHH", 0, 0, 0, 0)
fd_stdout = sys.stdout.fileno()
x = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, s)
rows, cols, xpx, ypx = struct.unpack("HHHH", x)
return cols
else:
- return 80
+ try:
+ return int(os.environ.get("COLUMNS", "80"))
+ except ValueError:
+ return 80
WIDTH = width()
@@ -142,13 +154,14 @@ def vt100_attrs(*attrs):
vt100_reset = vt100_attrs(0)
KEYWORDS = {"pass": (32,),
+ "skip": (33,),
"fail": (31,),
"start": (34,),
"total": (34,),
"ignored": (33,),
"selected": (34,)}
-COLORIZE = sys.stdout.isatty()
+COLORIZE = is_smart()
def colorize_word(word, text=None):
if text is None:
@@ -165,9 +178,6 @@ def indent(text):
lines = text.split("\n")
return " %s" % "\n ".join(lines)
-from qpid.testlib import testrunner
-testrunner.setBroker(str(config.broker))
-
class Interceptor:
def __init__(self):
@@ -264,16 +274,27 @@ root.setLevel(WARN)
log = getLogger("qpid.test")
+PASS = "pass"
+SKIP = "skip"
+FAIL = "fail"
+
class Runner:
def __init__(self):
self.exceptions = []
+ self.skip = False
def passed(self):
return not self.exceptions
+ def skipped(self):
+ return self.skip
+
def failed(self):
- return self.exceptions
+ return self.exceptions and not self.skip
+
+ def halt(self):
+ return self.exceptions or self.skip
def run(self, name, phase):
try:
@@ -281,18 +302,28 @@ class Runner:
except KeyboardInterrupt:
raise
except:
- self.exceptions.append((name, sys.exc_info()))
+ exi = sys.exc_info()
+ if issubclass(exi[0], Skipped):
+ self.skip = True
+ self.exceptions.append((name, exi))
def status(self):
if self.passed():
- return "pass"
+ return PASS
+ elif self.skipped():
+ return SKIP
+ elif self.failed():
+ return FAIL
else:
- return "fail"
+ return None
def print_exceptions(self):
for name, info in self.exceptions:
- print "Error during %s:" % name
- print indent("".join(traceback.format_exception(*info))).rstrip()
+ if issubclass(info[0], Skipped):
+ print indent("".join(traceback.format_exception_only(*info[:2]))).rstrip()
+ else:
+ print "Error during %s:" % name
+ print indent("".join(traceback.format_exception(*info))).rstrip()
ST_WIDTH = 8
@@ -329,11 +360,11 @@ def run_test(name, test, config):
sys.stdout.write("\n")
sys.stdout.write(output)
print " %s" % colorize_word(runner.status())
- if runner.failed():
+ if runner.failed() or runner.skipped():
runner.print_exceptions()
root.setLevel(level)
filter.patterns = patterns
- return runner.passed()
+ return runner.status()
class FunctionTest:
@@ -373,13 +404,13 @@ class MethodTest:
if hasattr(inst, "configure"):
runner.run("configure", lambda: inst.configure(config))
- if runner.failed(): return runner
+ if runner.halt(): return runner
if hasattr(inst, "setUp"):
runner.run("setup", inst.setUp)
- if runner.failed(): return runner
+ if runner.halt(): return runner
elif hasattr(inst, "setup"):
runner.run("setup", inst.setup)
- if runner.failed(): return runner
+ if runner.halt(): return runner
runner.run("test", test)
@@ -473,7 +504,9 @@ class Harness:
objects.append(child)
self.scanned.append(obj)
-modules = "qpid.tests", "tests", "tests_0-10"
+modules = opts.modules
+if not modules:
+ modules.extend(["qpid.tests", "tests", "tests_0-8", "tests_0-9", "tests_0-10"])
h = Harness()
for name in modules:
m = __import__(name, None, None, ["dummy"])
@@ -485,13 +518,17 @@ total = len(filtered) + len(ignored)
passed = 0
failed = 0
+skipped = 0
for t in filtered:
if list_only:
print t.name()
else:
- if t.run():
+ st = t.run()
+ if st == PASS:
passed += 1
- else:
+ elif st == SKIP:
+ skipped += 1
+ elif st == FAIL:
failed += 1
if opts.hoe:
break
@@ -499,6 +536,10 @@ for t in filtered:
run = passed + failed
if not list_only:
+ if passed:
+ _pass = "pass"
+ else:
+ _pass = "fail"
if failed:
outcome = "fail"
else:
@@ -507,12 +548,22 @@ if not list_only:
ign = "ignored"
else:
ign = "pass"
+ if skipped:
+ skip = "skip"
+ else:
+ skip = "pass"
print colorize("Totals:", 1), \
colorize_word("total", "%s tests" % total) + ",", \
- colorize_word("pass", "%s passed" % passed) + ",", \
+ colorize_word(_pass, "%s passed" % passed) + ",", \
+ colorize_word(skip, "%s skipped" % skipped) + ",", \
colorize_word(ign, "%s ignored" % len(ignored)) + ",", \
colorize_word(outcome, "%s failed" % failed),
if opts.hoe and failed > 0:
print " -- (halted after %s)" % run
else:
print
+
+if failed or skipped:
+ sys.exit(1)
+else:
+ sys.exit(0)
diff --git a/qpid/python/qpid/assembler.py b/qpid/python/qpid/assembler.py
deleted file mode 100644
index 92bb0aa0f8..0000000000
--- a/qpid/python/qpid/assembler.py
+++ /dev/null
@@ -1,118 +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.
-#
-
-from codec010 import StringCodec
-from framer import *
-from logging import getLogger
-
-log = getLogger("qpid.io.seg")
-
-class Segment:
-
- def __init__(self, first, last, type, track, channel, payload):
- self.id = None
- self.offset = None
- self.first = first
- self.last = last
- self.type = type
- self.track = track
- self.channel = channel
- self.payload = payload
-
- def decode(self, spec):
- segs = spec["segment_type"]
- choice = segs.choices[self.type]
- return getattr(self, "decode_%s" % choice.name)(spec)
-
- def decode_control(self, spec):
- sc = StringCodec(spec, self.payload)
- return sc.read_control()
-
- def decode_command(self, spec):
- sc = StringCodec(spec, self.payload)
- hdr, cmd = sc.read_command()
- cmd.id = self.id
- return hdr, cmd
-
- def decode_header(self, spec):
- sc = StringCodec(spec, self.payload)
- values = []
- while len(sc.encoded) > 0:
- values.append(sc.read_struct32())
- return values
-
- def decode_body(self, spec):
- return self.payload
-
- def __str__(self):
- return "%s%s %s %s %s %r" % (int(self.first), int(self.last), self.type,
- self.track, self.channel, self.payload)
-
- def __repr__(self):
- return str(self)
-
-class Assembler(Framer):
-
- def __init__(self, sock, max_payload = Frame.MAX_PAYLOAD):
- Framer.__init__(self, sock)
- self.max_payload = max_payload
- self.fragments = {}
-
- def read_segment(self):
- while True:
- frame = self.read_frame()
-
- key = (frame.channel, frame.track)
- seg = self.fragments.get(key)
- if seg == None:
- seg = Segment(frame.isFirstSegment(), frame.isLastSegment(),
- frame.type, frame.track, frame.channel, "")
- self.fragments[key] = seg
-
- seg.payload += frame.payload
-
- if frame.isLastFrame():
- self.fragments.pop(key)
- log.debug("RECV %s", seg)
- return seg
-
- def write_segment(self, segment):
- remaining = segment.payload
-
- first = True
- while first or remaining:
- payload = remaining[:self.max_payload]
- remaining = remaining[self.max_payload:]
-
- flags = 0
- if first:
- flags |= FIRST_FRM
- first = False
- if not remaining:
- flags |= LAST_FRM
- if segment.first:
- flags |= FIRST_SEG
- if segment.last:
- flags |= LAST_SEG
-
- frame = Frame(flags, segment.type, segment.track, segment.channel,
- payload)
- self.write_frame(frame)
-
- log.debug("SENT %s", segment)
diff --git a/qpid/python/qpid/client.py b/qpid/python/qpid/client.py
index 4605710de8..6107a4bc35 100644
--- a/qpid/python/qpid/client.py
+++ b/qpid/python/qpid/client.py
@@ -39,11 +39,8 @@ class Client:
if spec:
self.spec = spec
else:
- try:
- name = os.environ["AMQP_SPEC"]
- except KeyError:
- raise EnvironmentError("environment variable AMQP_SPEC must be set")
- self.spec = load(name)
+ from qpid_config import amqp_spec_0_9
+ self.spec = load(amqp_spec_0_9)
self.structs = StructFactory(self.spec)
self.sessions = {}
diff --git a/qpid/python/qpid/codec010.py b/qpid/python/qpid/codec010.py
index f07362c38d..682743df19 100644
--- a/qpid/python/qpid/codec010.py
+++ b/qpid/python/qpid/codec010.py
@@ -20,23 +20,66 @@
import datetime
from packer import Packer
from datatypes import serial, timestamp, RangedSet, Struct, UUID
+from ops import Compound, PRIMITIVE, COMPOUND
class CodecException(Exception): pass
+def direct(t):
+ return lambda x: t
+
+def map_str(s):
+ for c in s:
+ if ord(c) >= 0x80:
+ return "vbin16"
+ return "str16"
+
class Codec(Packer):
- def __init__(self, spec):
- self.spec = spec
+ ENCODINGS = {
+ unicode: direct("str16"),
+ str: map_str,
+ buffer: direct("vbin32"),
+ int: direct("int64"),
+ long: direct("int64"),
+ float: direct("double"),
+ None.__class__: direct("void"),
+ list: direct("list"),
+ tuple: direct("list"),
+ dict: direct("map"),
+ timestamp: direct("datetime"),
+ datetime.datetime: direct("datetime"),
+ UUID: direct("uuid"),
+ Compound: direct("struct32")
+ }
+
+ def encoding(self, obj):
+ enc = self._encoding(obj.__class__, obj)
+ if enc is None:
+ raise CodecException("no encoding for %r" % obj)
+ return PRIMITIVE[enc]
+
+ def _encoding(self, klass, obj):
+ if self.ENCODINGS.has_key(klass):
+ return self.ENCODINGS[klass](obj)
+ for base in klass.__bases__:
+ result = self._encoding(base, obj)
+ if result != None:
+ return result
+
+ def read_primitive(self, type):
+ return getattr(self, "read_%s" % type.NAME)()
+ def write_primitive(self, type, v):
+ getattr(self, "write_%s" % type.NAME)(v)
- def write_void(self, v):
- assert v == None
def read_void(self):
return None
+ def write_void(self, v):
+ assert v == None
- def write_bit(self, b):
- if not b: raise ValueError(b)
def read_bit(self):
return True
+ def write_bit(self, b):
+ if not b: raise ValueError(b)
def read_uint8(self):
return self.unpack("!B")
@@ -172,20 +215,8 @@ class Codec(Packer):
self.write_uint32(len(b))
self.write(b)
- def write_map(self, m):
- sc = StringCodec(self.spec)
- if m is not None:
- sc.write_uint32(len(m))
- for k, v in m.items():
- type = self.spec.encoding(v)
- if type == None:
- raise CodecException("no encoding for %s" % v.__class__)
- sc.write_str8(k)
- sc.write_uint8(type.code)
- type.encode(sc, v)
- self.write_vbin32(sc.encoded)
def read_map(self):
- sc = StringCodec(self.spec, self.read_vbin32())
+ sc = StringCodec(self.read_vbin32())
if not sc.encoded:
return None
count = sc.read_uint32()
@@ -193,91 +224,132 @@ class Codec(Packer):
while sc.encoded:
k = sc.read_str8()
code = sc.read_uint8()
- type = self.spec.types[code]
- v = type.decode(sc)
+ type = PRIMITIVE[code]
+ v = sc.read_primitive(type)
result[k] = v
return result
+ def write_map(self, m):
+ sc = StringCodec()
+ if m is not None:
+ sc.write_uint32(len(m))
+ for k, v in m.items():
+ type = self.encoding(v)
+ sc.write_str8(k)
+ sc.write_uint8(type.CODE)
+ sc.write_primitive(type, v)
+ self.write_vbin32(sc.encoded)
+ def read_array(self):
+ sc = StringCodec(self.read_vbin32())
+ if not sc.encoded:
+ return None
+ type = PRIMITIVE[sc.read_uint8()]
+ count = sc.read_uint32()
+ result = []
+ while count > 0:
+ result.append(sc.read_primitive(type))
+ count -= 1
+ return result
def write_array(self, a):
- sc = StringCodec(self.spec)
+ sc = StringCodec()
if a is not None:
if len(a) > 0:
- type = self.spec.encoding(a[0])
+ type = self.encoding(a[0])
else:
- type = self.spec.encoding(None)
- sc.write_uint8(type.code)
+ type = self.encoding(None)
+ sc.write_uint8(type.CODE)
sc.write_uint32(len(a))
for o in a:
- type.encode(sc, o)
+ sc.write_primitive(type, o)
self.write_vbin32(sc.encoded)
- def read_array(self):
- sc = StringCodec(self.spec, self.read_vbin32())
+
+ def read_list(self):
+ sc = StringCodec(self.read_vbin32())
if not sc.encoded:
return None
- type = self.spec.types[sc.read_uint8()]
count = sc.read_uint32()
result = []
while count > 0:
- result.append(type.decode(sc))
+ type = PRIMITIVE[sc.read_uint8()]
+ result.append(sc.read_primitive(type))
count -= 1
return result
-
def write_list(self, l):
- sc = StringCodec(self.spec)
+ sc = StringCodec()
if l is not None:
sc.write_uint32(len(l))
for o in l:
- type = self.spec.encoding(o)
- sc.write_uint8(type.code)
- type.encode(sc, o)
+ type = self.encoding(o)
+ sc.write_uint8(type.CODE)
+ sc.write_primitive(type, o)
self.write_vbin32(sc.encoded)
- def read_list(self):
- sc = StringCodec(self.spec, self.read_vbin32())
- if not sc.encoded:
- return None
- count = sc.read_uint32()
- result = []
- while count > 0:
- type = self.spec.types[sc.read_uint8()]
- result.append(type.decode(sc))
- count -= 1
- return result
def read_struct32(self):
size = self.read_uint32()
code = self.read_uint16()
- type = self.spec.structs[code]
- fields = type.decode_fields(self)
- return Struct(type, **fields)
+ cls = COMPOUND[code]
+ op = cls()
+ self.read_fields(op)
+ return op
def write_struct32(self, value):
- sc = StringCodec(self.spec)
- sc.write_uint16(value._type.code)
- value._type.encode_fields(sc, value)
- self.write_vbin32(sc.encoded)
-
- def read_control(self):
- cntrl = self.spec.controls[self.read_uint16()]
- return Struct(cntrl, **cntrl.decode_fields(self))
- def write_control(self, ctrl):
- type = ctrl._type
- self.write_uint16(type.code)
- type.encode_fields(self, ctrl)
-
- def read_command(self):
- type = self.spec.commands[self.read_uint16()]
- hdr = self.spec["session.header"].decode(self)
- cmd = Struct(type, **type.decode_fields(self))
- return hdr, cmd
- def write_command(self, hdr, cmd):
- self.write_uint16(cmd._type.code)
- hdr._type.encode(self, hdr)
- cmd._type.encode_fields(self, cmd)
+ self.write_compound(value)
+
+ def read_compound(self, cls):
+ size = self.read_size(cls.SIZE)
+ if cls.CODE is not None:
+ code = self.read_uint16()
+ assert code == cls.CODE
+ op = cls()
+ self.read_fields(op)
+ return op
+ def write_compound(self, op):
+ sc = StringCodec()
+ if op.CODE is not None:
+ sc.write_uint16(op.CODE)
+ sc.write_fields(op)
+ self.write_size(op.SIZE, len(sc.encoded))
+ self.write(sc.encoded)
+
+ def read_fields(self, op):
+ flags = 0
+ for i in range(op.PACK):
+ flags |= (self.read_uint8() << 8*i)
+
+ for i in range(len(op.FIELDS)):
+ f = op.FIELDS[i]
+ if flags & (0x1 << i):
+ if COMPOUND.has_key(f.type):
+ value = self.read_compound(COMPOUND[f.type])
+ else:
+ value = getattr(self, "read_%s" % f.type)()
+ setattr(op, f.name, value)
+ def write_fields(self, op):
+ flags = 0
+ for i in range(len(op.FIELDS)):
+ f = op.FIELDS[i]
+ value = getattr(op, f.name)
+ if f.type == "bit":
+ present = value
+ else:
+ present = value != None
+ if present:
+ flags |= (0x1 << i)
+ for i in range(op.PACK):
+ self.write_uint8((flags >> 8*i) & 0xFF)
+ for i in range(len(op.FIELDS)):
+ f = op.FIELDS[i]
+ if flags & (0x1 << i):
+ if COMPOUND.has_key(f.type):
+ enc = self.write_compound
+ else:
+ enc = getattr(self, "write_%s" % f.type)
+ value = getattr(op, f.name)
+ enc(value)
def read_size(self, width):
if width > 0:
attr = "read_uint%d" % (width*8)
return getattr(self, attr)()
-
def write_size(self, width, n):
if width > 0:
attr = "write_uint%d" % (width*8)
@@ -285,7 +357,6 @@ class Codec(Packer):
def read_uuid(self):
return UUID(self.unpack("16s"))
-
def write_uuid(self, s):
if isinstance(s, UUID):
s = s.bytes
@@ -293,7 +364,6 @@ class Codec(Packer):
def read_bin128(self):
return self.unpack("16s")
-
def write_bin128(self, b):
self.pack("16s", b)
@@ -301,14 +371,13 @@ class Codec(Packer):
class StringCodec(Codec):
- def __init__(self, spec, encoded = ""):
- Codec.__init__(self, spec)
+ def __init__(self, encoded = ""):
self.encoded = encoded
- def write(self, s):
- self.encoded += s
-
def read(self, n):
result = self.encoded[:n]
self.encoded = self.encoded[n:]
return result
+
+ def write(self, s):
+ self.encoded += s
diff --git a/qpid/python/qpid/connection.py b/qpid/python/qpid/connection.py
index 5abab3802c..680f8f62e3 100644
--- a/qpid/python/qpid/connection.py
+++ b/qpid/python/qpid/connection.py
@@ -20,14 +20,14 @@
import datatypes, session
from threading import Thread, Condition, RLock
from util import wait, notify
-from assembler import Assembler, Segment
from codec010 import StringCodec
+from framing import *
from session import Session
from generator import control_invoker
from spec import SPEC
from exceptions import *
from logging import getLogger
-import delegates
+import delegates, socket
class ChannelBusy(Exception): pass
@@ -43,12 +43,12 @@ def client(*args, **kwargs):
def server(*args, **kwargs):
return delegates.Server(*args, **kwargs)
-class Connection(Assembler):
+from framer import Framer
- def __init__(self, sock, spec=SPEC, delegate=client, **args):
- Assembler.__init__(self, sock)
- self.spec = spec
+class Connection(Framer):
+ def __init__(self, sock, delegate=client, **args):
+ Framer.__init__(self, sock)
self.lock = RLock()
self.attached = {}
self.sessions = {}
@@ -66,6 +66,10 @@ class Connection(Assembler):
self.channel_max = 65535
+ self.op_enc = OpEncoder()
+ self.seg_enc = SegmentEncoder()
+ self.frame_enc = FrameEncoder()
+
self.delegate = delegate(self, **args)
def attach(self, name, ch, delegate, force=False):
@@ -145,15 +149,44 @@ class Connection(Assembler):
raise ConnectionFailed(*self.close_code)
def run(self):
+ frame_dec = FrameDecoder()
+ seg_dec = SegmentDecoder()
+ op_dec = OpDecoder()
+
while not self.closed:
try:
- seg = self.read_segment()
- except Closed:
+ data = self.sock.recv(64*1024)
+ if not data:
+ self.detach_all()
+ break
+ except socket.timeout:
+ if self.aborted():
+ self.detach_all()
+ raise Closed("connection timed out")
+ else:
+ continue
+ except socket.error, e:
self.detach_all()
- break
- self.delegate.received(seg)
+ raise Closed(e)
+ frame_dec.write(data)
+ seg_dec.write(*frame_dec.read())
+ op_dec.write(*seg_dec.read())
+ for op in op_dec.read():
+ self.delegate.received(op)
self.sock.close()
+ def write_op(self, op):
+ self.sock_lock.acquire()
+ try:
+ self.op_enc.write(op)
+ self.seg_enc.write(*self.op_enc.read())
+ self.frame_enc.write(*self.seg_enc.read())
+ bytes = self.frame_enc.read()
+ self.write(bytes)
+ self.flush()
+ finally:
+ self.sock_lock.release()
+
def close(self, timeout=None):
if not self.opened: return
Channel(self, 0).connection_close(200)
@@ -169,19 +202,17 @@ class Connection(Assembler):
log = getLogger("qpid.io.ctl")
-class Channel(control_invoker(SPEC)):
+class Channel(control_invoker()):
def __init__(self, connection, id):
self.connection = connection
self.id = id
self.session = None
- def invoke(self, type, args, kwargs):
- ctl = type.new(args, kwargs)
- sc = StringCodec(self.spec)
- sc.write_control(ctl)
- self.connection.write_segment(Segment(True, True, type.segment_type,
- type.track, self.id, sc.encoded))
+ def invoke(self, op, args, kwargs):
+ ctl = op(*args, **kwargs)
+ ctl.channel = self.id
+ self.connection.write_op(ctl)
log.debug("SENT %s", ctl)
def __str__(self):
diff --git a/qpid/python/qpid/connection08.py b/qpid/python/qpid/connection08.py
index be94a792cb..d34cfe2847 100644
--- a/qpid/python/qpid/connection08.py
+++ b/qpid/python/qpid/connection08.py
@@ -28,6 +28,7 @@ from cStringIO import StringIO
from spec import load
from codec import EOF
from compat import SHUT_RDWR
+from exceptions import VersionError
class SockIO:
@@ -73,6 +74,9 @@ def listen(host, port, predicate = lambda: True):
s, a = sock.accept()
yield SockIO(s)
+class FramingError(Exception):
+ pass
+
class Connection:
def __init__(self, io, spec):
@@ -107,7 +111,16 @@ class Connection:
def read_8_0(self):
c = self.codec
- type = self.spec.constants.byid[c.decode_octet()].name
+ tid = c.decode_octet()
+ try:
+ type = self.spec.constants.byid[tid].name
+ except KeyError:
+ if tid == ord('A') and c.unpack("!3s") == "MQP":
+ _, _, major, minor = c.unpack("4B")
+ raise VersionError("client: %s-%s, server: %s-%s" %
+ (self.spec.major, self.spec.minor, major, minor))
+ else:
+ raise FramingError("unknown frame type: %s" % tid)
channel = c.decode_short()
body = c.decode_longstr()
dec = codec.Codec(StringIO(body), self.spec)
diff --git a/qpid/python/qpid/datatypes.py b/qpid/python/qpid/datatypes.py
index 4cd3fade2c..bba3f5b9ab 100644
--- a/qpid/python/qpid/datatypes.py
+++ b/qpid/python/qpid/datatypes.py
@@ -84,7 +84,7 @@ class Message:
def get(self, name):
if self.headers:
for h in self.headers:
- if h._type.name == name:
+ if h.NAME == name:
return h
return None
@@ -93,7 +93,7 @@ class Message:
self.headers = []
idx = 0
while idx < len(self.headers):
- if self.headers[idx]._type == header._type:
+ if self.headers[idx].NAME == header.NAME:
self.headers[idx] = header
return
idx += 1
@@ -102,7 +102,7 @@ class Message:
def clear(self, name):
idx = 0
while idx < len(self.headers):
- if self.headers[idx]._type.name == name:
+ if self.headers[idx].NAME == name:
del self.headers[idx]
return
idx += 1
diff --git a/qpid/python/qpid/delegates.py b/qpid/python/qpid/delegates.py
index 82bbe67ede..c74cc5a945 100644
--- a/qpid/python/qpid/delegates.py
+++ b/qpid/python/qpid/delegates.py
@@ -20,7 +20,9 @@
import os, connection, session
from util import notify
from datatypes import RangedSet
+from exceptions import VersionError
from logging import getLogger
+from ops import Control
import sys
log = getLogger("qpid.io.ctl")
@@ -29,26 +31,22 @@ class Delegate:
def __init__(self, connection, delegate=session.client):
self.connection = connection
- self.spec = connection.spec
self.delegate = delegate
- self.control = self.spec["track.control"].value
- def received(self, seg):
- ssn = self.connection.attached.get(seg.channel)
+ def received(self, op):
+ ssn = self.connection.attached.get(op.channel)
if ssn is None:
- ch = connection.Channel(self.connection, seg.channel)
+ ch = connection.Channel(self.connection, op.channel)
else:
ch = ssn.channel
- if seg.track == self.control:
- ctl = seg.decode(self.spec)
- log.debug("RECV %s", ctl)
- attr = ctl._type.qname.replace(".", "_")
- getattr(self, attr)(ch, ctl)
+ if isinstance(op, Control):
+ log.debug("RECV %s", op)
+ getattr(self, op.NAME)(ch, op)
elif ssn is None:
ch.session_detached()
else:
- ssn.received(seg)
+ ssn.received(op)
def connection_close(self, ch, close):
self.connection.close_code = (close.reply_code, close.reply_text)
@@ -124,7 +122,8 @@ class Server(Delegate):
def start(self):
self.connection.read_header()
- self.connection.write_header(self.spec.major, self.spec.minor)
+ # XXX
+ self.connection.write_header(0, 10)
connection.Channel(self.connection, 0).connection_start(mechanisms=["ANONYMOUS"])
def connection_start_ok(self, ch, start_ok):
@@ -156,8 +155,14 @@ class Client(Delegate):
self.heartbeat = heartbeat
def start(self):
- self.connection.write_header(self.spec.major, self.spec.minor)
- self.connection.read_header()
+ # XXX
+ cli_major = 0
+ cli_minor = 10
+ self.connection.write_header(cli_major, cli_minor)
+ magic, _, _, major, minor = self.connection.read_header()
+ if not (magic == "AMQP" and major == cli_major and minor == cli_minor):
+ raise VersionError("client: %s-%s, server: %s-%s" %
+ (cli_major, cli_minor, major, minor))
def connection_start(self, ch, start):
r = "\0%s\0%s" % (self.username, self.password)
diff --git a/qpid/python/qpid/exceptions.py b/qpid/python/qpid/exceptions.py
index 7eaaf81ed4..2bd80b7ffe 100644
--- a/qpid/python/qpid/exceptions.py
+++ b/qpid/python/qpid/exceptions.py
@@ -19,3 +19,4 @@
class Closed(Exception): pass
class Timeout(Exception): pass
+class VersionError(Exception): pass
diff --git a/qpid/python/qpid/framer.py b/qpid/python/qpid/framer.py
index 0d82e4378b..4cd0ae6f26 100644
--- a/qpid/python/qpid/framer.py
+++ b/qpid/python/qpid/framer.py
@@ -26,48 +26,6 @@ from logging import getLogger
raw = getLogger("qpid.io.raw")
frm = getLogger("qpid.io.frm")
-FIRST_SEG = 0x08
-LAST_SEG = 0x04
-FIRST_FRM = 0x02
-LAST_FRM = 0x01
-
-class Frame:
-
- HEADER = "!2BHxBH4x"
- HEADER_SIZE = struct.calcsize(HEADER)
- MAX_PAYLOAD = 65535 - struct.calcsize(HEADER)
-
- def __init__(self, flags, type, track, channel, payload):
- if len(payload) > Frame.MAX_PAYLOAD:
- raise ValueError("max payload size exceeded: %s" % len(payload))
- self.flags = flags
- self.type = type
- self.track = track
- self.channel = channel
- self.payload = payload
-
- def isFirstSegment(self):
- return bool(FIRST_SEG & self.flags)
-
- def isLastSegment(self):
- return bool(LAST_SEG & self.flags)
-
- def isFirstFrame(self):
- return bool(FIRST_FRM & self.flags)
-
- def isLastFrame(self):
- return bool(LAST_FRM & self.flags)
-
- def __repr__(self):
- return "%s%s%s%s %s %s %s %r" % (int(self.isFirstSegment()),
- int(self.isLastSegment()),
- int(self.isFirstFrame()),
- int(self.isLastFrame()),
- self.type,
- self.track,
- self.channel,
- self.payload)
-
class FramingError(Exception): pass
class Framer(Packer):
@@ -137,24 +95,3 @@ class Framer(Packer):
self.flush()
finally:
self.sock_lock.release()
-
- def write_frame(self, frame):
- self.sock_lock.acquire()
- try:
- size = len(frame.payload) + struct.calcsize(Frame.HEADER)
- track = frame.track & 0x0F
- self.pack(Frame.HEADER, frame.flags, frame.type, size, track, frame.channel)
- self.write(frame.payload)
- if frame.isLastSegment() and frame.isLastFrame():
- self.flush()
- frm.debug("SENT %s", frame)
- finally:
- self.sock_lock.release()
-
- def read_frame(self):
- flags, type, size, track, channel = self.unpack(Frame.HEADER)
- if flags & 0xF0: raise FramingError()
- payload = self.read(size - struct.calcsize(Frame.HEADER))
- frame = Frame(flags, type, track, channel, payload)
- frm.debug("RECV %s", frame)
- return frame
diff --git a/qpid/python/qpid/framing.py b/qpid/python/qpid/framing.py
index 7c5f68fbcc..0a8f26272c 100644
--- a/qpid/python/qpid/framing.py
+++ b/qpid/python/qpid/framing.py
@@ -18,8 +18,64 @@
#
import struct
-from qpid.framer import Frame, FIRST_SEG, LAST_SEG, FIRST_FRM, LAST_FRM
-from qpid.assembler import Segment
+
+FIRST_SEG = 0x08
+LAST_SEG = 0x04
+FIRST_FRM = 0x02
+LAST_FRM = 0x01
+
+class Frame:
+
+ HEADER = "!2BHxBH4x"
+ HEADER_SIZE = struct.calcsize(HEADER)
+ MAX_PAYLOAD = 65535 - struct.calcsize(HEADER)
+
+ def __init__(self, flags, type, track, channel, payload):
+ if len(payload) > Frame.MAX_PAYLOAD:
+ raise ValueError("max payload size exceeded: %s" % len(payload))
+ self.flags = flags
+ self.type = type
+ self.track = track
+ self.channel = channel
+ self.payload = payload
+
+ def isFirstSegment(self):
+ return bool(FIRST_SEG & self.flags)
+
+ def isLastSegment(self):
+ return bool(LAST_SEG & self.flags)
+
+ def isFirstFrame(self):
+ return bool(FIRST_FRM & self.flags)
+
+ def isLastFrame(self):
+ return bool(LAST_FRM & self.flags)
+
+ def __repr__(self):
+ return "%s%s%s%s %s %s %s %r" % (int(self.isFirstSegment()),
+ int(self.isLastSegment()),
+ int(self.isFirstFrame()),
+ int(self.isLastFrame()),
+ self.type,
+ self.track,
+ self.channel,
+ self.payload)
+
+class Segment:
+
+ def __init__(self, first, last, type, track, channel, payload):
+ self.id = None
+ self.offset = None
+ self.first = first
+ self.last = last
+ self.type = type
+ self.track = track
+ self.channel = channel
+ self.payload = payload
+
+ def __repr__(self):
+ return "%s%s %s %s %s %r" % (int(self.first), int(self.last), self.type,
+ self.track, self.channel, self.payload)
class FrameDecoder:
@@ -140,3 +196,115 @@ class SegmentEncoder:
result = self.frames
self.frames = []
return result
+
+from ops import COMMANDS, CONTROLS, COMPOUND, Header, segment_type, track
+from spec import SPEC
+
+from codec010 import StringCodec
+
+class OpEncoder:
+
+ def __init__(self):
+ self.segments = []
+
+ def write(self, *ops):
+ for op in ops:
+ if COMMANDS.has_key(op.NAME):
+ seg_type = segment_type.command
+ seg_track = track.command
+ enc = self.encode_command(op)
+ elif CONTROLS.has_key(op.NAME):
+ seg_type = segment_type.control
+ seg_track = track.control
+ enc = self.encode_compound(op)
+ else:
+ raise ValueError(op)
+ seg = Segment(True, False, seg_type, seg_track, op.channel, enc)
+ self.segments.append(seg)
+ if hasattr(op, "headers") and op.headers is not None:
+ hdrs = ""
+ for h in op.headers:
+ hdrs += self.encode_compound(h)
+ seg = Segment(False, False, segment_type.header, seg_track, op.channel,
+ hdrs)
+ self.segments.append(seg)
+ if hasattr(op, "payload") and op.payload is not None:
+ self.segments.append(Segment(False, False, segment_type.body, seg_track,
+ op.channel, op.payload))
+ self.segments[-1].last = True
+
+ def encode_command(self, cmd):
+ sc = StringCodec()
+ sc.write_uint16(cmd.CODE)
+ sc.write_compound(Header(sync=cmd.sync))
+ sc.write_fields(cmd)
+ return sc.encoded
+
+ def encode_compound(self, op):
+ sc = StringCodec()
+ sc.write_compound(op)
+ return sc.encoded
+
+ def read(self):
+ result = self.segments
+ self.segments = []
+ return result
+
+class OpDecoder:
+
+ def __init__(self):
+ self.op = None
+ self.ops = []
+
+ def write(self, *segments):
+ for seg in segments:
+ if seg.first:
+ if seg.type == segment_type.command:
+ self.op = self.decode_command(seg.payload)
+ elif seg.type == segment_type.control:
+ self.op = self.decode_control(seg.payload)
+ else:
+ raise ValueError(seg)
+ self.op.channel = seg.channel
+ elif seg.type == segment_type.header:
+ if self.op.headers is None:
+ self.op.headers = []
+ self.op.headers.extend(self.decode_headers(seg.payload))
+ elif seg.type == segment_type.body:
+ if self.op.payload is None:
+ self.op.payload = seg.payload
+ else:
+ self.op.payload += seg.payload
+ if seg.last:
+ self.ops.append(self.op)
+ self.op = None
+
+ def decode_command(self, encoded):
+ sc = StringCodec(encoded)
+ code = sc.read_uint16()
+ cls = COMMANDS[code]
+ hdr = sc.read_compound(Header)
+ cmd = cls()
+ sc.read_fields(cmd)
+ cmd.sync = hdr.sync
+ return cmd
+
+ def decode_control(self, encoded):
+ sc = StringCodec(encoded)
+ code = sc.read_uint16()
+ cls = CONTROLS[code]
+ ctl = cls()
+ sc.read_fields(ctl)
+ return ctl
+
+ def decode_headers(self, encoded):
+ sc = StringCodec(encoded)
+ result = []
+ while sc.encoded:
+ result.append(sc.read_struct32())
+ return result
+
+ def read(self):
+ result = self.ops
+ self.ops = []
+ return result
diff --git a/qpid/python/qpid/generator.py b/qpid/python/qpid/generator.py
index 729425d6a3..02d11e5005 100644
--- a/qpid/python/qpid/generator.py
+++ b/qpid/python/qpid/generator.py
@@ -19,42 +19,38 @@
import sys
-from spec010 import Control
+from ops import *
-def METHOD(module, inst):
- method = lambda self, *args, **kwargs: self.invoke(inst, args, kwargs)
+def METHOD(module, op):
+ method = lambda self, *args, **kwargs: self.invoke(op, args, kwargs)
if sys.version_info[:2] > (2, 3):
- method.__name__ = str(inst.pyname)
- method.__doc__ = str(inst.pydoc)
+ method.__name__ = op.__name__
+ method.__doc__ = op.__doc__
method.__module__ = module
return method
-def generate(spec, module, predicate=lambda x: True):
- dict = {"spec": spec}
+def generate(module, operations):
+ dict = {}
- for name, enum in spec.enums.items():
- dict[name] = enum
+ for name, enum in ENUMS.items():
+ if isinstance(name, basestring):
+ dict[name] = enum
- for name, st in spec.structs_by_name.items():
- dict[name] = METHOD(module, st)
+ for name, op in COMPOUND.items():
+ if isinstance(name, basestring):
+ dict[name] = METHOD(module, op)
- for st in spec.structs.values():
- dict[st.name] = METHOD(module, st)
-
- for name, inst in spec.instructions.items():
- if predicate(inst):
- dict[name] = METHOD(module, inst)
+ for name, op in operations.items():
+ if isinstance(name, basestring):
+ dict[name] = METHOD(module, op)
return dict
-def invoker(name, spec, predicate=lambda x: True):
- return type("%s_%s_%s" % (name, spec.major, spec.minor),
- (), generate(spec, invoker.__module__, predicate))
+def invoker(name, operations):
+ return type(name, (), generate(invoker.__module__, operations))
-def command_invoker(spec):
- is_command = lambda cmd: cmd.track == spec["track.command"].value
- return invoker("CommandInvoker", spec, is_command)
+def command_invoker():
+ return invoker("CommandInvoker", COMMANDS)
-def control_invoker(spec):
- is_control = lambda inst: isinstance(inst, Control)
- return invoker("ControlInvoker", spec, is_control)
+def control_invoker():
+ return invoker("ControlInvoker", CONTROLS)
diff --git a/qpid/python/tests_0-9/execution.py b/qpid/python/qpid/harness.py
index f2facfe42b..ce48481612 100644
--- a/qpid/python/tests_0-9/execution.py
+++ b/qpid/python/qpid/harness.py
@@ -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
@@ -17,13 +17,4 @@
# under the License.
#
-from qpid.content import Content
-from qpid.testlib import testrunner, TestBase
-
-class ExecutionTests (TestBase):
- def test_flush(self):
- channel = self.channel
- for i in [1, 2, 3]:
- channel.basic_publish()
- channel.execution_flush()
- assert(channel.completion.wait(channel.completion.command_id, timeout=1))
+class Skipped(Exception): pass
diff --git a/qpid/python/qpid/messaging.py b/qpid/python/qpid/messaging.py
index f06ef87709..9b3fecbf9b 100644
--- a/qpid/python/qpid/messaging.py
+++ b/qpid/python/qpid/messaging.py
@@ -34,8 +34,8 @@ import connection, time, socket, sys, traceback
from codec010 import StringCodec
from datatypes import timestamp, uuid4, RangedSet, Message as Message010
from logging import getLogger
+from ops import PRIMITIVE
from session import Client, INCOMPLETE
-from spec import SPEC
from threading import Thread, RLock, Condition
from util import connect
@@ -191,9 +191,12 @@ class Connection(Lockable):
try:
self._socket = connect(self.host, self.port)
except socket.error, e:
- raise ConnectError(*e.args)
+ raise ConnectError(e)
self._conn = connection.Connection(self._socket)
- self._conn.start()
+ try:
+ self._conn.start()
+ except connection.VersionError, e:
+ raise ConnectError(e)
for ssn in self.sessions.values():
ssn._attach()
@@ -263,8 +266,8 @@ FILTER_DEFAULTS = {
def delegate(session):
class Delegate(Client):
- def message_transfer(self, cmd, headers, body):
- session._message_transfer(cmd, headers, body)
+ def message_transfer(self, cmd):
+ session._message_transfer(cmd)
return Delegate
class Session(Lockable):
@@ -314,9 +317,9 @@ class Session(Lockable):
link._disconnected()
@synchronized
- def _message_transfer(self, cmd, headers, body):
- m = Message010(body)
- m.headers = headers
+ def _message_transfer(self, cmd):
+ m = Message010(cmd.payload)
+ m.headers = cmd.headers
m.id = cmd.id
msg = self._decode(m)
rcv = self.receivers[int(cmd.destination)]
@@ -812,16 +815,16 @@ class Receiver(Lockable):
def codec(name):
- type = SPEC.named[name]
+ type = PRIMITIVE[name]
def encode(x):
- sc = StringCodec(SPEC)
- type.encode(sc, x)
+ sc = StringCodec()
+ sc.write_primitive(type, x)
return sc.encoded
def decode(x):
- sc = StringCodec(SPEC, x)
- return type.decode(sc)
+ sc = StringCodec(x)
+ return sc.read_primitive(type)
return encode, decode
diff --git a/qpid/python/qpid/ops.py b/qpid/python/qpid/ops.py
new file mode 100644
index 0000000000..447f9953df
--- /dev/null
+++ b/qpid/python/qpid/ops.py
@@ -0,0 +1,283 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+import os, mllib, cPickle as pickle
+from util import fill
+
+class Primitive(object):
+ pass
+
+class Enum(object):
+ pass
+
+class Field:
+
+ def __init__(self, name, type, default=None):
+ self.name = name
+ self.type = type
+ self.default = default
+
+ def __repr__(self):
+ return "%s: %s" % (self.name, self.type)
+
+class Compound(object):
+
+ UNENCODED=[]
+
+ def __init__(self, *args, **kwargs):
+ args = list(args)
+ for f in self.ARGS:
+ if args:
+ a = args.pop(0)
+ else:
+ a = kwargs.pop(f.name, f.default)
+ setattr(self, f.name, a)
+ if args:
+ raise TypeError("%s takes at most %s arguments (%s given))" %
+ (self.__class__.__name__, len(self.ARGS),
+ len(self.ARGS) + len(args)))
+ if kwargs:
+ raise TypeError("got unexpected keyword argument '%s'" % kwargs.keys()[0])
+
+ def fields(self):
+ result = {}
+ for f in self.FIELDS:
+ result[f.name] = getattr(self, f.name)
+ return result
+
+ def args(self):
+ result = {}
+ for f in self.ARGS:
+ result[f.name] = getattr(self, f.name)
+ return result
+
+ def __getitem__(self, attr):
+ return getattr(self, attr)
+
+ def __setitem__(self, attr, value):
+ setattr(self, attr, value)
+
+ def dispatch(self, target, *args):
+ handler = "do_%s" % self.NAME
+ if hasattr(target, handler):
+ getattr(target, handler)(self, *args)
+ else:
+ print "UNHANDLED:", target, args
+
+ def __repr__(self, extras=()):
+ return "%s(%s)" % (self.__class__.__name__,
+ ", ".join(["%s=%r" % (f.name, getattr(self, f.name))
+ for f in self.ARGS
+ if getattr(self, f.name) is not f.default]))
+
+class Command(Compound):
+ UNENCODED=[Field("channel", "uint16", 0),
+ Field("id", "sequence-no", None),
+ Field("sync", "bit", False),
+ Field("headers", None, None),
+ Field("payload", None, None)]
+
+class Control(Compound):
+ UNENCODED=[Field("channel", "uint16", 0)]
+
+def pythonize(st):
+ if st is None:
+ return None
+ else:
+ return str(st.replace("-", "_"))
+
+def pydoc(op, children=()):
+ doc = "\n\n".join([fill(p.text(), 0) for p in op.query["doc"]])
+ for ch in children:
+ doc += "\n\n " + pythonize(ch["@name"]) + " -- " + str(ch["@label"])
+ ch_descs ="\n\n".join([fill(p.text(), 4) for p in ch.query["doc"]])
+ if ch_descs:
+ doc += "\n\n" + ch_descs
+ return doc
+
+def studly(st):
+ return "".join([p.capitalize() for p in st.split("-")])
+
+def klass(nd):
+ while nd.parent is not None:
+ if hasattr(nd.parent, "name") and nd.parent.name == "class":
+ return nd.parent
+ else:
+ nd = nd.parent
+
+def included(nd):
+ cls = klass(nd)
+ if cls is None:
+ return True
+ else:
+ return cls["@name"] not in ("file", "stream")
+
+def num(s):
+ if s: return int(s, 0)
+
+def code(nd):
+ c = num(nd["@code"])
+ if c is None:
+ return None
+ else:
+ cls = klass(nd)
+ if cls is None:
+ return c
+ else:
+ return c | (num(cls["@code"]) << 8)
+
+def default(f):
+ if f["@type"] == "bit":
+ return False
+ else:
+ return None
+
+def make_compound(decl, base):
+ dict = {}
+ fields = decl.query["field"]
+ dict["__doc__"] = pydoc(decl, fields)
+ dict["NAME"] = pythonize(decl["@name"])
+ dict["SIZE"] = num(decl["@size"])
+ dict["CODE"] = code(decl)
+ dict["PACK"] = num(decl["@pack"])
+ dict["FIELDS"] = [Field(pythonize(f["@name"]), resolve(f), default(f)) for f in fields]
+ dict["ARGS"] = dict["FIELDS"] + base.UNENCODED
+ return str(studly(decl["@name"])), (base,), dict
+
+def make_restricted(decl):
+ name = pythonize(decl["@name"])
+ dict = {}
+ choices = decl.query["choice"]
+ dict["__doc__"] = pydoc(decl, choices)
+ dict["NAME"] = name
+ dict["TYPE"] = str(decl.parent["@type"])
+ values = []
+ for ch in choices:
+ val = int(ch["@value"], 0)
+ dict[pythonize(ch["@name"])] = val
+ values.append(val)
+ dict["VALUES"] = values
+ return name, (Enum,), dict
+
+def make_type(decl):
+ name = pythonize(decl["@name"])
+ dict = {}
+ dict["__doc__"] = pydoc(decl)
+ dict["NAME"] = name
+ dict["CODE"] = code(decl)
+ return str(studly(decl["@name"])), (Primitive,), dict
+
+def make_command(decl):
+ decl.set_attr("name", "%s-%s" % (decl.parent["@name"], decl["@name"]))
+ decl.set_attr("size", "0")
+ decl.set_attr("pack", "2")
+ name, bases, dict = make_compound(decl, Command)
+ dict["RESULT"] = pythonize(decl["result/@type"]) or pythonize(decl["result/struct/@name"])
+ return name, bases, dict
+
+def make_control(decl):
+ decl.set_attr("name", "%s-%s" % (decl.parent["@name"], decl["@name"]))
+ decl.set_attr("size", "0")
+ decl.set_attr("pack", "2")
+ return make_compound(decl, Control)
+
+def make_struct(decl):
+ return make_compound(decl, Compound)
+
+def make_enum(decl):
+ decl.set_attr("name", decl.parent["@name"])
+ return make_restricted(decl)
+
+
+vars = globals()
+
+def make(nd):
+ return vars["make_%s" % nd.name](nd)
+
+from qpid_config import amqp_spec as file
+pclfile = "%s.ops.pcl" % file
+
+if False and (os.path.exists(pclfile) and
+ os.path.getmtime(pclfile) > os.path.getmtime(file)):
+ f = open(pclfile, "read")
+ types = pickle.load(f)
+ f.close()
+else:
+ spec = mllib.xml_parse(file)
+
+ def qualify(nd, field="@name"):
+ cls = klass(nd)
+ if cls is None:
+ return pythonize(nd[field])
+ else:
+ return pythonize("%s.%s" % (cls["@name"], nd[field]))
+
+ domains = dict([(qualify(d), pythonize(d["@type"]))
+ for d in spec.query["amqp/domain", included] + \
+ spec.query["amqp/class/domain", included]])
+
+ def resolve(nd):
+ candidates = qualify(nd, "@type"), pythonize(nd["@type"])
+ for c in candidates:
+ if domains.has_key(c):
+ while domains.has_key(c):
+ c = domains[c]
+ return c
+ else:
+ return c
+
+ type_decls = \
+ spec.query["amqp/class/command", included] + \
+ spec.query["amqp/class/control", included] + \
+ spec.query["amqp/class/command/result/struct", included] + \
+ spec.query["amqp/class/struct", included] + \
+ spec.query["amqp/class/domain/enum", included] + \
+ spec.query["amqp/domain/enum", included] + \
+ spec.query["amqp/type"]
+ types = [make(nd) for nd in type_decls]
+
+ if os.access(os.path.dirname(os.path.abspath(pclfile)), os.W_OK):
+ f = open(pclfile, "write")
+ pickle.dump(types, f)
+ f.close()
+
+ENUMS = {}
+PRIMITIVE = {}
+COMPOUND = {}
+COMMANDS = {}
+CONTROLS = {}
+
+for name, bases, dict in types:
+ t = type(name, bases, dict)
+ vars[name] = t
+
+ if issubclass(t, Command):
+ COMMANDS[t.NAME] = t
+ COMMANDS[t.CODE] = t
+ elif issubclass(t, Control):
+ CONTROLS[t.NAME] = t
+ CONTROLS[t.CODE] = t
+ elif issubclass(t, Compound):
+ COMPOUND[t.NAME] = t
+ if t.CODE is not None:
+ COMPOUND[t.CODE] = t
+ elif issubclass(t, Primitive):
+ PRIMITIVE[t.NAME] = t
+ PRIMITIVE[t.CODE] = t
+ elif issubclass(t, Enum):
+ ENUMS[t.NAME] = t
diff --git a/qpid/python/qpid/peer.py b/qpid/python/qpid/peer.py
index 18d7848b8d..2bc9844351 100644
--- a/qpid/python/qpid/peer.py
+++ b/qpid/python/qpid/peer.py
@@ -25,7 +25,7 @@ incoming method frames to a delegate.
"""
import thread, threading, traceback, socket, sys, logging
-from connection08 import EOF, Method, Header, Body, Request, Response
+from connection08 import EOF, Method, Header, Body, Request, Response, VersionError
from message import Message
from queue import Queue, Closed as QueueClosed
from content import Content
@@ -95,6 +95,8 @@ class Peer:
break
ch = self.channel(frame.channel)
ch.receive(frame, self.work)
+ except VersionError, e:
+ self.closed(e)
except:
self.fatal()
diff --git a/qpid/python/qpid/session.py b/qpid/python/qpid/session.py
index 3b8bd18469..4413a22899 100644
--- a/qpid/python/qpid/session.py
+++ b/qpid/python/qpid/session.py
@@ -22,9 +22,9 @@ from spec import SPEC
from generator import command_invoker
from datatypes import RangedSet, Struct, Future
from codec010 import StringCodec
-from assembler import Segment
from queue import Queue
from datatypes import Message, serial
+from ops import Command, MessageTransfer
from util import wait, notify
from exceptions import *
from logging import getLogger
@@ -44,7 +44,7 @@ def server(*args):
INCOMPLETE = object()
-class Session(command_invoker(SPEC)):
+class Session(command_invoker()):
def __init__(self, name, auto_sync=True, timeout=10, delegate=client):
self.name = name
@@ -67,8 +67,6 @@ class Session(command_invoker(SPEC)):
self.results = {}
self.exceptions = []
- self.assembly = None
-
self.delegate = delegate(self)
def incoming(self, destination):
@@ -134,68 +132,51 @@ class Session(command_invoker(SPEC)):
finally:
self.lock.release()
- def invoke(self, type, args, kwargs):
- # XXX
- if not hasattr(type, "track"):
- return type.new(args, kwargs)
-
- self.invoke_lock.acquire()
- try:
- return self.do_invoke(type, args, kwargs)
- finally:
- self.invoke_lock.release()
+ def invoke(self, op, args, kwargs):
+ if issubclass(op, Command):
+ self.invoke_lock.acquire()
+ try:
+ return self.do_invoke(op, args, kwargs)
+ finally:
+ self.invoke_lock.release()
+ else:
+ return op(*args, **kwargs)
- def do_invoke(self, type, args, kwargs):
+ def do_invoke(self, op, args, kwargs):
if self._closing:
raise SessionClosed()
if self.channel == None:
raise SessionDetached()
- if type.segments:
- if len(args) == len(type.fields) + 1:
+ if op == MessageTransfer:
+ if len(args) == len(op.FIELDS) + 1:
message = args[-1]
args = args[:-1]
else:
message = kwargs.pop("message", None)
- else:
- message = None
-
- hdr = Struct(self.spec["session.header"])
- hdr.sync = self.auto_sync or kwargs.pop("sync", False)
- self.need_sync = not hdr.sync
+ if message is not None:
+ kwargs["headers"] = message.headers
+ kwargs["payload"] = message.body
- cmd = type.new(args, kwargs)
- sc = StringCodec(self.spec)
- sc.write_command(hdr, cmd)
+ cmd = op(*args, **kwargs)
+ cmd.sync = self.auto_sync or cmd.sync
+ self.need_sync = not cmd.sync
+ cmd.channel = self.channel.id
- seg = Segment(True, (message == None or
- (message.headers == None and message.body == None)),
- type.segment_type, type.track, self.channel.id, sc.encoded)
-
- if type.result:
+ if op.RESULT:
result = Future(exception=SessionException)
self.results[self.sender.next_id] = result
- self.send(seg)
-
- log.debug("SENT %s %s %s", seg.id, hdr, cmd)
-
- if message != None:
- if message.headers != None:
- sc = StringCodec(self.spec)
- for st in message.headers:
- sc.write_struct32(st)
- seg = Segment(False, message.body == None, self.spec["segment_type.header"].value,
- type.track, self.channel.id, sc.encoded)
- self.send(seg)
- if message.body != None:
- seg = Segment(False, True, self.spec["segment_type.body"].value,
- type.track, self.channel.id, message.body)
- self.send(seg)
- msg.debug("SENT %s", message)
-
- if type.result:
+ log.debug("SENDING %s", cmd)
+
+ self.send(cmd)
+
+ log.debug("SENT %s", cmd)
+ if op == MessageTransfer:
+ msg.debug("SENT %s", cmd)
+
+ if op.RESULT:
if self.auto_sync:
return result.get(self.timeout)
else:
@@ -203,81 +184,47 @@ class Session(command_invoker(SPEC)):
elif self.auto_sync:
self.sync(self.timeout)
- def received(self, seg):
- self.receiver.received(seg)
- if seg.first:
- assert self.assembly == None
- self.assembly = []
- self.assembly.append(seg)
- if seg.last:
- self.dispatch(self.assembly)
- self.assembly = None
-
- def dispatch(self, assembly):
- segments = assembly[:]
-
- hdr, cmd = assembly.pop(0).decode(self.spec)
- log.debug("RECV %s %s %s", cmd.id, hdr, cmd)
-
- args = []
-
- for st in cmd._type.segments:
- if assembly:
- seg = assembly[0]
- if seg.type == st.segment_type:
- args.append(seg.decode(self.spec))
- assembly.pop(0)
- continue
- args.append(None)
-
- assert len(assembly) == 0
+ def received(self, cmd):
+ self.receiver.received(cmd)
+ self.dispatch(cmd)
- attr = cmd._type.qname.replace(".", "_")
- result = getattr(self.delegate, attr)(cmd, *args)
+ def dispatch(self, cmd):
+ log.debug("RECV %s", cmd)
- if cmd._type.result:
+ result = getattr(self.delegate, cmd.NAME)(cmd)
+ if result is INCOMPLETE:
+ return
+ elif result is not None:
self.execution_result(cmd.id, result)
- if result is not INCOMPLETE:
- for seg in segments:
- self.receiver.completed(seg)
- # XXX: don't forget to obey sync for manual completion as well
- if hdr.sync:
- self.channel.session_completed(self.receiver._completed)
+ self.receiver.completed(cmd)
+ # XXX: don't forget to obey sync for manual completion as well
+ if cmd.sync:
+ self.channel.session_completed(self.receiver._completed)
- def send(self, seg):
- self.sender.send(seg)
-
- def __str__(self):
- return '<Session: %s, %s>' % (self.name, self.channel)
+ def send(self, cmd):
+ self.sender.send(cmd)
def __repr__(self):
- return str(self)
+ return '<Session: %s, %s>' % (self.name, self.channel)
class Receiver:
def __init__(self, session):
self.session = session
self.next_id = None
- self.next_offset = None
self._completed = RangedSet()
- def received(self, seg):
- if self.next_id == None or self.next_offset == None:
+ def received(self, cmd):
+ if self.next_id == None:
raise Exception("todo")
- seg.id = self.next_id
- seg.offset = self.next_offset
- if seg.last:
- self.next_id += 1
- self.next_offset = 0
- else:
- self.next_offset += len(seg.payload)
+ cmd.id = self.next_id
+ self.next_id += 1
- def completed(self, seg):
- if seg.id == None:
- raise ValueError("cannot complete unidentified segment")
- if seg.last:
- self._completed.add(seg.id)
+ def completed(self, cmd):
+ if cmd.id == None:
+ raise ValueError("cannot complete unidentified command")
+ self._completed.add(cmd.id)
def known_completed(self, commands):
completed = RangedSet()
@@ -294,30 +241,24 @@ class Sender:
def __init__(self, session):
self.session = session
self.next_id = serial(0)
- self.next_offset = 0
- self.segments = []
+ self.commands = []
self._completed = RangedSet()
- def send(self, seg):
- seg.id = self.next_id
- seg.offset = self.next_offset
- if seg.last:
- self.next_id += 1
- self.next_offset = 0
- else:
- self.next_offset += len(seg.payload)
- self.segments.append(seg)
+ def send(self, cmd):
+ cmd.id = self.next_id
+ self.next_id += 1
if self.session.send_id:
self.session.send_id = False
- self.session.channel.session_command_point(seg.id, seg.offset)
- self.session.channel.connection.write_segment(seg)
+ self.session.channel.session_command_point(cmd.id, 0)
+ self.commands.append(cmd)
+ self.session.channel.connection.write_op(cmd)
def completed(self, commands):
idx = 0
- while idx < len(self.segments):
- seg = self.segments[idx]
- if seg.id in commands:
- del self.segments[idx]
+ while idx < len(self.commands):
+ cmd = self.commands[idx]
+ if cmd.id in commands:
+ del self.commands[idx]
else:
idx += 1
for range in commands.ranges:
@@ -332,7 +273,7 @@ class Incoming(Queue):
def start(self):
self.session.message_set_flow_mode(self.destination, self.session.flow_mode.credit)
- for unit in self.session.credit_unit.values():
+ for unit in self.session.credit_unit.VALUES:
self.session.message_flow(self.destination, unit, 0xFFFFFFFFL)
def stop(self):
@@ -356,9 +297,9 @@ class Delegate:
class Client(Delegate):
- def message_transfer(self, cmd, headers, body):
- m = Message(body)
- m.headers = headers
+ def message_transfer(self, cmd):
+ m = Message(cmd.payload)
+ m.headers = cmd.headers
m.id = cmd.id
messages = self.session.incoming(cmd.destination)
messages.put(m)
diff --git a/qpid/python/qpid/spec.py b/qpid/python/qpid/spec.py
index cd76c70c5c..e9bfef1fa6 100644
--- a/qpid/python/qpid/spec.py
+++ b/qpid/python/qpid/spec.py
@@ -29,7 +29,7 @@ class so that the generated code can be reused in a variety of
situations.
"""
-import os, mllib, spec08, spec010
+import os, mllib, spec08
def default():
try:
@@ -54,7 +54,7 @@ def load(specfile, *errata):
minor = doc["amqp/@minor"]
if major == "0" and minor == "10":
- return spec010.load(specfile, *errata)
+ return None
else:
return spec08.load(specfile, *errata)
diff --git a/qpid/python/qpid/spec010.py b/qpid/python/qpid/spec010.py
deleted file mode 100644
index eabc8e2983..0000000000
--- a/qpid/python/qpid/spec010.py
+++ /dev/null
@@ -1,708 +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.
-#
-
-import os, cPickle, datatypes, datetime
-from codec010 import StringCodec
-from util import mtime, fill
-
-class Node:
-
- def __init__(self, children):
- self.children = children
- self.named = {}
- self.docs = []
- self.rules = []
-
- def register(self):
- for ch in self.children:
- ch.register(self)
-
- def resolve(self):
- for ch in self.children:
- ch.resolve()
-
- def __getitem__(self, name):
- path = name.split(".", 1)
- nd = self.named
- for step in path:
- nd = nd[step]
- return nd
-
- def __iter__(self):
- return iter(self.children)
-
-class Anonymous:
-
- def __init__(self, children):
- self.children = children
-
- def register(self, node):
- for ch in self.children:
- ch.register(node)
-
- def resolve(self):
- for ch in self.children:
- ch.resolve()
-
-class Named:
-
- def __init__(self, name):
- self.name = name
- self.qname = None
-
- def register(self, node):
- self.spec = node.spec
- self.klass = node.klass
- node.named[self.name] = self
- if node.qname:
- self.qname = "%s.%s" % (node.qname, self.name)
- else:
- self.qname = self.name
-
- def __str__(self):
- return self.qname
-
- def __repr__(self):
- return str(self)
-
-class Lookup:
-
- def lookup(self, name):
- value = None
- if self.klass:
- try:
- value = self.klass[name]
- except KeyError:
- pass
- if not value:
- value = self.spec[name]
- return value
-
-class Coded:
-
- def __init__(self, code):
- self.code = code
-
-class Constant(Named, Node):
-
- def __init__(self, name, value, children):
- Named.__init__(self, name)
- Node.__init__(self, children)
- self.value = value
-
- def register(self, node):
- Named.register(self, node)
- node.constants.append(self)
- Node.register(self)
-
-class Type(Named, Node):
-
- def __init__(self, name, children):
- Named.__init__(self, name)
- Node.__init__(self, children)
-
- def is_present(self, value):
- return value != None
-
- def register(self, node):
- Named.register(self, node)
- Node.register(self)
-
-class Primitive(Coded, Type):
-
- def __init__(self, name, code, fixed, variable, children):
- Coded.__init__(self, code)
- Type.__init__(self, name, children)
- self.fixed = fixed
- self.variable = variable
-
- def register(self, node):
- Type.register(self, node)
- if self.code is not None:
- self.spec.types[self.code] = self
-
- def is_present(self, value):
- if self.fixed == 0:
- return value
- else:
- return Type.is_present(self, value)
-
- def encode(self, codec, value):
- getattr(codec, "write_%s" % self.name)(value)
-
- def decode(self, codec):
- return getattr(codec, "read_%s" % self.name)()
-
-class Domain(Type, Lookup):
-
- def __init__(self, name, type, children):
- Type.__init__(self, name, children)
- self.type = type
- self.choices = {}
-
- def resolve(self):
- self.type = self.lookup(self.type)
- Node.resolve(self)
-
- def encode(self, codec, value):
- self.type.encode(codec, value)
-
- def decode(self, codec):
- return self.type.decode(codec)
-
-class Enum:
-
- def __init__(self, name):
- self.name = name
- self._names = ()
- self._values = ()
-
- def values(self):
- return self._values
-
- def __repr__(self):
- return "%s(%s)" % (self.name, ", ".join(self._names))
-
-class Choice(Named, Node):
-
- def __init__(self, name, value, children):
- Named.__init__(self, name)
- Node.__init__(self, children)
- self.value = value
-
- def register(self, node):
- Named.register(self, node)
- node.choices[self.value] = self
- Node.register(self)
- try:
- enum = node.spec.enums[node.name]
- except KeyError:
- enum = Enum(node.name)
- node.spec.enums[node.name] = enum
- setattr(enum, self.name, self.value)
- enum._names += (self.name,)
- enum._values += (self.value,)
-
-class Composite(Type, Coded):
-
- def __init__(self, name, label, code, size, pack, children):
- Coded.__init__(self, code)
- Type.__init__(self, name, children)
- self.label = label
- self.fields = []
- self.size = size
- self.pack = pack
-
- def new(self, args, kwargs):
- return datatypes.Struct(self, *args, **kwargs)
-
- def decode(self, codec):
- codec.read_size(self.size)
- if self.code is not None:
- code = codec.read_uint16()
- assert self.code == code
- return datatypes.Struct(self, **self.decode_fields(codec))
-
- def decode_fields(self, codec):
- flags = 0
- for i in range(self.pack):
- flags |= (codec.read_uint8() << 8*i)
-
- result = {}
-
- for i in range(len(self.fields)):
- f = self.fields[i]
- if flags & (0x1 << i):
- result[f.name] = f.type.decode(codec)
- else:
- result[f.name] = None
- return result
-
- def encode(self, codec, value):
- sc = StringCodec(self.spec)
- if self.code is not None:
- sc.write_uint16(self.code)
- self.encode_fields(sc, value)
- codec.write_size(self.size, len(sc.encoded))
- codec.write(sc.encoded)
-
- def encode_fields(self, codec, values):
- flags = 0
- for i in range(len(self.fields)):
- f = self.fields[i]
- if f.type.is_present(values[f.name]):
- flags |= (0x1 << i)
- for i in range(self.pack):
- codec.write_uint8((flags >> 8*i) & 0xFF)
- for i in range(len(self.fields)):
- f = self.fields[i]
- if flags & (0x1 << i):
- f.type.encode(codec, values[f.name])
-
- def docstring(self):
- docs = []
- if self.label:
- docs.append(self.label)
- docs += [d.text for d in self.docs]
- s = "\n\n".join([fill(t, 2) for t in docs])
- for f in self.fields:
- fdocs = []
- if f.label:
- fdocs.append(f.label)
- else:
- fdocs.append("")
- fdocs += [d.text for d in f.docs]
- s += "\n\n" + "\n\n".join([fill(fdocs[0], 4, f.name)] +
- [fill(t, 4) for t in fdocs[1:]])
- return s
-
-
-class Field(Named, Node, Lookup):
-
- def __init__(self, name, label, type, children):
- Named.__init__(self, name)
- Node.__init__(self, children)
- self.label = label
- self.type = type
- self.exceptions = []
-
- def default(self):
- return None
-
- def register(self, node):
- Named.register(self, node)
- node.fields.append(self)
- Node.register(self)
-
- def resolve(self):
- self.type = self.lookup(self.type)
- Node.resolve(self)
-
- def __str__(self):
- return "%s: %s" % (self.qname, self.type.qname)
-
-class Struct(Composite):
-
- def register(self, node):
- Composite.register(self, node)
- if self.code is not None:
- self.spec.structs[self.code] = self
- self.spec.structs_by_name[self.name] = self
- self.pyname = self.name
- self.pydoc = self.docstring()
-
- def __str__(self):
- fields = ",\n ".join(["%s: %s" % (f.name, f.type.qname)
- for f in self.fields])
- return "%s {\n %s\n}" % (self.qname, fields)
-
-class Segment:
-
- def __init__(self):
- self.segment_type = None
-
- def register(self, node):
- self.spec = node.spec
- self.klass = node.klass
- node.segments.append(self)
- Node.register(self)
-
-class Instruction(Composite, Segment):
-
- def __init__(self, name, label, code, children):
- Composite.__init__(self, name, label, code, 0, 2, children)
- Segment.__init__(self)
- self.track = None
- self.handlers = []
-
- def __str__(self):
- return "%s(%s)" % (self.qname, ", ".join(["%s: %s" % (f.name, f.type.qname)
- for f in self.fields]))
-
- def register(self, node):
- Composite.register(self, node)
- self.pyname = self.qname.replace(".", "_")
- self.pydoc = self.docstring()
- self.spec.instructions[self.pyname] = self
-
-class Control(Instruction):
-
- def __init__(self, name, code, label, children):
- Instruction.__init__(self, name, code, label, children)
- self.response = None
-
- def register(self, node):
- Instruction.register(self, node)
- node.controls.append(self)
- self.spec.controls[self.code] = self
- self.segment_type = self.spec["segment_type.control"].value
- self.track = self.spec["track.control"].value
-
-class Command(Instruction):
-
- def __init__(self, name, label, code, children):
- Instruction.__init__(self, name, label, code, children)
- self.result = None
- self.exceptions = []
- self.segments = []
-
- def register(self, node):
- Instruction.register(self, node)
- node.commands.append(self)
- self.spec.commands[self.code] = self
- self.segment_type = self.spec["segment_type.command"].value
- self.track = self.spec["track.command"].value
-
-class Header(Segment, Node):
-
- def __init__(self, children):
- Segment.__init__(self)
- Node.__init__(self, children)
- self.entries = []
-
- def register(self, node):
- Segment.register(self, node)
- self.segment_type = self.spec["segment_type.header"].value
- Node.register(self)
-
-class Entry(Lookup):
-
- def __init__(self, type):
- self.type = type
-
- def register(self, node):
- self.spec = node.spec
- self.klass = node.klass
- node.entries.append(self)
-
- def resolve(self):
- self.type = self.lookup(self.type)
-
-class Body(Segment, Node):
-
- def __init__(self, children):
- Segment.__init__(self)
- Node.__init__(self, children)
-
- def register(self, node):
- Segment.register(self, node)
- self.segment_type = self.spec["segment_type.body"].value
- Node.register(self)
-
- def resolve(self): pass
-
-class Class(Named, Coded, Node):
-
- def __init__(self, name, code, children):
- Named.__init__(self, name)
- Coded.__init__(self, code)
- Node.__init__(self, children)
- self.controls = []
- self.commands = []
-
- def register(self, node):
- Named.register(self, node)
- self.klass = self
- node.classes.append(self)
- Node.register(self)
-
-class Doc:
-
- def __init__(self, type, title, text):
- self.type = type
- self.title = title
- self.text = text
-
- def register(self, node):
- node.docs.append(self)
-
- def resolve(self): pass
-
-class Role(Named, Node):
-
- def __init__(self, name, children):
- Named.__init__(self, name)
- Node.__init__(self, children)
-
- def register(self, node):
- Named.register(self, node)
- Node.register(self)
-
-class Rule(Named, Node):
-
- def __init__(self, name, children):
- Named.__init__(self, name)
- Node.__init__(self, children)
-
- def register(self, node):
- Named.register(self, node)
- node.rules.append(self)
- Node.register(self)
-
-class Exception(Named, Node):
-
- def __init__(self, name, error_code, children):
- Named.__init__(self, name)
- Node.__init__(self, children)
- self.error_code = error_code
-
- def register(self, node):
- Named.register(self, node)
- node.exceptions.append(self)
- Node.register(self)
-
-def direct(t):
- return lambda x: t
-
-def map_str(s):
- for c in s:
- if ord(c) >= 0x80:
- return "vbin16"
- return "str16"
-
-class Spec(Node):
-
- ENCODINGS = {
- unicode: direct("str16"),
- str: map_str,
- buffer: direct("vbin32"),
- int: direct("int64"),
- long: direct("int64"),
- float: direct("double"),
- None.__class__: direct("void"),
- list: direct("list"),
- tuple: direct("list"),
- dict: direct("map"),
- datatypes.timestamp: direct("datetime"),
- datetime.datetime: direct("datetime"),
- datatypes.UUID: direct("uuid")
- }
-
- def __init__(self, major, minor, port, children):
- Node.__init__(self, children)
- self.major = major
- self.minor = minor
- self.port = port
- self.constants = []
- self.classes = []
- self.types = {}
- self.qname = None
- self.spec = self
- self.klass = None
- self.instructions = {}
- self.controls = {}
- self.commands = {}
- self.structs = {}
- self.structs_by_name = {}
- self.enums = {}
-
- def encoding(self, obj):
- return self._encoding(obj.__class__, obj)
-
- def _encoding(self, klass, obj):
- if Spec.ENCODINGS.has_key(klass):
- return self.named[Spec.ENCODINGS[klass](obj)]
- for base in klass.__bases__:
- result = self._encoding(base, obj)
- if result != None:
- return result
-
-class Implement:
-
- def __init__(self, handle):
- self.handle = handle
-
- def register(self, node):
- node.handlers.append(self.handle)
-
- def resolve(self): pass
-
-class Response(Node):
-
- def __init__(self, name, children):
- Node.__init__(self, children)
- self.name = name
-
- def register(self, node):
- Node.register(self)
-
-class Result(Node, Lookup):
-
- def __init__(self, type, children):
- self.type = type
- Node.__init__(self, children)
-
- def register(self, node):
- node.result = self
- self.qname = node.qname
- self.klass = node.klass
- self.spec = node.spec
- Node.register(self)
-
- def resolve(self):
- self.type = self.lookup(self.type)
- Node.resolve(self)
-
-import mllib
-
-def num(s):
- if s: return int(s, 0)
-
-REPLACE = {" ": "_", "-": "_"}
-KEYWORDS = {"global": "global_",
- "return": "return_"}
-
-def id(name):
- name = str(name)
- for key, val in REPLACE.items():
- name = name.replace(key, val)
- try:
- name = KEYWORDS[name]
- except KeyError:
- pass
- return name
-
-class Loader:
-
- def __init__(self):
- self.class_code = 0
-
- def code(self, nd):
- c = num(nd["@code"])
- if c is None:
- return None
- else:
- return c | (self.class_code << 8)
-
- def list(self, q):
- result = []
- for nd in q:
- result.append(nd.dispatch(self))
- return result
-
- def children(self, n):
- return self.list(n.query["#tag"])
-
- def data(self, d):
- return d.data
-
- def do_amqp(self, a):
- return Spec(num(a["@major"]), num(a["@minor"]), num(a["@port"]),
- self.children(a))
-
- def do_type(self, t):
- return Primitive(id(t["@name"]), self.code(t), num(t["@fixed-width"]),
- num(t["@variable-width"]), self.children(t))
-
- def do_constant(self, c):
- return Constant(id(c["@name"]), num(c["@value"]), self.children(c))
-
- def do_domain(self, d):
- return Domain(id(d["@name"]), id(d["@type"]), self.children(d))
-
- def do_enum(self, e):
- return Anonymous(self.children(e))
-
- def do_choice(self, c):
- return Choice(id(c["@name"]), num(c["@value"]), self.children(c))
-
- def do_class(self, c):
- code = num(c["@code"])
- self.class_code = code
- children = self.children(c)
- children += self.list(c.query["command/result/struct"])
- self.class_code = 0
- return Class(id(c["@name"]), code, children)
-
- def do_doc(self, doc):
- text = reduce(lambda x, y: x + y, self.list(doc.children))
- return Doc(doc["@type"], doc["@title"], text)
-
- def do_xref(self, x):
- return x["@ref"]
-
- def do_role(self, r):
- return Role(id(r["@name"]), self.children(r))
-
- def do_control(self, c):
- return Control(id(c["@name"]), c["@label"], self.code(c), self.children(c))
-
- def do_rule(self, r):
- return Rule(id(r["@name"]), self.children(r))
-
- def do_implement(self, i):
- return Implement(id(i["@handle"]))
-
- def do_response(self, r):
- return Response(id(r["@name"]), self.children(r))
-
- def do_field(self, f):
- return Field(id(f["@name"]), f["@label"], id(f["@type"]), self.children(f))
-
- def do_struct(self, s):
- return Struct(id(s["@name"]), s["@label"], self.code(s), num(s["@size"]),
- num(s["@pack"]), self.children(s))
-
- def do_command(self, c):
- return Command(id(c["@name"]), c["@label"], self.code(c), self.children(c))
-
- def do_segments(self, s):
- return Anonymous(self.children(s))
-
- def do_header(self, h):
- return Header(self.children(h))
-
- def do_entry(self, e):
- return Entry(id(e["@type"]))
-
- def do_body(self, b):
- return Body(self.children(b))
-
- def do_result(self, r):
- type = r["@type"]
- if not type:
- type = r["struct/@name"]
- return Result(id(type), self.list(r.query["#tag", lambda x: x.name != "struct"]))
-
- def do_exception(self, e):
- return Exception(id(e["@name"]), id(e["@error-code"]), self.children(e))
-
-def load(xml):
- fname = xml + ".pcl"
-
- if os.path.exists(fname) and mtime(fname) > mtime(__file__):
- file = open(fname, "r")
- s = cPickle.load(file)
- file.close()
- else:
- doc = mllib.xml_parse(xml)
- s = doc["amqp"].dispatch(Loader())
- s.register()
- s.resolve()
-
- try:
- file = open(fname, "w")
- except IOError:
- file = None
-
- if file:
- cPickle.dump(s, file)
- file.close()
-
- return s
diff --git a/qpid/python/qpid/testlib.py b/qpid/python/qpid/testlib.py
index 12b2781561..1439b892ea 100644
--- a/qpid/python/qpid/testlib.py
+++ b/qpid/python/qpid/testlib.py
@@ -21,239 +21,13 @@
# Support library for qpid python tests.
#
-import sys, re, unittest, os, random, logging, traceback
-import qpid.client, qpid.spec, qmf.console
+import unittest, traceback, socket
+import qpid.client, qmf.console
import Queue
-from fnmatch import fnmatch
-from getopt import getopt, GetoptError
from qpid.content import Content
from qpid.message import Message
-
-#0-10 support
-from qpid.connection import Connection
-from qpid.spec010 import load
-from qpid.util import connect, ssl, URL
-
-def findmodules(root):
- """Find potential python modules under directory root"""
- found = []
- for dirpath, subdirs, files in os.walk(root):
- modpath = dirpath.replace(os.sep, '.')
- if not re.match(r'\.svn$', dirpath): # Avoid SVN directories
- for f in files:
- match = re.match(r'(.+)\.py$', f)
- if match and f != '__init__.py':
- found.append('.'.join([modpath, match.group(1)]))
- return found
-
-def default(value, default):
- if (value == None): return default
- else: return value
-
-class TestRunner:
-
- SPEC_FOLDER = "../specs"
- qpidd = os.getenv("QPIDD")
-
- """Runs unit tests.
-
- Parses command line arguments, provides utility functions for tests,
- runs the selected test suite.
- """
-
- def _die(self, message = None):
- if message: print message
- print """
-run-tests [options] [test*]
-The name of a test is package.module.ClassName.testMethod
-Options:
- -?/-h/--help : this message
- -s/--spec <spec.xml> : URL of AMQP XML specification or one of these abbreviations:
- 0-8 - use the default 0-8 specification.
- 0-9 - use the default 0-9 specification.
- 0-10-errata - use the 0-10 specification with qpid errata.
- -e/--errata <errata.xml> : file containing amqp XML errata
- -b/--broker [amqps://][<user>[/<password>]@]<host>[:<port>] : broker to connect to
- -B/--start-broker <broker-args> : start a local broker using broker-args; set QPIDD
- env to point to broker executable. broker-args will be
- prepended with "--daemon --port=0"
- -v/--verbose : verbose - lists tests as they are run.
- -d/--debug : enable debug logging.
- -i/--ignore <test> : ignore the named test.
- -I/--ignore-file : file containing patterns to ignore.
- -S/--skip-self-test : skips the client self tests in the 'tests folder'
- -F/--spec-folder : folder that contains the specs to be loaded
- """
- sys.exit(1)
-
- def startBroker(self, brokerArgs):
- """Start a single broker daemon"""
- if TestRunner.qpidd == None:
- self._die("QPIDD environment var must point to qpidd when using -B/--start-broker")
- cmd = "%s --daemon --port=0 %s" % (TestRunner.qpidd, brokerArgs)
- portStr = os.popen(cmd).read()
- if len(portStr) == 0:
- self._die("%s failed to start" % TestRunner.qpidd)
- port = int(portStr)
- pid = int(os.popen("%s -p %d -c" % (TestRunner.qpidd, port)).read())
- print "Started broker: pid=%d, port=%d" % (pid, port)
- self.brokerTuple = (pid, port)
- self.setBroker("localhost:%d" % port)
-
- def stopBroker(self):
- """Stop the broker using qpidd -q"""
- if self.brokerTuple:
- ret = os.spawnl(os.P_WAIT, TestRunner.qpidd, TestRunner.qpidd, "--port=%d" % self.brokerTuple[1], "-q")
- if ret != 0:
- self._die("stop_node(): pid=%d port=%d: qpidd -q returned %d" % (self.brokerTuple[0], self.brokerTuple[1], ret))
- print "Stopped broker: pid=%d, port=%d" % self.brokerTuple
-
- def killBroker(self):
- """Kill the broker using kill -9 (SIGTERM)"""
- if self.brokerTuple:
- os.kill(self.brokerTuple[0], signal.SIGTERM)
- print "Killed broker: pid=%d, port=%d" % self.brokerTuple
-
- def setBroker(self, broker):
- try:
- self.url = URL(broker)
- except ValueError:
- self._die("'%s' is not a valid broker" % (broker))
- self.user = default(self.url.user, "guest")
- self.password = default(self.url.password, "guest")
- self.host = self.url.host
- if self.url.scheme == URL.AMQPS:
- self.ssl = True
- default_port = 5671
- else:
- self.ssl = False
- default_port = 5672
- self.port = default(self.url.port, default_port)
-
- def ignoreFile(self, filename):
- f = file(filename)
- for line in f.readlines(): self.ignore.append(line.strip())
- f.close()
-
- def use08spec(self):
- "True if we are running with the old 0-8 spec."
- # NB: AMQP 0-8 identifies itself as 8-0 for historical reasons.
- return self.spec.major==8 and self.spec.minor==0
-
- def use09spec(self):
- "True if we are running with the 0-9 (non-wip) spec."
- return self.spec.major==0 and self.spec.minor==9
-
- def _parseargs(self, args):
- # Defaults
- self.setBroker("localhost")
- self.verbose = 1
- self.ignore = []
- self.specfile = "0-8"
- self.errata = []
- self.skip_self_test = False
-
- try:
- opts, self.tests = getopt(args, "s:e:b:B:h?dvSi:I:F:",
- ["help", "spec", "errata=", "broker=",
- "start-broker=", "verbose", "skip-self-test", "ignore",
- "ignore-file", "spec-folder"])
- except GetoptError, e:
- self._die(str(e))
- # check for mutually exclusive options
- if "-B" in opts or "--start-broker" in opts:
- if "-b" in opts or "--broker" in opts:
- self._die("Cannot use -B/--start-broker and -b/broker options together")
- for opt, value in opts:
- if opt in ("-?", "-h", "--help"): self._die()
- if opt in ("-s", "--spec"): self.specfile = value
- if opt in ("-e", "--errata"): self.errata.append(value)
- if opt in ("-b", "--broker"): self.setBroker(value)
- if opt in ("-B", "--start-broker"): self.startBroker(value)
- if opt in ("-v", "--verbose"): self.verbose = 2
- if opt in ("-d", "--debug"): logging.basicConfig(level=logging.DEBUG)
- if opt in ("-i", "--ignore"): self.ignore.append(value)
- if opt in ("-I", "--ignore-file"): self.ignoreFile(value)
- if opt in ("-S", "--skip-self-test"): self.skip_self_test = True
- if opt in ("-F", "--spec-folder"): TestRunner.SPEC_FOLDER = value
-
- # Abbreviations for default settings.
- if (self.specfile == "0-10"):
- self.spec = load(self.get_spec_file("amqp.0-10.xml"))
- elif (self.specfile == "0-10-errata"):
- self.spec = load(self.get_spec_file("amqp.0-10-qpid-errata.xml"))
- else:
- if (self.specfile == "0-8"):
- self.specfile = self.get_spec_file("amqp.0-8.xml")
- elif (self.specfile == "0-9"):
- self.specfile = self.get_spec_file("amqp.0-9.xml")
- self.errata.append(self.get_spec_file("amqp-errata.0-9.xml"))
-
- if (self.specfile == None):
- self._die("No XML specification provided")
- print "Using specification from:", self.specfile
-
- self.spec = qpid.spec.load(self.specfile, *self.errata)
-
- if len(self.tests) == 0:
- if not self.skip_self_test:
- self.tests=findmodules("tests")
- if self.use08spec() or self.use09spec():
- self.tests+=findmodules("tests_0-8")
- elif (self.spec.major == 99 and self.spec.minor == 0):
- self.tests+=findmodules("tests_0-10_preview")
- elif (self.spec.major == 0 and self.spec.minor == 10):
- self.tests+=findmodules("tests_0-10")
-
- def testSuite(self):
- class IgnoringTestSuite(unittest.TestSuite):
- def addTest(self, test):
- if isinstance(test, unittest.TestCase):
- for pattern in testrunner.ignore:
- if fnmatch(test.id(), pattern):
- return
- unittest.TestSuite.addTest(self, test)
-
- # Use our IgnoringTestSuite in the test loader.
- unittest.TestLoader.suiteClass = IgnoringTestSuite
- return unittest.defaultTestLoader.loadTestsFromNames(self.tests)
-
- def run(self, args=sys.argv[1:]):
- self.brokerTuple = None
- self._parseargs(args)
- runner = unittest.TextTestRunner(descriptions=False,
- verbosity=self.verbose)
- result = runner.run(self.testSuite())
-
- if (self.ignore):
- print "======================================="
- print "NOTE: the following tests were ignored:"
- for t in self.ignore: print t
- print "======================================="
-
- self.stopBroker()
- return result.wasSuccessful()
-
- def connect(self, host=None, port=None, spec=None, user=None, password=None, tune_params=None):
- """Connect to the broker, returns a qpid.client.Client"""
- host = host or self.host
- port = port or self.port
- spec = spec or self.spec
- user = user or self.user
- password = password or self.password
- client = qpid.client.Client(host, port, spec)
- if self.use08spec():
- client.start({"LOGIN": user, "PASSWORD": password}, tune_params=tune_params)
- else:
- client.start("\x00" + user + "\x00" + password, mechanism="PLAIN", tune_params=tune_params)
- return client
-
- def get_spec_file(self, fname):
- return TestRunner.SPEC_FOLDER + os.sep + fname
-
-# Global instance for tests to call connect.
-testrunner = TestRunner()
-
+from qpid.harness import Skipped
+from qpid.exceptions import VersionError
class TestBase(unittest.TestCase):
"""Base class for Qpid test cases.
@@ -267,6 +41,9 @@ class TestBase(unittest.TestCase):
resources to clean up later.
"""
+ def configure(self, config):
+ self.config = config
+
def setUp(self):
self.queues = []
self.exchanges = []
@@ -293,9 +70,26 @@ class TestBase(unittest.TestCase):
else:
self.client.close()
- def connect(self, *args, **keys):
+ def connect(self, host=None, port=None, user=None, password=None, tune_params=None):
"""Create a new connction, return the Client object"""
- return testrunner.connect(*args, **keys)
+ host = host or self.config.broker.host
+ port = port or self.config.broker.port or 5672
+ user = user or "guest"
+ password = password or "guest"
+ client = qpid.client.Client(host, port)
+ try:
+ if client.spec.major == 8 and client.spec.minor == 0:
+ client.start({"LOGIN": user, "PASSWORD": password}, tune_params=tune_params)
+ else:
+ client.start("\x00" + user + "\x00" + password, mechanism="PLAIN", tune_params=tune_params)
+ except qpid.client.Closed, e:
+ if isinstance(e.args[0], VersionError):
+ raise Skipped(e.args[0])
+ else:
+ raise e
+ except socket.error, e:
+ raise Skipped(e)
+ return client
def queue_declare(self, channel=None, *args, **keys):
channel = channel or self.channel
@@ -319,17 +113,8 @@ class TestBase(unittest.TestCase):
def consume(self, queueName):
"""Consume from named queue returns the Queue object."""
- if testrunner.use08spec() or testrunner.use09spec():
- reply = self.channel.basic_consume(queue=queueName, no_ack=True)
- return self.client.queue(reply.consumer_tag)
- else:
- if not "uniqueTag" in dir(self): self.uniqueTag = 1
- else: self.uniqueTag += 1
- consumer_tag = "tag" + str(self.uniqueTag)
- self.channel.message_subscribe(queue=queueName, destination=consumer_tag)
- self.channel.message_flow(destination=consumer_tag, unit=0, value=0xFFFFFFFFL)
- self.channel.message_flow(destination=consumer_tag, unit=1, value=0xFFFFFFFFL)
- return self.client.queue(consumer_tag)
+ reply = self.channel.basic_consume(queue=queueName, no_ack=True)
+ return self.client.queue(reply.consumer_tag)
def subscribe(self, channel=None, **keys):
channel = channel or self.channel
@@ -350,24 +135,14 @@ class TestBase(unittest.TestCase):
Publish to exchange and assert queue.get() returns the same message.
"""
body = self.uniqueString()
- if testrunner.use08spec() or testrunner.use09spec():
- self.channel.basic_publish(
- exchange=exchange,
- content=Content(body, properties=properties),
- routing_key=routing_key)
- else:
- self.channel.message_transfer(
- destination=exchange,
- content=Content(body, properties={'application_headers':properties,'routing_key':routing_key}))
+ self.channel.basic_publish(
+ exchange=exchange,
+ content=Content(body, properties=properties),
+ routing_key=routing_key)
msg = queue.get(timeout=1)
- if testrunner.use08spec() or testrunner.use09spec():
- self.assertEqual(body, msg.content.body)
- if (properties):
- self.assertEqual(properties, msg.content.properties)
- else:
- self.assertEqual(body, msg.content.body)
- if (properties):
- self.assertEqual(properties, msg.content['application_headers'])
+ self.assertEqual(body, msg.content.body)
+ if (properties):
+ self.assertEqual(properties, msg.content.properties)
def assertPublishConsume(self, queue="", exchange="", routing_key="", properties=None):
"""
@@ -394,11 +169,20 @@ class TestBase(unittest.TestCase):
self.assertEqual("close", message.method.name)
self.assertEqual(expectedCode, message.reply_code)
+#0-10 support
+from qpid.connection import Connection
+from qpid.util import connect, ssl, URL
+
class TestBase010(unittest.TestCase):
"""
Base class for Qpid test cases. using the final 0-10 spec
"""
+ def configure(self, config):
+ self.config = config
+ self.broker = config.broker
+ self.defines = self.config.defines
+
def setUp(self):
self.conn = self.connect()
self.session = self.conn.session("test-session", timeout=10)
@@ -406,15 +190,26 @@ class TestBase010(unittest.TestCase):
def startQmf(self, handler=None):
self.qmf = qmf.console.Session(handler)
- self.qmf_broker = self.qmf.addBroker(str(testrunner.url))
+ self.qmf_broker = self.qmf.addBroker(str(self.broker))
def connect(self, host=None, port=None):
- sock = connect(host or testrunner.host, port or testrunner.port)
- if testrunner.url.scheme == URL.AMQPS:
+ url = self.broker
+ if url.scheme == URL.AMQPS:
+ default_port = 5671
+ else:
+ default_port = 5672
+ try:
+ sock = connect(host or url.host, port or url.port or default_port)
+ except socket.error, e:
+ raise Skipped(e)
+ if url.scheme == URL.AMQPS:
sock = ssl(sock)
- conn = Connection(sock, username=testrunner.user,
- password=testrunner.password)
- conn.start(timeout=10)
+ conn = Connection(sock, username=url.user or "guest",
+ password=url.password or "guest")
+ try:
+ conn.start(timeout=10)
+ except VersionError, e:
+ raise Skipped(e)
return conn
def tearDown(self):
diff --git a/qpid/python/qpid/tests/framing.py b/qpid/python/qpid/tests/framing.py
index 4cd596b583..0b33df8b9a 100644
--- a/qpid/python/qpid/tests/framing.py
+++ b/qpid/python/qpid/tests/framing.py
@@ -40,6 +40,50 @@ class Base(Test):
assert seg1.channel == seg2.channel, "expected: %r, got %r" % (seg1, seg2)
assert seg1.payload == seg2.payload, "expected: %r, got %r" % (seg1, seg2)
+ def cmp_list(self, l1, l2):
+ if l1 is None:
+ assert l2 is None
+ return
+
+ assert len(l1) == len(l2)
+ for v1, v2 in zip(l1, l2):
+ if isinstance(v1, Compound):
+ self.cmp_ops(v1, v2)
+ else:
+ assert v1 == v2
+
+ def cmp_ops(self, op1, op2):
+ if op1 is None:
+ assert op2 is None
+ return
+
+ assert op1.__class__ == op2.__class__
+ cls = op1.__class__
+ assert op1.NAME == op2.NAME
+ assert op1.CODE == op2.CODE
+ assert op1.FIELDS == op2.FIELDS
+ for f in cls.FIELDS:
+ v1 = getattr(op1, f.name)
+ v2 = getattr(op2, f.name)
+ if COMPOUND.has_key(f.type) or f.type == "struct32":
+ self.cmp_ops(v1, v2)
+ elif f.type in ("list", "array"):
+ self.cmp_list(v1, v2)
+ else:
+ assert v1 == v2, "expected: %r, got %r" % (v1, v2)
+
+ if issubclass(cls, Command) or issubclass(cls, Control):
+ assert op1.channel == op2.channel
+
+ if issubclass(cls, Command):
+ assert op1.sync == op2.sync, "expected: %r, got %r" % (op1.sync, op2.sync)
+ assert (op1.headers is None and op2.headers is None) or \
+ (op1.headers is not None and op2.headers is not None)
+ if op1.headers is not None:
+ assert len(op1.headers) == len(op2.headers)
+ for h1, h2 in zip(op1.headers, op2.headers):
+ self.cmp_ops(h1, h2)
+
class FrameTest(Base):
def enc_dec(self, frames, encoded=None):
@@ -171,3 +215,75 @@ class SegmentTest(Base):
for i in range(0, 8, 2)]
self.enc_dec([Segment(False, False, 0, 0, 0, "abcdefgh")], frames, ilvd, max_payload=2)
+
+from qpid.ops import *
+
+class OpTest(Base):
+
+ def enc_dec(self, ops):
+ enc = OpEncoder()
+ dec = OpDecoder()
+ enc.write(*ops)
+ segs = enc.read()
+ dec.write(*segs)
+ dops = dec.read()
+ assert len(ops) == len(dops)
+ for op1, op2 in zip(ops, dops):
+ self.cmp_ops(op1, op2)
+
+ def testEmtpyMT(self):
+ self.enc_dec([MessageTransfer()])
+
+ def testEmptyMTSync(self):
+ self.enc_dec([MessageTransfer(sync=True)])
+
+ def testMT(self):
+ self.enc_dec([MessageTransfer(destination="asdf")])
+
+ def testSyncMT(self):
+ self.enc_dec([MessageTransfer(destination="asdf", sync=True)])
+
+ def testEmptyPayloadMT(self):
+ self.enc_dec([MessageTransfer(payload="")])
+
+ def testPayloadMT(self):
+ self.enc_dec([MessageTransfer(payload="test payload")])
+
+ def testHeadersEmptyPayloadMT(self):
+ self.enc_dec([MessageTransfer(headers=[DeliveryProperties()])])
+
+ def testHeadersPayloadMT(self):
+ self.enc_dec([MessageTransfer(headers=[DeliveryProperties()], payload="test payload")])
+
+ def testMultiHeadersEmptyPayloadMT(self):
+ self.enc_dec([MessageTransfer(headers=[DeliveryProperties(), MessageProperties()])])
+
+ def testMultiHeadersPayloadMT(self):
+ self.enc_dec([MessageTransfer(headers=[MessageProperties(), DeliveryProperties()], payload="test payload")])
+
+ def testContentTypeHeadersPayloadMT(self):
+ self.enc_dec([MessageTransfer(headers=[MessageProperties(content_type="text/plain")], payload="test payload")])
+
+ def testMulti(self):
+ self.enc_dec([MessageTransfer(),
+ MessageTransfer(sync=True),
+ MessageTransfer(destination="one"),
+ MessageTransfer(destination="two", sync=True),
+ MessageTransfer(destination="three", payload="test payload")])
+
+ def testControl(self):
+ self.enc_dec([SessionAttach(name="asdf")])
+
+ def testMixed(self):
+ self.enc_dec([SessionAttach(name="fdsa"), MessageTransfer(destination="test")])
+
+ def testChannel(self):
+ self.enc_dec([SessionAttach(name="asdf", channel=3), MessageTransfer(destination="test", channel=1)])
+
+ def testCompound(self):
+ self.enc_dec([MessageTransfer(headers=[MessageProperties(reply_to=ReplyTo(exchange="exch", routing_key="rk"))])])
+
+ def testListCompound(self):
+ self.enc_dec([ExecutionResult(value=RecoverResult(in_doubt=[Xid(global_id="one"),
+ Xid(global_id="two"),
+ Xid(global_id="three")]))])
diff --git a/qpid/python/qpid/tests/messaging.py b/qpid/python/qpid/tests/messaging.py
index 8a142d6c96..7706ebbabe 100644
--- a/qpid/python/qpid/tests/messaging.py
+++ b/qpid/python/qpid/tests/messaging.py
@@ -22,6 +22,7 @@
import time
from qpid.tests import Test
+from qpid.harness import Skipped
from qpid.messaging import Connection, ConnectError, Disconnected, Empty, Message, UNLIMITED, uuid4
from Queue import Queue, Empty as QueueEmpty
@@ -42,7 +43,10 @@ class Base(Test):
def setup(self):
self.test_id = uuid4()
self.broker = self.config.broker
- self.conn = self.setup_connection()
+ try:
+ self.conn = self.setup_connection()
+ except ConnectError, e:
+ raise Skipped(e)
self.ssn = self.setup_session()
self.snd = self.setup_sender()
self.rcv = self.setup_receiver()
@@ -65,7 +69,7 @@ class Base(Test):
receiver = ssn.receiver("ping-queue")
msg = receiver.fetch(0)
ssn.acknowledge()
- assert msg.content == content
+ assert msg.content == content, "expected %r, got %r" % (content, msg.content)
def drain(self, rcv, limit=None):
contents = []
diff --git a/qpid/python/qpid_config.py b/qpid/python/qpid_config.py
index 8f987e9962..3cf6b69b7e 100644
--- a/qpid/python/qpid_config.py
+++ b/qpid/python/qpid_config.py
@@ -21,3 +21,5 @@ import os
qpid_home = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
amqp_spec = os.path.join(qpid_home, "specs", "amqp.0-10-qpid-errata.xml")
+amqp_spec_0_8 = os.path.join(qpid_home, "specs", "amqp.0-8.xml")
+amqp_spec_0_9 = os.path.join(qpid_home, "specs", "amqp.0-9.xml")
diff --git a/qpid/python/rule2test b/qpid/python/rule2test
deleted file mode 100755
index 10f151366e..0000000000
--- a/qpid/python/rule2test
+++ /dev/null
@@ -1,108 +0,0 @@
-#!/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.
-#
-
-#
-# Convert rules to tests
-#
-import sys, re, os.path
-from getopt import getopt, GetoptError
-from string import capitalize
-from xml import dom
-from xml.dom.minidom import parse
-
-def camelcase(s):
- """Convert 'string like this' to 'StringLikeThis'"""
- return "".join([capitalize(w) for w in re.split(re.compile("\W*"), s)])
-
-def uncapitalize(s): return s[0].lower()+s[1:]
-
-def ancestors(node):
- "Return iterator of ancestors from top-level element to node"
- def generator(node):
- while node and node.parentNode:
- yield node
- node = node.parentNode
- return reversed(list(generator(node)))
-
-def tagAndName(element):
- nameAttr = element.getAttribute("name");
- if (nameAttr) : return camelcase(nameAttr) + camelcase(element.tagName)
- else: return camelcase(element.tagName)
-
-def nodeText(n):
- """Recursively collect text from all text nodes under n"""
- if n.nodeType == dom.Node.TEXT_NODE:
- return n.data
- if n.childNodes:
- return reduce(lambda t, c: t + nodeText(c), n.childNodes, "")
- return ""
-
-def cleanup(docString, level=8):
- unindent = re.sub("\n[ \t]*", "\n", docString.strip())
- emptyLines = re.sub("\n\n\n", "\n\n", unindent)
- indented = re.sub("\n", "\n"+level*" ", emptyLines)
- return level*" " + indented
-
-def printTest(test, docstring):
- print "class %s(TestBase):" % test
- print ' """'
- print docstring
- print ' """'
- print
- print
-
-def printTests(doc, module):
- """Returns dictionary { classname : [ (methodname, docstring)* ] * }"""
- tests = {}
- rules = doc.getElementsByTagName("rule")
- for r in rules:
- path = list(ancestors(r))
- if module == path[1].getAttribute("name").lower():
- test = "".join(map(tagAndName, path[2:])) + "Tests"
- docstring = cleanup(nodeText(r), 4)
- printTest(test, docstring)
-
-def usage(message=None):
- if message: print >>sys.stderr, message
- print >>sys.stderr, """
-rule2test [options] <amqpclass>
-
-Print test classes for each rule for the amqpclass in amqp.xml.
-
-Options:
- -?/-h/--help : this message
- -s/--spec <spec.xml> : file containing amqp XML spec
-"""
- return 1
-
-def main(argv):
- try: opts, args = getopt(argv[1:], "h?s:", ["help", "spec="])
- except GetoptError, e: return usage(e)
- spec = "../specs/amqp.xml" # Default
- for opt, val in opts:
- if (opt in ("-h", "-?", "--help")): return usage()
- if (opt in ("-s", "--spec")): spec = val
- doc = parse(spec)
- if len(args) == 0: return usage()
- printTests(doc, args[0])
- return 0
-
-if (__name__ == "__main__"): sys.exit(main(sys.argv))
diff --git a/qpid/python/tests/__init__.py b/qpid/python/tests/__init__.py
index 8ad514fc2f..1e495f3af3 100644
--- a/qpid/python/tests/__init__.py
+++ b/qpid/python/tests/__init__.py
@@ -19,12 +19,4 @@
# under the License.
#
-from codec import *
-from queue import *
-from spec import *
-from framer import *
-from assembler import *
-from datatypes import *
-from connection import *
-from spec010 import *
-from codec010 import *
+import codec, queue, datatypes, connection, spec010, codec010
diff --git a/qpid/python/tests/assembler.py b/qpid/python/tests/assembler.py
deleted file mode 100644
index f4e37084b6..0000000000
--- a/qpid/python/tests/assembler.py
+++ /dev/null
@@ -1,78 +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.
-#
-
-from threading import *
-from unittest import TestCase
-from qpid.util import connect, listen
-from qpid.assembler import *
-
-PORT = 1234
-
-class AssemblerTest(TestCase):
-
- def setUp(self):
- started = Event()
- self.running = True
-
- def run():
- running = True
- for s in listen("0.0.0.0", PORT, lambda: self.running, lambda: started.set()):
- asm = Assembler(s)
- try:
- asm.write_header(*asm.read_header()[-2:])
- while True:
- seg = asm.read_segment()
- asm.write_segment(seg)
- except Closed:
- pass
-
- self.server = Thread(target=run)
- self.server.setDaemon(True)
- self.server.start()
-
- started.wait(3)
- assert started.isSet()
-
- def tearDown(self):
- self.running = False
- self.server.join()
-
- def test(self):
- asm = Assembler(connect("0.0.0.0", PORT), max_payload = 1)
- asm.write_header(0, 10)
- asm.write_segment(Segment(True, False, 1, 2, 3, "TEST"))
- asm.write_segment(Segment(False, True, 1, 2, 3, "ING"))
-
- assert asm.read_header() == ("AMQP", 1, 1, 0, 10)
-
- seg = asm.read_segment()
- assert seg.first == True
- assert seg.last == False
- assert seg.type == 1
- assert seg.track == 2
- assert seg.channel == 3
- assert seg.payload == "TEST"
-
- seg = asm.read_segment()
- assert seg.first == False
- assert seg.last == True
- assert seg.type == 1
- assert seg.track == 2
- assert seg.channel == 3
- assert seg.payload == "ING"
diff --git a/qpid/python/tests/codec.py b/qpid/python/tests/codec.py
index 4bd3675af8..9b51b4713c 100644
--- a/qpid/python/tests/codec.py
+++ b/qpid/python/tests/codec.py
@@ -23,7 +23,6 @@ from qpid.codec import Codec
from qpid.spec import load
from cStringIO import StringIO
from qpid.reference import ReferenceId
-from qpid.testlib import testrunner
__doc__ = """
@@ -54,13 +53,8 @@ __doc__ = """
"""
-SPEC = None
-
-def spec():
- global SPEC
- if SPEC == None:
- SPEC = load(testrunner.get_spec_file("amqp.0-8.xml"))
- return SPEC
+from qpid_config import amqp_spec_0_8
+SPEC = load(amqp_spec_0_8)
# --------------------------------------
# --------------------------------------
@@ -76,7 +70,7 @@ class BaseDataTypes(unittest.TestCase):
"""
standard setUp for unitetest (refer unittest documentation for details)
"""
- self.codec = Codec(StringIO(), spec())
+ self.codec = Codec(StringIO(), SPEC)
# ------------------
def tearDown(self):
@@ -507,7 +501,7 @@ def test(type, value):
else:
values = [value]
stream = StringIO()
- codec = Codec(stream, spec())
+ codec = Codec(stream, SPEC)
for v in values:
codec.encode(type, v)
codec.flush()
diff --git a/qpid/python/tests/codec010.py b/qpid/python/tests/codec010.py
index a1f89dc3f4..787ebc146f 100644
--- a/qpid/python/tests/codec010.py
+++ b/qpid/python/tests/codec010.py
@@ -20,21 +20,17 @@
import time
from unittest import TestCase
-from qpid.spec010 import load
from qpid.codec010 import StringCodec
-from qpid.testlib import testrunner
from qpid.datatypes import timestamp, uuid4
+from qpid.ops import PRIMITIVE
class CodecTest(TestCase):
- def setUp(self):
- self.spec = load(testrunner.get_spec_file("amqp.0-10.xml"))
-
def check(self, type, value, compare=True):
- t = self.spec[type]
- sc = StringCodec(self.spec)
- t.encode(sc, value)
- decoded = t.decode(sc)
+ t = PRIMITIVE[type]
+ sc = StringCodec()
+ sc.write_primitive(t, value)
+ decoded = sc.read_primitive(t)
if compare:
assert decoded == value, "%s, %s" % (decoded, value)
return decoded
diff --git a/qpid/python/tests/connection.py b/qpid/python/tests/connection.py
index 19cdad9f97..d340e4e9c1 100644
--- a/qpid/python/tests/connection.py
+++ b/qpid/python/tests/connection.py
@@ -22,10 +22,10 @@ from unittest import TestCase
from qpid.util import connect, listen
from qpid.connection import *
from qpid.datatypes import Message
-from qpid.testlib import testrunner
from qpid.delegates import Server
from qpid.queue import Queue
from qpid.session import Delegate
+from qpid.ops import QueueQueryResult
PORT = 1234
@@ -51,12 +51,12 @@ class TestSession(Delegate):
pass
def queue_query(self, qq):
- return qq._type.result.type.new((qq.queue,), {})
+ return QueueQueryResult(qq.queue)
- def message_transfer(self, cmd, headers, body):
+ def message_transfer(self, cmd):
if cmd.destination == "echo":
- m = Message(body)
- m.headers = headers
+ m = Message(cmd.payload)
+ m.headers = cmd.headers
self.session.message_transfer(cmd.destination, cmd.accept_mode,
cmd.acquire_mode, m)
elif cmd.destination == "abort":
@@ -64,7 +64,7 @@ class TestSession(Delegate):
elif cmd.destination == "heartbeat":
self.session.channel.connection_heartbeat()
else:
- self.queue.put((cmd, headers, body))
+ self.queue.put(cmd)
class ConnectionTest(TestCase):
@@ -134,17 +134,17 @@ class ConnectionTest(TestCase):
ssn.message_transfer(d)
for d in destinations:
- cmd, header, body = self.queue.get(10)
+ cmd = self.queue.get(10)
assert cmd.destination == d
- assert header == None
- assert body == None
+ assert cmd.headers == None
+ assert cmd.payload == None
msg = Message("this is a test")
ssn.message_transfer("four", message=msg)
- cmd, header, body = self.queue.get(10)
+ cmd = self.queue.get(10)
assert cmd.destination == "four"
- assert header == None
- assert body == msg.body
+ assert cmd.headers == None
+ assert cmd.payload == msg.body
qq = ssn.queue_query("asdf")
assert qq.queue == "asdf"
diff --git a/qpid/python/tests/datatypes.py b/qpid/python/tests/datatypes.py
index e9e09094fa..1a60bb4107 100644
--- a/qpid/python/tests/datatypes.py
+++ b/qpid/python/tests/datatypes.py
@@ -18,9 +18,8 @@
#
from unittest import TestCase
-from qpid.testlib import testrunner
-from qpid.spec010 import load
from qpid.datatypes import *
+from qpid.ops import DeliveryProperties, FragmentProperties, MessageProperties
class SerialTest(TestCase):
@@ -176,10 +175,9 @@ class UUIDTest(TestCase):
class MessageTest(TestCase):
def setUp(self):
- self.spec = load(testrunner.get_spec_file("amqp.0-10-qpid-errata.xml"))
- self.mp = Struct(self.spec["message.message_properties"])
- self.dp = Struct(self.spec["message.delivery_properties"])
- self.fp = Struct(self.spec["message.fragment_properties"])
+ self.mp = MessageProperties()
+ self.dp = DeliveryProperties()
+ self.fp = FragmentProperties()
def testHas(self):
m = Message(self.mp, self.dp, self.fp, "body")
@@ -207,7 +205,7 @@ class MessageTest(TestCase):
def testSetReplace(self):
m = Message(self.mp, self.dp, self.fp, "body")
- dp = Struct(self.spec["message.delivery_properties"])
+ dp = DeliveryProperties()
assert m.get("delivery_properties") == self.dp
assert m.get("delivery_properties") != dp
m.set(dp)
diff --git a/qpid/python/tests/framer.py b/qpid/python/tests/framer.py
deleted file mode 100644
index e99166721c..0000000000
--- a/qpid/python/tests/framer.py
+++ /dev/null
@@ -1,95 +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.
-#
-
-from threading import *
-from unittest import TestCase
-from qpid.util import connect, listen
-from qpid.framer import *
-
-PORT = 1234
-
-class FramerTest(TestCase):
-
- def setUp(self):
- self.running = True
- started = Event()
- def run():
- for s in listen("0.0.0.0", PORT, lambda: self.running, lambda: started.set()):
- conn = Framer(s)
- try:
- conn.write_header(*conn.read_header()[-2:])
- while True:
- frame = conn.read_frame()
- conn.write_frame(frame)
- conn.flush()
- except Closed:
- pass
-
- self.server = Thread(target=run)
- self.server.setDaemon(True)
- self.server.start()
-
- started.wait(3)
- assert started.isSet()
-
- def tearDown(self):
- self.running = False
- self.server.join(3)
-
- def test(self):
- c = Framer(connect("0.0.0.0", PORT))
-
- c.write_header(0, 10)
- assert c.read_header() == ("AMQP", 1, 1, 0, 10)
-
- c.write_frame(Frame(FIRST_FRM, 1, 2, 3, "THIS"))
- c.write_frame(Frame(0, 1, 2, 3, "IS"))
- c.write_frame(Frame(0, 1, 2, 3, "A"))
- c.write_frame(Frame(LAST_FRM, 1, 2, 3, "TEST"))
- c.flush()
-
- f = c.read_frame()
- assert f.flags & FIRST_FRM
- assert not (f.flags & LAST_FRM)
- assert f.type == 1
- assert f.track == 2
- assert f.channel == 3
- assert f.payload == "THIS"
-
- f = c.read_frame()
- assert f.flags == 0
- assert f.type == 1
- assert f.track == 2
- assert f.channel == 3
- assert f.payload == "IS"
-
- f = c.read_frame()
- assert f.flags == 0
- assert f.type == 1
- assert f.track == 2
- assert f.channel == 3
- assert f.payload == "A"
-
- f = c.read_frame()
- assert f.flags & LAST_FRM
- assert not (f.flags & FIRST_FRM)
- assert f.type == 1
- assert f.track == 2
- assert f.channel == 3
- assert f.payload == "TEST"
diff --git a/qpid/python/tests/spec.py b/qpid/python/tests/spec.py
deleted file mode 100644
index d5ea1d682a..0000000000
--- a/qpid/python/tests/spec.py
+++ /dev/null
@@ -1,74 +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.
-#
-from unittest import TestCase
-from qpid.spec import load
-from qpid.testlib import testrunner
-
-class SpecTest(TestCase):
-
- def check_load(self, *urls):
- spec = load(*map(testrunner.get_spec_file, urls))
- qdecl = spec.method("queue_declare")
- assert qdecl != None
- assert not qdecl.content
-
- queue = qdecl.fields.byname["queue"]
- assert queue != None
- assert queue.domain.name == "queue_name"
- assert queue.type == "shortstr"
-
- qdecl_ok = spec.method("queue_declare_ok")
-
- # 0-8 is actually 8-0
- if (spec.major == 8 and spec.minor == 0 or
- spec.major == 0 and spec.minor == 9):
- assert qdecl_ok != None
-
- assert len(qdecl.responses) == 1
- assert qdecl_ok in qdecl.responses
-
- publish = spec.method("basic_publish")
- assert publish != None
- assert publish.content
-
- if (spec.major == 0 and spec.minor == 10):
- assert qdecl_ok == None
- reply_to = spec.domains.byname["reply_to"]
- assert reply_to.type.size == 2
- assert reply_to.type.pack == 2
- assert len(reply_to.type.fields) == 2
-
- qq = spec.method("queue_query")
- assert qq != None
- assert qq.result.size == 4
- assert qq.result.type != None
- args = qq.result.fields.byname["arguments"]
- assert args.type == "table"
-
- def test_load_0_8(self):
- self.check_load("amqp.0-8.xml")
-
- def test_load_0_9(self):
- self.check_load("amqp.0-9.xml")
-
- def test_load_0_9_errata(self):
- self.check_load("amqp.0-9.xml", "amqp-errata.0-9.xml")
-
- def test_load_0_10(self):
- self.check_load("amqp.0-10-preview.xml")
diff --git a/qpid/python/tests/spec010.py b/qpid/python/tests/spec010.py
index df9cb9590a..ac04e1ee02 100644
--- a/qpid/python/tests/spec010.py
+++ b/qpid/python/tests/spec010.py
@@ -19,66 +19,56 @@
import os, tempfile, shutil, stat
from unittest import TestCase
-from qpid.spec010 import load
from qpid.codec010 import Codec, StringCodec
-from qpid.testlib import testrunner
-from qpid.datatypes import Struct
+from qpid.ops import *
class SpecTest(TestCase):
- def setUp(self):
- self.spec = load(testrunner.get_spec_file("amqp.0-10-qpid-errata.xml"))
-
def testSessionHeader(self):
- hdr = self.spec["session.header"]
- sc = StringCodec(self.spec)
- hdr.encode(sc, Struct(hdr, sync=True))
+ sc = StringCodec()
+ sc.write_compound(Header(sync=True))
assert sc.encoded == "\x01\x01"
- sc = StringCodec(self.spec)
- hdr.encode(sc, Struct(hdr, sync=False))
+ sc = StringCodec()
+ sc.write_compound(Header(sync=False))
assert sc.encoded == "\x01\x00"
- def encdec(self, type, value):
- sc = StringCodec(self.spec)
- type.encode(sc, value)
- decoded = type.decode(sc)
+ def encdec(self, value):
+ sc = StringCodec()
+ sc.write_compound(value)
+ decoded = sc.read_compound(value.__class__)
return decoded
def testMessageProperties(self):
- mp = self.spec["message.message_properties"]
- rt = self.spec["message.reply_to"]
-
- props = Struct(mp, content_length=3735928559L,
- reply_to=Struct(rt, exchange="the exchange name",
- routing_key="the routing key"))
- dec = self.encdec(mp, props)
+ props = MessageProperties(content_length=3735928559L,
+ reply_to=ReplyTo(exchange="the exchange name",
+ routing_key="the routing key"))
+ dec = self.encdec(props)
assert props.content_length == dec.content_length
assert props.reply_to.exchange == dec.reply_to.exchange
assert props.reply_to.routing_key == dec.reply_to.routing_key
def testMessageSubscribe(self):
- ms = self.spec["message.subscribe"]
- cmd = Struct(ms, exclusive=True, destination="this is a test")
- dec = self.encdec(self.spec["message.subscribe"], cmd)
+ cmd = MessageSubscribe(exclusive=True, destination="this is a test")
+ dec = self.encdec(cmd)
assert cmd.exclusive == dec.exclusive
assert cmd.destination == dec.destination
def testXid(self):
- xid = self.spec["dtx.xid"]
- sc = StringCodec(self.spec)
- st = Struct(xid, format=0, global_id="gid", branch_id="bid")
- xid.encode(sc, st)
+ sc = StringCodec()
+ xid = Xid(format=0, global_id="gid", branch_id="bid")
+ sc.write_compound(xid)
assert sc.encoded == '\x00\x00\x00\x10\x06\x04\x07\x00\x00\x00\x00\x00\x03gid\x03bid'
- assert xid.decode(sc).__dict__ == st.__dict__
+ dec = sc.read_compound(Xid)
+ assert xid.__dict__ == dec.__dict__
- def testLoadReadOnly(self):
- spec = "amqp.0-10-qpid-errata.xml"
- f = testrunner.get_spec_file(spec)
- dest = tempfile.mkdtemp()
- shutil.copy(f, dest)
- shutil.copy(os.path.join(os.path.dirname(f), "amqp.0-10.dtd"), dest)
- os.chmod(dest, stat.S_IRUSR | stat.S_IXUSR)
- fname = os.path.join(dest, spec)
- load(fname)
- assert not os.path.exists("%s.pcl" % fname)
+# def testLoadReadOnly(self):
+# spec = "amqp.0-10-qpid-errata.xml"
+# f = testrunner.get_spec_file(spec)
+# dest = tempfile.mkdtemp()
+# shutil.copy(f, dest)
+# shutil.copy(os.path.join(os.path.dirname(f), "amqp.0-10.dtd"), dest)
+# os.chmod(dest, stat.S_IRUSR | stat.S_IXUSR)
+# fname = os.path.join(dest, spec)
+# load(fname)
+# assert not os.path.exists("%s.pcl" % fname)
diff --git a/qpid/python/tests_0-10/__init__.py b/qpid/python/tests_0-10/__init__.py
index 1fd7f72357..f9315a6f90 100644
--- a/qpid/python/tests_0-10/__init__.py
+++ b/qpid/python/tests_0-10/__init__.py
@@ -24,6 +24,7 @@ from broker import *
from dtx import *
from example import *
from exchange import *
+from management import *
from message import *
from query import *
from queue import *
diff --git a/qpid/python/tests_0-10/management.py b/qpid/python/tests_0-10/management.py
index 29394c6a7d..5cd0caba40 100644
--- a/qpid/python/tests_0-10/management.py
+++ b/qpid/python/tests_0-10/management.py
@@ -29,7 +29,7 @@ class ManagementTest (TestBase010):
Tests for the management hooks
"""
- def test_broker_connectivity_oldAPI (self):
+ def disabled_test_broker_connectivity_oldAPI (self):
"""
Call the "echo" method on the broker to verify it is alive and talking.
"""
diff --git a/qpid/python/tests_0-10/message.py b/qpid/python/tests_0-10/message.py
index f80eca6363..d40d5e811e 100644
--- a/qpid/python/tests_0-10/message.py
+++ b/qpid/python/tests_0-10/message.py
@@ -477,7 +477,7 @@ class MessageTests(TestBase010):
#send message A
ssn.message_transfer(message=Message(ssn.delivery_properties(routing_key="q"), "A"))
- for unit in ssn.credit_unit.values():
+ for unit in ssn.credit_unit.VALUES:
ssn.message_flow("c", unit, 0xFFFFFFFFL)
q = ssn.incoming("c")
@@ -490,7 +490,7 @@ class MessageTests(TestBase010):
ssn.channel.session_completed(ssn.receiver._completed)
ssn.message_accept(RangedSet(msgA.id))
- for unit in ssn.credit_unit.values():
+ for unit in ssn.credit_unit.VALUES:
ssn.message_flow("c", unit, 0xFFFFFFFFL)
#send message B
diff --git a/qpid/python/tests_0-10/tx.py b/qpid/python/tests_0-10/tx.py
index 463fbcb888..8cdc539a08 100644
--- a/qpid/python/tests_0-10/tx.py
+++ b/qpid/python/tests_0-10/tx.py
@@ -19,7 +19,7 @@
from qpid.client import Client, Closed
from qpid.queue import Empty
from qpid.datatypes import Message, RangedSet
-from qpid.testlib import testrunner, TestBase010
+from qpid.testlib import TestBase010
class TxTests(TestBase010):
"""
diff --git a/qpid/python/tests_0-8/__init__.py b/qpid/python/tests_0-8/__init__.py
index 9a09d2d04f..526f2452f8 100644
--- a/qpid/python/tests_0-8/__init__.py
+++ b/qpid/python/tests_0-8/__init__.py
@@ -18,3 +18,5 @@
# specific language governing permissions and limitations
# under the License.
#
+
+import basic, broker, example, exchange, queue, testlib, tx
diff --git a/qpid/python/tests_0-8/basic.py b/qpid/python/tests_0-8/basic.py
index 95ca0d7287..d5837fc19c 100644
--- a/qpid/python/tests_0-8/basic.py
+++ b/qpid/python/tests_0-8/basic.py
@@ -19,7 +19,7 @@
from qpid.client import Client, Closed
from qpid.queue import Empty
from qpid.content import Content
-from qpid.testlib import testrunner, TestBase
+from qpid.testlib import TestBase
class BasicTests(TestBase):
"""Tests for 'methods' on the amqp basic 'class'"""
@@ -219,10 +219,11 @@ class BasicTests(TestBase):
channel.basic_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four
channel.basic_cancel(consumer_tag=subscription.consumer_tag)
- subscription2 = channel.basic_consume(queue="test-requeue")
- queue2 = self.client.queue(subscription2.consumer_tag)
channel.basic_recover(requeue=True)
+
+ subscription2 = channel.basic_consume(queue="test-requeue")
+ queue2 = self.client.queue(subscription2.consumer_tag)
msg3b = queue2.get(timeout=1)
msg5b = queue2.get(timeout=1)
diff --git a/qpid/python/tests_0-8/broker.py b/qpid/python/tests_0-8/broker.py
index d9ac69c5e3..7f3fe7530e 100644
--- a/qpid/python/tests_0-8/broker.py
+++ b/qpid/python/tests_0-8/broker.py
@@ -19,15 +19,15 @@
from qpid.client import Closed
from qpid.queue import Empty
from qpid.content import Content
-from qpid.testlib import testrunner, TestBase
+from qpid.testlib import TestBase
class BrokerTests(TestBase):
"""Tests for basic Broker functionality"""
- def test_amqp_basic_13(self):
+ def test_ack_and_no_ack(self):
"""
First, this test tries to receive a message with a no-ack
- consumer. Second, this test tries to explicitely receive and
+ consumer. Second, this test tries to explicitly receive and
acknowledge a message with an acknowledging consumer.
"""
ch = self.channel
@@ -40,7 +40,7 @@ class BrokerTests(TestBase):
msg = self.client.queue(ctag).get(timeout = 5)
self.assert_(msg.content.body == body)
- # Acknowleding consumer
+ # Acknowledging consumer
self.queue_declare(ch, queue = "otherqueue")
ctag = ch.basic_consume(queue = "otherqueue", no_ack = False).consumer_tag
body = "test ack"
@@ -102,3 +102,19 @@ class BrokerTests(TestBase):
except Closed, e:
self.assertConnectionException(504, e.args[0])
+ def test_channel_flow(self):
+ channel = self.channel
+ channel.queue_declare(queue="flow_test_queue", exclusive=True)
+ ctag = channel.basic_consume(queue="flow_test_queue", no_ack=True).consumer_tag
+ incoming = self.client.queue(ctag)
+
+ channel.channel_flow(active=False)
+ channel.basic_publish(routing_key="flow_test_queue", content=Content("abcdefghijklmnopqrstuvwxyz"))
+ try:
+ incoming.get(timeout=1)
+ self.fail("Received message when flow turned off.")
+ except Empty: None
+
+ channel.channel_flow(active=True)
+ msg = incoming.get(timeout=1)
+ self.assertEqual("abcdefghijklmnopqrstuvwxyz", msg.content.body)
diff --git a/qpid/python/tests_0-8/example.py b/qpid/python/tests_0-8/example.py
index a1949ccb9f..d82bad1f61 100644
--- a/qpid/python/tests_0-8/example.py
+++ b/qpid/python/tests_0-8/example.py
@@ -18,7 +18,7 @@
#
from qpid.content import Content
-from qpid.testlib import testrunner, TestBase
+from qpid.testlib import TestBase
class ExampleTest (TestBase):
"""
diff --git a/qpid/python/tests_0-8/queue.py b/qpid/python/tests_0-8/queue.py
index 60ac4c3dfb..b7a41736ab 100644
--- a/qpid/python/tests_0-8/queue.py
+++ b/qpid/python/tests_0-8/queue.py
@@ -19,7 +19,7 @@
from qpid.client import Client, Closed
from qpid.queue import Empty
from qpid.content import Content
-from qpid.testlib import testrunner, TestBase
+from qpid.testlib import TestBase
class QueueTests(TestBase):
"""Tests for 'methods' on the amqp queue 'class'"""
diff --git a/qpid/python/tests_0-8/testlib.py b/qpid/python/tests_0-8/testlib.py
index cab07cc4ac..76f7e964a2 100644
--- a/qpid/python/tests_0-8/testlib.py
+++ b/qpid/python/tests_0-8/testlib.py
@@ -22,7 +22,7 @@
#
from qpid.content import Content
-from qpid.testlib import testrunner, TestBase
+from qpid.testlib import TestBase
from Queue import Empty
import sys
diff --git a/qpid/python/tests_0-8/tx.py b/qpid/python/tests_0-8/tx.py
index 054fb8d8b7..9faddb1110 100644
--- a/qpid/python/tests_0-8/tx.py
+++ b/qpid/python/tests_0-8/tx.py
@@ -19,7 +19,7 @@
from qpid.client import Client, Closed
from qpid.queue import Empty
from qpid.content import Content
-from qpid.testlib import testrunner, TestBase
+from qpid.testlib import TestBase
class TxTests(TestBase):
"""
diff --git a/qpid/python/tests_0-9/__init__.py b/qpid/python/tests_0-9/__init__.py
index 9a09d2d04f..d9f2ed7dbb 100644
--- a/qpid/python/tests_0-9/__init__.py
+++ b/qpid/python/tests_0-9/__init__.py
@@ -18,3 +18,5 @@
# specific language governing permissions and limitations
# under the License.
#
+
+import query, queue
diff --git a/qpid/python/tests_0-9/basic.py b/qpid/python/tests_0-9/basic.py
deleted file mode 100644
index 607ba26343..0000000000
--- a/qpid/python/tests_0-9/basic.py
+++ /dev/null
@@ -1,396 +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.
-#
-from qpid.client import Client, Closed
-from qpid.queue import Empty
-from qpid.content import Content
-from qpid.testlib import testrunner, TestBase
-
-class BasicTests(TestBase):
- """Tests for 'methods' on the amqp basic 'class'"""
-
- def test_consume_no_local(self):
- """
- Test that the no_local flag is honoured in the consume method
- """
- channel = self.channel
- #setup, declare two queues:
- channel.queue_declare(queue="test-queue-1a", exclusive=True)
- channel.queue_declare(queue="test-queue-1b", exclusive=True)
- #establish two consumers one of which excludes delivery of locally sent messages
- channel.basic_consume(consumer_tag="local_included", queue="test-queue-1a")
- channel.basic_consume(consumer_tag="local_excluded", queue="test-queue-1b", no_local=True)
-
- #send a message
- channel.basic_publish(routing_key="test-queue-1a", content=Content("consume_no_local"))
- channel.basic_publish(routing_key="test-queue-1b", content=Content("consume_no_local"))
-
- #check the queues of the two consumers
- excluded = self.client.queue("local_excluded")
- included = self.client.queue("local_included")
- msg = included.get(timeout=1)
- self.assertEqual("consume_no_local", msg.content.body)
- try:
- excluded.get(timeout=1)
- self.fail("Received locally published message though no_local=true")
- except Empty: None
-
-
- def test_consume_exclusive(self):
- """
- Test that the exclusive flag is honoured in the consume method
- """
- channel = self.channel
- #setup, declare a queue:
- channel.queue_declare(queue="test-queue-2", exclusive=True)
-
- #check that an exclusive consumer prevents other consumer being created:
- channel.basic_consume(consumer_tag="first", queue="test-queue-2", exclusive=True)
- try:
- channel.basic_consume(consumer_tag="second", queue="test-queue-2")
- self.fail("Expected consume request to fail due to previous exclusive consumer")
- except Closed, e:
- self.assertChannelException(403, e.args[0])
-
- #open new channel and cleanup last consumer:
- channel = self.client.channel(2)
- channel.channel_open()
-
- #check that an exclusive consumer cannot be created if a consumer already exists:
- channel.basic_consume(consumer_tag="first", queue="test-queue-2")
- try:
- channel.basic_consume(consumer_tag="second", queue="test-queue-2", exclusive=True)
- self.fail("Expected exclusive consume request to fail due to previous consumer")
- except Closed, e:
- self.assertChannelException(403, e.args[0])
-
- def test_consume_queue_errors(self):
- """
- Test error conditions associated with the queue field of the consume method:
- """
- channel = self.channel
- try:
- #queue specified but doesn't exist:
- channel.basic_consume(queue="invalid-queue")
- self.fail("Expected failure when consuming from non-existent queue")
- except Closed, e:
- self.assertChannelException(404, e.args[0])
-
- channel = self.client.channel(2)
- channel.channel_open()
- try:
- #queue not specified and none previously declared for channel:
- channel.basic_consume(queue="")
- self.fail("Expected failure when consuming from unspecified queue")
- except Closed, e:
- self.assertConnectionException(530, e.args[0])
-
- def test_consume_unique_consumers(self):
- """
- Ensure unique consumer tags are enforced
- """
- channel = self.channel
- #setup, declare a queue:
- channel.queue_declare(queue="test-queue-3", exclusive=True)
-
- #check that attempts to use duplicate tags are detected and prevented:
- channel.basic_consume(consumer_tag="first", queue="test-queue-3")
- try:
- channel.basic_consume(consumer_tag="first", queue="test-queue-3")
- self.fail("Expected consume request to fail due to non-unique tag")
- except Closed, e:
- self.assertConnectionException(530, e.args[0])
-
- def test_cancel(self):
- """
- Test compliance of the basic.cancel method
- """
- channel = self.channel
- #setup, declare a queue:
- channel.queue_declare(queue="test-queue-4", exclusive=True)
- channel.basic_consume(consumer_tag="my-consumer", queue="test-queue-4")
- channel.basic_publish(routing_key="test-queue-4", content=Content("One"))
-
- myqueue = self.client.queue("my-consumer")
- msg = myqueue.get(timeout=1)
- self.assertEqual("One", msg.content.body)
-
- #cancel should stop messages being delivered
- channel.basic_cancel(consumer_tag="my-consumer")
- channel.basic_publish(routing_key="test-queue-4", content=Content("Two"))
- try:
- msg = myqueue.get(timeout=1)
- self.fail("Got message after cancellation: " + msg)
- except Empty: None
-
- #cancellation of non-existant consumers should be handled without error
- channel.basic_cancel(consumer_tag="my-consumer")
- channel.basic_cancel(consumer_tag="this-never-existed")
-
-
- def test_ack(self):
- """
- Test basic ack/recover behaviour
- """
- channel = self.channel
- channel.queue_declare(queue="test-ack-queue", exclusive=True)
-
- reply = channel.basic_consume(queue="test-ack-queue", no_ack=False)
- queue = self.client.queue(reply.consumer_tag)
-
- channel.basic_publish(routing_key="test-ack-queue", content=Content("One"))
- channel.basic_publish(routing_key="test-ack-queue", content=Content("Two"))
- channel.basic_publish(routing_key="test-ack-queue", content=Content("Three"))
- channel.basic_publish(routing_key="test-ack-queue", content=Content("Four"))
- channel.basic_publish(routing_key="test-ack-queue", content=Content("Five"))
-
- msg1 = queue.get(timeout=1)
- msg2 = queue.get(timeout=1)
- msg3 = queue.get(timeout=1)
- msg4 = queue.get(timeout=1)
- msg5 = queue.get(timeout=1)
-
- self.assertEqual("One", msg1.content.body)
- self.assertEqual("Two", msg2.content.body)
- self.assertEqual("Three", msg3.content.body)
- self.assertEqual("Four", msg4.content.body)
- self.assertEqual("Five", msg5.content.body)
-
- channel.basic_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two
- channel.basic_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four
-
- channel.basic_recover(requeue=False)
-
- msg3b = queue.get(timeout=1)
- msg5b = queue.get(timeout=1)
-
- self.assertEqual("Three", msg3b.content.body)
- self.assertEqual("Five", msg5b.content.body)
-
- try:
- extra = queue.get(timeout=1)
- self.fail("Got unexpected message: " + extra.content.body)
- except Empty: None
-
- def test_recover_requeue(self):
- """
- Test requeing on recovery
- """
- channel = self.channel
- channel.queue_declare(queue="test-requeue", exclusive=True)
-
- subscription = channel.basic_consume(queue="test-requeue", no_ack=False)
- queue = self.client.queue(subscription.consumer_tag)
-
- channel.basic_publish(routing_key="test-requeue", content=Content("One"))
- channel.basic_publish(routing_key="test-requeue", content=Content("Two"))
- channel.basic_publish(routing_key="test-requeue", content=Content("Three"))
- channel.basic_publish(routing_key="test-requeue", content=Content("Four"))
- channel.basic_publish(routing_key="test-requeue", content=Content("Five"))
-
- msg1 = queue.get(timeout=1)
- msg2 = queue.get(timeout=1)
- msg3 = queue.get(timeout=1)
- msg4 = queue.get(timeout=1)
- msg5 = queue.get(timeout=1)
-
- self.assertEqual("One", msg1.content.body)
- self.assertEqual("Two", msg2.content.body)
- self.assertEqual("Three", msg3.content.body)
- self.assertEqual("Four", msg4.content.body)
- self.assertEqual("Five", msg5.content.body)
-
- channel.basic_ack(delivery_tag=msg2.delivery_tag, multiple=True) #One & Two
- channel.basic_ack(delivery_tag=msg4.delivery_tag, multiple=False) #Four
-
- channel.basic_cancel(consumer_tag=subscription.consumer_tag)
-
- channel.basic_recover(requeue=True)
-
- subscription2 = channel.basic_consume(queue="test-requeue")
- queue2 = self.client.queue(subscription2.consumer_tag)
-
- msg3b = queue2.get(timeout=1)
- msg5b = queue2.get(timeout=1)
-
- self.assertEqual("Three", msg3b.content.body)
- self.assertEqual("Five", msg5b.content.body)
-
- self.assertEqual(True, msg3b.redelivered)
- self.assertEqual(True, msg5b.redelivered)
-
- try:
- extra = queue2.get(timeout=1)
- self.fail("Got unexpected message in second queue: " + extra.content.body)
- except Empty: None
- try:
- extra = queue.get(timeout=1)
- self.fail("Got unexpected message in original queue: " + extra.content.body)
- except Empty: None
-
-
- def test_qos_prefetch_count(self):
- """
- Test that the prefetch count specified is honoured
- """
- #setup: declare queue and subscribe
- channel = self.channel
- channel.queue_declare(queue="test-prefetch-count", exclusive=True)
- subscription = channel.basic_consume(queue="test-prefetch-count", no_ack=False)
- queue = self.client.queue(subscription.consumer_tag)
-
- #set prefetch to 5:
- channel.basic_qos(prefetch_count=5)
-
- #publish 10 messages:
- for i in range(1, 11):
- channel.basic_publish(routing_key="test-prefetch-count", content=Content("Message %d" % i))
-
- #only 5 messages should have been delivered:
- for i in range(1, 6):
- msg = queue.get(timeout=1)
- self.assertEqual("Message %d" % i, msg.content.body)
- try:
- extra = queue.get(timeout=1)
- self.fail("Got unexpected 6th message in original queue: " + extra.content.body)
- except Empty: None
-
- #ack messages and check that the next set arrive ok:
- channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True)
-
- for i in range(6, 11):
- msg = queue.get(timeout=1)
- self.assertEqual("Message %d" % i, msg.content.body)
-
- channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True)
-
- try:
- extra = queue.get(timeout=1)
- self.fail("Got unexpected 11th message in original queue: " + extra.content.body)
- except Empty: None
-
-
-
- def test_qos_prefetch_size(self):
- """
- Test that the prefetch size specified is honoured
- """
- #setup: declare queue and subscribe
- channel = self.channel
- channel.queue_declare(queue="test-prefetch-size", exclusive=True)
- subscription = channel.basic_consume(queue="test-prefetch-size", no_ack=False)
- queue = self.client.queue(subscription.consumer_tag)
-
- #set prefetch to 50 bytes (each message is 9 or 10 bytes):
- channel.basic_qos(prefetch_size=50)
-
- #publish 10 messages:
- for i in range(1, 11):
- channel.basic_publish(routing_key="test-prefetch-size", content=Content("Message %d" % i))
-
- #only 5 messages should have been delivered (i.e. 45 bytes worth):
- for i in range(1, 6):
- msg = queue.get(timeout=1)
- self.assertEqual("Message %d" % i, msg.content.body)
-
- try:
- extra = queue.get(timeout=1)
- self.fail("Got unexpected 6th message in original queue: " + extra.content.body)
- except Empty: None
-
- #ack messages and check that the next set arrive ok:
- channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True)
-
- for i in range(6, 11):
- msg = queue.get(timeout=1)
- self.assertEqual("Message %d" % i, msg.content.body)
-
- channel.basic_ack(delivery_tag=msg.delivery_tag, multiple=True)
-
- try:
- extra = queue.get(timeout=1)
- self.fail("Got unexpected 11th message in original queue: " + extra.content.body)
- except Empty: None
-
- #make sure that a single oversized message still gets delivered
- large = "abcdefghijklmnopqrstuvwxyz"
- large = large + "-" + large;
- channel.basic_publish(routing_key="test-prefetch-size", content=Content(large))
- msg = queue.get(timeout=1)
- self.assertEqual(large, msg.content.body)
-
- def test_get(self):
- """
- Test basic_get method
- """
- channel = self.channel
- channel.queue_declare(queue="test-get", exclusive=True)
-
- #publish some messages (no_ack=True)
- for i in range(1, 11):
- channel.basic_publish(routing_key="test-get", content=Content("Message %d" % i))
-
- #use basic_get to read back the messages, and check that we get an empty at the end
- for i in range(1, 11):
- reply = channel.basic_get(no_ack=True)
- self.assertEqual(reply.method.klass.name, "basic")
- self.assertEqual(reply.method.name, "get_ok")
- self.assertEqual("Message %d" % i, reply.content.body)
-
- reply = channel.basic_get(no_ack=True)
- self.assertEqual(reply.method.klass.name, "basic")
- self.assertEqual(reply.method.name, "get_empty")
-
- #repeat for no_ack=False
- for i in range(11, 21):
- channel.basic_publish(routing_key="test-get", content=Content("Message %d" % i))
-
- for i in range(11, 21):
- reply = channel.basic_get(no_ack=False)
- self.assertEqual(reply.method.klass.name, "basic")
- self.assertEqual(reply.method.name, "get_ok")
- self.assertEqual("Message %d" % i, reply.content.body)
- if(i == 13):
- channel.basic_ack(delivery_tag=reply.delivery_tag, multiple=True)
- if(i in [15, 17, 19]):
- channel.basic_ack(delivery_tag=reply.delivery_tag)
-
- reply = channel.basic_get(no_ack=True)
- self.assertEqual(reply.method.klass.name, "basic")
- self.assertEqual(reply.method.name, "get_empty")
-
- #recover(requeue=True)
- channel.basic_recover(requeue=True)
-
- #get the unacked messages again (14, 16, 18, 20)
- for i in [14, 16, 18, 20]:
- reply = channel.basic_get(no_ack=False)
- self.assertEqual(reply.method.klass.name, "basic")
- self.assertEqual(reply.method.name, "get_ok")
- self.assertEqual("Message %d" % i, reply.content.body)
- channel.basic_ack(delivery_tag=reply.delivery_tag)
-
- reply = channel.basic_get(no_ack=True)
- self.assertEqual(reply.method.klass.name, "basic")
- self.assertEqual(reply.method.name, "get_empty")
-
- channel.basic_recover(requeue=True)
-
- reply = channel.basic_get(no_ack=True)
- self.assertEqual(reply.method.klass.name, "basic")
- self.assertEqual(reply.method.name, "get_empty")
diff --git a/qpid/python/tests_0-9/broker.py b/qpid/python/tests_0-9/broker.py
deleted file mode 100644
index 03b4132d3e..0000000000
--- a/qpid/python/tests_0-9/broker.py
+++ /dev/null
@@ -1,133 +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.
-#
-from qpid.client import Closed
-from qpid.queue import Empty
-from qpid.content import Content
-from qpid.testlib import testrunner, TestBase
-
-class BrokerTests(TestBase):
- """Tests for basic Broker functionality"""
-
- def test_ack_and_no_ack(self):
- """
- First, this test tries to receive a message with a no-ack
- consumer. Second, this test tries to explicitly receive and
- acknowledge a message with an acknowledging consumer.
- """
- ch = self.channel
- self.queue_declare(ch, queue = "myqueue")
-
- # No ack consumer
- ctag = "tag1"
- ch.message_consume(queue = "myqueue", destination = ctag, no_ack = True)
- body = "test no-ack"
- ch.message_transfer(routing_key = "myqueue", body = body)
- msg = self.client.queue(ctag).get(timeout = 5)
- self.assert_(msg.body == body)
-
- # Acknowledging consumer
- self.queue_declare(ch, queue = "otherqueue")
- ctag = "tag2"
- ch.message_consume(queue = "otherqueue", destination = ctag, no_ack = False)
- body = "test ack"
- ch.message_transfer(routing_key = "otherqueue", body = body)
- msg = self.client.queue(ctag).get(timeout = 5)
- msg.ok()
- self.assert_(msg.body == body)
-
- def test_simple_delivery_immediate(self):
- """
- Test simple message delivery where consume is issued before publish
- """
- channel = self.channel
- self.exchange_declare(channel, exchange="test-exchange", type="direct")
- self.queue_declare(channel, queue="test-queue")
- channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key")
- consumer_tag = "tag1"
- channel.message_consume(queue="test-queue", destination=consumer_tag, no_ack=True)
- queue = self.client.queue(consumer_tag)
-
- body = "Immediate Delivery"
- channel.message_transfer(destination="test-exchange", routing_key="key", body=body, immediate=True)
- msg = queue.get(timeout=5)
- self.assert_(msg.body == body)
-
- # TODO: Ensure we fail if immediate=True and there's no consumer.
-
-
- def test_simple_delivery_queued(self):
- """
- Test basic message delivery where publish is issued before consume
- (i.e. requires queueing of the message)
- """
- channel = self.channel
- self.exchange_declare(channel, exchange="test-exchange", type="direct")
- self.queue_declare(channel, queue="test-queue")
- channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key")
- body = "Queued Delivery"
- channel.message_transfer(destination="test-exchange", routing_key="key", body=body)
-
- consumer_tag = "tag1"
- channel.message_consume(queue="test-queue", destination=consumer_tag, no_ack=True)
- queue = self.client.queue(consumer_tag)
- msg = queue.get(timeout=5)
- self.assert_(msg.body == body)
-
- def test_invalid_channel(self):
- channel = self.client.channel(200)
- try:
- channel.queue_declare(exclusive=True)
- self.fail("Expected error on queue_declare for invalid channel")
- except Closed, e:
- self.assertConnectionException(504, e.args[0])
-
- def test_closed_channel(self):
- channel = self.client.channel(200)
- channel.channel_open()
- channel.channel_close()
- try:
- channel.queue_declare(exclusive=True)
- self.fail("Expected error on queue_declare for closed channel")
- except Closed, e:
- if isinstance(e.args[0], str): self.fail(e)
- self.assertConnectionException(504, e.args[0])
-
- def test_ping_pong(self):
- channel = self.channel
- reply = channel.channel_ping()
- self.assertEqual(reply.method.klass.name, "channel")
- self.assertEqual(reply.method.name, "ok")
- #todo: provide a way to get notified of incoming pongs...
-
- def test_channel_flow(self):
- channel = self.channel
- channel.queue_declare(queue="flow_test_queue", exclusive=True)
- channel.message_consume(destination="my-tag", queue="flow_test_queue")
- incoming = self.client.queue("my-tag")
-
- channel.channel_flow(active=False)
- channel.message_transfer(routing_key="flow_test_queue", body="abcdefghijklmnopqrstuvwxyz")
- try:
- incoming.get(timeout=1)
- self.fail("Received message when flow turned off.")
- except Empty: None
-
- channel.channel_flow(active=True)
- msg = incoming.get(timeout=1)
- self.assertEqual("abcdefghijklmnopqrstuvwxyz", msg.body)
diff --git a/qpid/python/tests_0-9/dtx.py b/qpid/python/tests_0-9/dtx.py
deleted file mode 100644
index bc268f4129..0000000000
--- a/qpid/python/tests_0-9/dtx.py
+++ /dev/null
@@ -1,587 +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.
-#
-from qpid.client import Client, Closed
-from qpid.queue import Empty
-from qpid.content import Content
-from qpid.testlib import testrunner, TestBase
-from struct import pack, unpack
-from time import sleep
-
-class DtxTests(TestBase):
- """
- Tests for the amqp dtx related classes.
-
- Tests of the form test_simple_xxx test the basic transactional
- behaviour. The approach here is to 'swap' a message from one queue
- to another by consuming and re-publishing in the same
- transaction. That transaction is then completed in different ways
- and the appropriate result verified.
-
- The other tests enforce more specific rules and behaviour on a
- per-method or per-field basis.
- """
-
- XA_RBROLLBACK = 1
- XA_RBTIMEOUT = 2
- XA_OK = 8
-
- def test_simple_commit(self):
- """
- Test basic one-phase commit behaviour.
- """
- channel = self.channel
- tx = self.xid("my-xid")
- self.txswap(tx, "commit")
-
- #neither queue should have any messages accessible
- self.assertMessageCount(0, "queue-a")
- self.assertMessageCount(0, "queue-b")
-
- #commit
- self.assertEqual(self.XA_OK, channel.dtx_coordination_commit(xid=tx, one_phase=True).flags)
-
- #check result
- self.assertMessageCount(0, "queue-a")
- self.assertMessageCount(1, "queue-b")
- self.assertMessageId("commit", "queue-b")
-
- def test_simple_prepare_commit(self):
- """
- Test basic two-phase commit behaviour.
- """
- channel = self.channel
- tx = self.xid("my-xid")
- self.txswap(tx, "prepare-commit")
-
- #prepare
- self.assertEqual(self.XA_OK, channel.dtx_coordination_prepare(xid=tx).flags)
-
- #neither queue should have any messages accessible
- self.assertMessageCount(0, "queue-a")
- self.assertMessageCount(0, "queue-b")
-
- #commit
- self.assertEqual(self.XA_OK, channel.dtx_coordination_commit(xid=tx, one_phase=False).flags)
-
- #check result
- self.assertMessageCount(0, "queue-a")
- self.assertMessageCount(1, "queue-b")
- self.assertMessageId("prepare-commit", "queue-b")
-
-
- def test_simple_rollback(self):
- """
- Test basic rollback behaviour.
- """
- channel = self.channel
- tx = self.xid("my-xid")
- self.txswap(tx, "rollback")
-
- #neither queue should have any messages accessible
- self.assertMessageCount(0, "queue-a")
- self.assertMessageCount(0, "queue-b")
-
- #rollback
- self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).flags)
-
- #check result
- self.assertMessageCount(1, "queue-a")
- self.assertMessageCount(0, "queue-b")
- self.assertMessageId("rollback", "queue-a")
-
- def test_simple_prepare_rollback(self):
- """
- Test basic rollback behaviour after the transaction has been prepared.
- """
- channel = self.channel
- tx = self.xid("my-xid")
- self.txswap(tx, "prepare-rollback")
-
- #prepare
- self.assertEqual(self.XA_OK, channel.dtx_coordination_prepare(xid=tx).flags)
-
- #neither queue should have any messages accessible
- self.assertMessageCount(0, "queue-a")
- self.assertMessageCount(0, "queue-b")
-
- #rollback
- self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).flags)
-
- #check result
- self.assertMessageCount(1, "queue-a")
- self.assertMessageCount(0, "queue-b")
- self.assertMessageId("prepare-rollback", "queue-a")
-
- def test_select_required(self):
- """
- check that an error is flagged if select is not issued before
- start or end
- """
- channel = self.channel
- tx = self.xid("dummy")
- try:
- channel.dtx_demarcation_start(xid=tx)
-
- #if we get here we have failed, but need to do some cleanup:
- channel.dtx_demarcation_end(xid=tx)
- channel.dtx_coordination_rollback(xid=tx)
- self.fail("Channel not selected for use with dtx, expected exception!")
- except Closed, e:
- self.assertConnectionException(503, e.args[0])
-
- def test_start_already_known(self):
- """
- Verify that an attempt to start an association with a
- transaction that is already known is not allowed (unless the
- join flag is set).
- """
- #create two channels on different connection & select them for use with dtx:
- channel1 = self.channel
- channel1.dtx_demarcation_select()
-
- other = self.connect()
- channel2 = other.channel(1)
- channel2.channel_open()
- channel2.dtx_demarcation_select()
-
- #create a xid
- tx = self.xid("dummy")
- #start work on one channel under that xid:
- channel1.dtx_demarcation_start(xid=tx)
- #then start on the other without the join set
- failed = False
- try:
- channel2.dtx_demarcation_start(xid=tx)
- except Closed, e:
- failed = True
- error = e
-
- #cleanup:
- if not failed:
- channel2.dtx_demarcation_end(xid=tx)
- other.close()
- channel1.dtx_demarcation_end(xid=tx)
- channel1.dtx_coordination_rollback(xid=tx)
-
- #verification:
- if failed: self.assertConnectionException(503, e.args[0])
- else: self.fail("Xid already known, expected exception!")
-
- def test_forget_xid_on_completion(self):
- """
- Verify that a xid is 'forgotten' - and can therefore be used
- again - once it is completed.
- """
- channel = self.channel
- #do some transactional work & complete the transaction
- self.test_simple_commit()
-
- #start association for the same xid as the previously completed txn
- tx = self.xid("my-xid")
- channel.dtx_demarcation_start(xid=tx)
- channel.dtx_demarcation_end(xid=tx)
- channel.dtx_coordination_rollback(xid=tx)
-
- def test_start_join_and_resume(self):
- """
- Ensure the correct error is signalled when both the join and
- resume flags are set on starting an association between a
- channel and a transcation.
- """
- channel = self.channel
- channel.dtx_demarcation_select()
- tx = self.xid("dummy")
- try:
- channel.dtx_demarcation_start(xid=tx, join=True, resume=True)
- #failed, but need some cleanup:
- channel.dtx_demarcation_end(xid=tx)
- channel.dtx_coordination_rollback(xid=tx)
- self.fail("Join and resume both set, expected exception!")
- except Closed, e:
- self.assertConnectionException(503, e.args[0])
-
- def test_start_join(self):
- """
- Verify 'join' behaviour, where a channel is associated with a
- transaction that is already associated with another channel.
- """
- #create two channels & select them for use with dtx:
- channel1 = self.channel
- channel1.dtx_demarcation_select()
-
- channel2 = self.client.channel(2)
- channel2.channel_open()
- channel2.dtx_demarcation_select()
-
- #setup
- channel1.queue_declare(queue="one", exclusive=True)
- channel1.queue_declare(queue="two", exclusive=True)
- channel1.message_transfer(routing_key="one", message_id="a", body="DtxMessage")
- channel1.message_transfer(routing_key="two", message_id="b", body="DtxMessage")
-
- #create a xid
- tx = self.xid("dummy")
- #start work on one channel under that xid:
- channel1.dtx_demarcation_start(xid=tx)
- #then start on the other with the join flag set
- channel2.dtx_demarcation_start(xid=tx, join=True)
-
- #do work through each channel
- self.swap(channel1, "one", "two")#swap 'a' from 'one' to 'two'
- self.swap(channel2, "two", "one")#swap 'b' from 'two' to 'one'
-
- #mark end on both channels
- channel1.dtx_demarcation_end(xid=tx)
- channel2.dtx_demarcation_end(xid=tx)
-
- #commit and check
- channel1.dtx_coordination_commit(xid=tx, one_phase=True)
- self.assertMessageCount(1, "one")
- self.assertMessageCount(1, "two")
- self.assertMessageId("a", "two")
- self.assertMessageId("b", "one")
-
-
- def test_suspend_resume(self):
- """
- Test suspension and resumption of an association
- """
- channel = self.channel
- channel.dtx_demarcation_select()
-
- #setup
- channel.queue_declare(queue="one", exclusive=True)
- channel.queue_declare(queue="two", exclusive=True)
- channel.message_transfer(routing_key="one", message_id="a", body="DtxMessage")
- channel.message_transfer(routing_key="two", message_id="b", body="DtxMessage")
-
- tx = self.xid("dummy")
-
- channel.dtx_demarcation_start(xid=tx)
- self.swap(channel, "one", "two")#swap 'a' from 'one' to 'two'
- channel.dtx_demarcation_end(xid=tx, suspend=True)
-
- channel.dtx_demarcation_start(xid=tx, resume=True)
- self.swap(channel, "two", "one")#swap 'b' from 'two' to 'one'
- channel.dtx_demarcation_end(xid=tx)
-
- #commit and check
- channel.dtx_coordination_commit(xid=tx, one_phase=True)
- self.assertMessageCount(1, "one")
- self.assertMessageCount(1, "two")
- self.assertMessageId("a", "two")
- self.assertMessageId("b", "one")
-
- def test_end_suspend_and_fail(self):
- """
- Verify that the correct error is signalled if the suspend and
- fail flag are both set when disassociating a transaction from
- the channel
- """
- channel = self.channel
- channel.dtx_demarcation_select()
- tx = self.xid("suspend_and_fail")
- channel.dtx_demarcation_start(xid=tx)
- try:
- channel.dtx_demarcation_end(xid=tx, suspend=True, fail=True)
- self.fail("Suspend and fail both set, expected exception!")
- except Closed, e:
- self.assertConnectionException(503, e.args[0])
-
- #cleanup
- other = self.connect()
- channel = other.channel(1)
- channel.channel_open()
- channel.dtx_coordination_rollback(xid=tx)
- channel.channel_close()
- other.close()
-
-
- def test_end_unknown_xid(self):
- """
- Verifies that the correct exception is thrown when an attempt
- is made to end the association for a xid not previously
- associated with the channel
- """
- channel = self.channel
- channel.dtx_demarcation_select()
- tx = self.xid("unknown-xid")
- try:
- channel.dtx_demarcation_end(xid=tx)
- self.fail("Attempted to end association with unknown xid, expected exception!")
- except Closed, e:
- #FYI: this is currently *not* the exception specified, but I think the spec is wrong! Confirming...
- self.assertConnectionException(503, e.args[0])
-
- def test_end(self):
- """
- Verify that the association is terminated by end and subsequent
- operations are non-transactional
- """
- channel = self.client.channel(2)
- channel.channel_open()
- channel.queue_declare(queue="tx-queue", exclusive=True)
-
- #publish a message under a transaction
- channel.dtx_demarcation_select()
- tx = self.xid("dummy")
- channel.dtx_demarcation_start(xid=tx)
- channel.message_transfer(routing_key="tx-queue", message_id="one", body="DtxMessage")
- channel.dtx_demarcation_end(xid=tx)
-
- #now that association with txn is ended, publish another message
- channel.message_transfer(routing_key="tx-queue", message_id="two", body="DtxMessage")
-
- #check the second message is available, but not the first
- self.assertMessageCount(1, "tx-queue")
- channel.message_consume(queue="tx-queue", destination="results", no_ack=False)
- msg = self.client.queue("results").get(timeout=1)
- self.assertEqual("two", msg.message_id)
- channel.message_cancel(destination="results")
- #ack the message then close the channel
- msg.ok()
- channel.channel_close()
-
- channel = self.channel
- #commit the transaction and check that the first message (and
- #only the first message) is then delivered
- channel.dtx_coordination_commit(xid=tx, one_phase=True)
- self.assertMessageCount(1, "tx-queue")
- self.assertMessageId("one", "tx-queue")
-
- def test_invalid_commit_one_phase_true(self):
- """
- Test that a commit with one_phase = True is rejected if the
- transaction in question has already been prepared.
- """
- other = self.connect()
- tester = other.channel(1)
- tester.channel_open()
- tester.queue_declare(queue="dummy", exclusive=True)
- tester.dtx_demarcation_select()
- tx = self.xid("dummy")
- tester.dtx_demarcation_start(xid=tx)
- tester.message_transfer(routing_key="dummy", body="whatever")
- tester.dtx_demarcation_end(xid=tx)
- tester.dtx_coordination_prepare(xid=tx)
- failed = False
- try:
- tester.dtx_coordination_commit(xid=tx, one_phase=True)
- except Closed, e:
- failed = True
- error = e
-
- if failed:
- self.channel.dtx_coordination_rollback(xid=tx)
- self.assertConnectionException(503, e.args[0])
- else:
- tester.channel_close()
- other.close()
- self.fail("Invalid use of one_phase=True, expected exception!")
-
- def test_invalid_commit_one_phase_false(self):
- """
- Test that a commit with one_phase = False is rejected if the
- transaction in question has not yet been prepared.
- """
- """
- Test that a commit with one_phase = True is rejected if the
- transaction in question has already been prepared.
- """
- other = self.connect()
- tester = other.channel(1)
- tester.channel_open()
- tester.queue_declare(queue="dummy", exclusive=True)
- tester.dtx_demarcation_select()
- tx = self.xid("dummy")
- tester.dtx_demarcation_start(xid=tx)
- tester.message_transfer(routing_key="dummy", body="whatever")
- tester.dtx_demarcation_end(xid=tx)
- failed = False
- try:
- tester.dtx_coordination_commit(xid=tx, one_phase=False)
- except Closed, e:
- failed = True
- error = e
-
- if failed:
- self.channel.dtx_coordination_rollback(xid=tx)
- self.assertConnectionException(503, e.args[0])
- else:
- tester.channel_close()
- other.close()
- self.fail("Invalid use of one_phase=False, expected exception!")
-
- def test_implicit_end(self):
- """
- Test that an association is implicitly ended when the channel
- is closed (whether by exception or explicit client request)
- and the transaction in question is marked as rollback only.
- """
- channel1 = self.channel
- channel2 = self.client.channel(2)
- channel2.channel_open()
-
- #setup:
- channel2.queue_declare(queue="dummy", exclusive=True)
- channel2.message_transfer(routing_key="dummy", body="whatever")
- tx = self.xid("dummy")
-
- channel2.dtx_demarcation_select()
- channel2.dtx_demarcation_start(xid=tx)
- channel2.message_get(queue="dummy", destination="dummy")
- self.client.queue("dummy").get(timeout=1).ok()
- channel2.message_transfer(routing_key="dummy", body="whatever")
- channel2.channel_close()
-
- self.assertEqual(self.XA_RBROLLBACK, channel1.dtx_coordination_prepare(xid=tx).flags)
- channel1.dtx_coordination_rollback(xid=tx)
-
- def test_get_timeout(self):
- """
- Check that get-timeout returns the correct value, (and that a
- transaction with a timeout can complete normally)
- """
- channel = self.channel
- tx = self.xid("dummy")
-
- channel.dtx_demarcation_select()
- channel.dtx_demarcation_start(xid=tx)
- self.assertEqual(0, channel.dtx_coordination_get_timeout(xid=tx).timeout)
- channel.dtx_coordination_set_timeout(xid=tx, timeout=60)
- self.assertEqual(60, channel.dtx_coordination_get_timeout(xid=tx).timeout)
- self.assertEqual(self.XA_OK, channel.dtx_demarcation_end(xid=tx).flags)
- self.assertEqual(self.XA_OK, channel.dtx_coordination_rollback(xid=tx).flags)
-
- def test_set_timeout(self):
- """
- Test the timeout of a transaction results in the expected
- behaviour
- """
- #open new channel to allow self.channel to be used in checking te queue
- channel = self.client.channel(2)
- channel.channel_open()
- #setup:
- tx = self.xid("dummy")
- channel.queue_declare(queue="queue-a", exclusive=True)
- channel.queue_declare(queue="queue-b", exclusive=True)
- channel.message_transfer(routing_key="queue-a", message_id="timeout", body="DtxMessage")
-
- channel.dtx_demarcation_select()
- channel.dtx_demarcation_start(xid=tx)
- self.swap(channel, "queue-a", "queue-b")
- channel.dtx_coordination_set_timeout(xid=tx, timeout=2)
- sleep(3)
- #check that the work has been rolled back already
- self.assertMessageCount(1, "queue-a")
- self.assertMessageCount(0, "queue-b")
- self.assertMessageId("timeout", "queue-a")
- #check the correct codes are returned when we try to complete the txn
- self.assertEqual(self.XA_RBTIMEOUT, channel.dtx_demarcation_end(xid=tx).flags)
- self.assertEqual(self.XA_RBTIMEOUT, channel.dtx_coordination_rollback(xid=tx).flags)
-
-
-
- def test_recover(self):
- """
- Test basic recover behaviour
- """
- channel = self.channel
-
- channel.dtx_demarcation_select()
- channel.queue_declare(queue="dummy", exclusive=True)
-
- prepared = []
- for i in range(1, 10):
- tx = self.xid("tx%s" % (i))
- channel.dtx_demarcation_start(xid=tx)
- channel.message_transfer(routing_key="dummy", body="message%s" % (i))
- channel.dtx_demarcation_end(xid=tx)
- if i in [2, 5, 6, 8]:
- channel.dtx_coordination_prepare(xid=tx)
- prepared.append(tx)
- else:
- channel.dtx_coordination_rollback(xid=tx)
-
- indoubt = channel.dtx_coordination_recover().xids
- #convert indoubt table to a list of xids (note: this will change for 0-10)
- data = indoubt["xids"]
- xids = []
- pos = 0
- while pos < len(data):
- size = unpack("!B", data[pos])[0]
- start = pos + 1
- end = start + size
- xid = data[start:end]
- xids.append(xid)
- pos = end
-
- #rollback the prepared transactions returned by recover
- for x in xids:
- channel.dtx_coordination_rollback(xid=x)
-
- #validate against the expected list of prepared transactions
- actual = set(xids)
- expected = set(prepared)
- intersection = actual.intersection(expected)
-
- if intersection != expected:
- missing = expected.difference(actual)
- extra = actual.difference(expected)
- for x in missing:
- channel.dtx_coordination_rollback(xid=x)
- self.fail("Recovered xids not as expected. missing: %s; extra: %s" % (missing, extra))
-
- def xid(self, txid, branchqual = ''):
- return pack('LBB', 0, len(txid), len(branchqual)) + txid + branchqual
-
- def txswap(self, tx, id):
- channel = self.channel
- #declare two queues:
- channel.queue_declare(queue="queue-a", exclusive=True)
- channel.queue_declare(queue="queue-b", exclusive=True)
- #put message with specified id on one queue:
- channel.message_transfer(routing_key="queue-a", message_id=id, body="DtxMessage")
-
- #start the transaction:
- channel.dtx_demarcation_select()
- self.assertEqual(self.XA_OK, self.channel.dtx_demarcation_start(xid=tx).flags)
-
- #'swap' the message from one queue to the other, under that transaction:
- self.swap(self.channel, "queue-a", "queue-b")
-
- #mark the end of the transactional work:
- self.assertEqual(self.XA_OK, self.channel.dtx_demarcation_end(xid=tx).flags)
-
- def swap(self, channel, src, dest):
- #consume from src:
- channel.message_get(destination="temp-swap", queue=src)
- msg = self.client.queue("temp-swap").get(timeout=1)
- msg.ok();
-
- #re-publish to dest
- channel.message_transfer(routing_key=dest, message_id=msg.message_id, body=msg.body)
-
- def assertMessageCount(self, expected, queue):
- self.assertEqual(expected, self.channel.queue_declare(queue=queue, passive=True).message_count)
-
- def assertMessageId(self, expected, queue):
- self.channel.message_consume(queue=queue, destination="results", no_ack=True)
- self.assertEqual(expected, self.client.queue("results").get(timeout=1).message_id)
- self.channel.message_cancel(destination="results")
diff --git a/qpid/python/tests_0-9/example.py b/qpid/python/tests_0-9/example.py
deleted file mode 100644
index 7ab4cc7d0a..0000000000
--- a/qpid/python/tests_0-9/example.py
+++ /dev/null
@@ -1,94 +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.
-#
-
-from qpid.content import Content
-from qpid.testlib import testrunner, TestBase
-
-class ExampleTest (TestBase):
- """
- An example Qpid test, illustrating the unittest frameowkr and the
- python Qpid client. The test class must inherit TestCase. The
- test code uses the Qpid client to interact with a qpid broker and
- verify it behaves as expected.
- """
-
- def test_example(self):
- """
- An example test. Note that test functions must start with 'test_'
- to be recognized by the test framework.
- """
-
- # By inheriting TestBase, self.client is automatically connected
- # and self.channel is automatically opened as channel(1)
- # Other channel methods mimic the protocol.
- channel = self.channel
-
- # Now we can send regular commands. If you want to see what the method
- # arguments mean or what other commands are available, you can use the
- # python builtin help() method. For example:
- #help(chan)
- #help(chan.exchange_declare)
-
- # If you want browse the available protocol methods without being
- # connected to a live server you can use the amqp-doc utility:
- #
- # Usage amqp-doc [<options>] <spec> [<pattern_1> ... <pattern_n>]
- #
- # Options:
- # -e, --regexp use regex instead of glob when matching
-
- # Now that we know what commands are available we can use them to
- # interact with the server.
-
- # Here we use ordinal arguments.
- self.exchange_declare(channel, 0, "test", "direct")
-
- # Here we use keyword arguments.
- self.queue_declare(channel, queue="test-queue")
- channel.queue_bind(queue="test-queue", exchange="test", routing_key="key")
-
- # Call Channel.basic_consume to register as a consumer.
- # All the protocol methods return a message object. The message object
- # has fields corresponding to the reply method fields, plus a content
- # field that is filled if the reply includes content. In this case the
- # interesting field is the consumer_tag.
- channel.message_consume(queue="test-queue", destination="consumer_tag")
-
- # We can use the Client.queue(...) method to access the queue
- # corresponding to our consumer_tag.
- queue = self.client.queue("consumer_tag")
-
- # Now lets publish a message and see if our consumer gets it. To do
- # this we need to import the Content class.
- body = "Hello World!"
- channel.message_transfer(destination="test",
- routing_key="key",
- body = body)
-
- # Now we'll wait for the message to arrive. We can use the timeout
- # argument in case the server hangs. By default queue.get() will wait
- # until a message arrives or the connection to the server dies.
- msg = queue.get(timeout=10)
-
- # And check that we got the right response with assertEqual
- self.assertEqual(body, msg.body)
-
- # Now acknowledge the message.
- msg.ok()
-
diff --git a/qpid/python/tests_0-9/exchange.py b/qpid/python/tests_0-9/exchange.py
deleted file mode 100644
index 3a47ffff8c..0000000000
--- a/qpid/python/tests_0-9/exchange.py
+++ /dev/null
@@ -1,327 +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.
-#
-
-"""
-Tests for exchange behaviour.
-
-Test classes ending in 'RuleTests' are derived from rules in amqp.xml.
-"""
-
-import Queue, logging
-from qpid.testlib import TestBase
-from qpid.content import Content
-from qpid.client import Closed
-
-
-class StandardExchangeVerifier:
- """Verifies standard exchange behavior.
-
- Used as base class for classes that test standard exchanges."""
-
- def verifyDirectExchange(self, ex):
- """Verify that ex behaves like a direct exchange."""
- self.queue_declare(queue="q")
- self.channel.queue_bind(queue="q", exchange=ex, routing_key="k")
- self.assertPublishConsume(exchange=ex, queue="q", routing_key="k")
- try:
- self.assertPublishConsume(exchange=ex, queue="q", routing_key="kk")
- self.fail("Expected Empty exception")
- except Queue.Empty: None # Expected
-
- def verifyFanOutExchange(self, ex):
- """Verify that ex behaves like a fanout exchange."""
- self.queue_declare(queue="q")
- self.channel.queue_bind(queue="q", exchange=ex)
- self.queue_declare(queue="p")
- self.channel.queue_bind(queue="p", exchange=ex)
- for qname in ["q", "p"]: self.assertPublishGet(self.consume(qname), ex)
-
- def verifyTopicExchange(self, ex):
- """Verify that ex behaves like a topic exchange"""
- self.queue_declare(queue="a")
- self.channel.queue_bind(queue="a", exchange=ex, routing_key="a.#.b.*")
- q = self.consume("a")
- self.assertPublishGet(q, ex, "a.b.x")
- self.assertPublishGet(q, ex, "a.x.b.x")
- self.assertPublishGet(q, ex, "a.x.x.b.x")
- # Shouldn't match
- self.channel.message_transfer(destination=ex, routing_key="a.b", body="")
- self.channel.message_transfer(destination=ex, routing_key="a.b.x.y", body="")
- self.channel.message_transfer(destination=ex, routing_key="x.a.b.x", body="")
- self.channel.message_transfer(destination=ex, routing_key="a.b", body="")
- self.assert_(q.empty())
-
- def verifyHeadersExchange(self, ex):
- """Verify that ex is a headers exchange"""
- self.queue_declare(queue="q")
- self.channel.queue_bind(queue="q", exchange=ex, arguments={ "x-match":"all", "name":"fred" , "age":3} )
- q = self.consume("q")
- headers = {"name":"fred", "age":3}
- self.assertPublishGet(q, exchange=ex, properties=headers)
- self.channel.message_transfer(destination=ex, body="") # No headers, won't deliver
- self.assertEmpty(q);
-
-
-class RecommendedTypesRuleTests(TestBase, StandardExchangeVerifier):
- """
- The server SHOULD implement these standard exchange types: topic, headers.
-
- Client attempts to declare an exchange with each of these standard types.
- """
-
- def testDirect(self):
- """Declare and test a direct exchange"""
- self.exchange_declare(0, exchange="d", type="direct")
- self.verifyDirectExchange("d")
-
- def testFanout(self):
- """Declare and test a fanout exchange"""
- self.exchange_declare(0, exchange="f", type="fanout")
- self.verifyFanOutExchange("f")
-
- def testTopic(self):
- """Declare and test a topic exchange"""
- self.exchange_declare(0, exchange="t", type="topic")
- self.verifyTopicExchange("t")
-
- def testHeaders(self):
- """Declare and test a headers exchange"""
- self.exchange_declare(0, exchange="h", type="headers")
- self.verifyHeadersExchange("h")
-
-
-class RequiredInstancesRuleTests(TestBase, StandardExchangeVerifier):
- """
- The server MUST, in each virtual host, pre-declare an exchange instance
- for each standard exchange type that it implements, where the name of the
- exchange instance is amq. followed by the exchange type name.
-
- Client creates a temporary queue and attempts to bind to each required
- exchange instance (amq.fanout, amq.direct, and amq.topic, amq.match if
- those types are defined).
- """
- def testAmqDirect(self): self.verifyDirectExchange("amq.direct")
-
- def testAmqFanOut(self): self.verifyFanOutExchange("amq.fanout")
-
- def testAmqTopic(self): self.verifyTopicExchange("amq.topic")
-
- def testAmqMatch(self): self.verifyHeadersExchange("amq.match")
-
-class DefaultExchangeRuleTests(TestBase, StandardExchangeVerifier):
- """
- The server MUST predeclare a direct exchange to act as the default exchange
- for content Publish methods and for default queue bindings.
-
- Client checks that the default exchange is active by specifying a queue
- binding with no exchange name, and publishing a message with a suitable
- routing key but without specifying the exchange name, then ensuring that
- the message arrives in the queue correctly.
- """
- def testDefaultExchange(self):
- # Test automatic binding by queue name.
- self.queue_declare(queue="d")
- self.assertPublishConsume(queue="d", routing_key="d")
- # Test explicit bind to default queue
- self.verifyDirectExchange("")
-
-
-# TODO aconway 2006-09-27: Fill in empty tests:
-
-class DefaultAccessRuleTests(TestBase):
- """
- The server MUST NOT allow clients to access the default exchange except
- by specifying an empty exchange name in the Queue.Bind and content Publish
- methods.
- """
-
-class ExtensionsRuleTests(TestBase):
- """
- The server MAY implement other exchange types as wanted.
- """
-
-
-class DeclareMethodMinimumRuleTests(TestBase):
- """
- The server SHOULD support a minimum of 16 exchanges per virtual host and
- ideally, impose no limit except as defined by available resources.
-
- The client creates as many exchanges as it can until the server reports
- an error; the number of exchanges successfuly created must be at least
- sixteen.
- """
-
-
-class DeclareMethodTicketFieldValidityRuleTests(TestBase):
- """
- The client MUST provide a valid access ticket giving "active" access to
- the realm in which the exchange exists or will be created, or "passive"
- access if the if-exists flag is set.
-
- Client creates access ticket with wrong access rights and attempts to use
- in this method.
- """
-
-
-class DeclareMethodExchangeFieldReservedRuleTests(TestBase):
- """
- Exchange names starting with "amq." are reserved for predeclared and
- standardised exchanges. The client MUST NOT attempt to create an exchange
- starting with "amq.".
-
-
- """
-
-
-class DeclareMethodTypeFieldTypedRuleTests(TestBase):
- """
- Exchanges cannot be redeclared with different types. The client MUST not
- attempt to redeclare an existing exchange with a different type than used
- in the original Exchange.Declare method.
-
-
- """
-
-
-class DeclareMethodTypeFieldSupportRuleTests(TestBase):
- """
- The client MUST NOT attempt to create an exchange with a type that the
- server does not support.
-
-
- """
-
-
-class DeclareMethodPassiveFieldNotFoundRuleTests(TestBase):
- """
- If set, and the exchange does not already exist, the server MUST raise a
- channel exception with reply code 404 (not found).
- """
- def test(self):
- try:
- self.channel.exchange_declare(exchange="humpty_dumpty", passive=True)
- self.fail("Expected 404 for passive declaration of unknown exchange.")
- except Closed, e:
- self.assertChannelException(404, e.args[0])
-
-
-class DeclareMethodDurableFieldSupportRuleTests(TestBase):
- """
- The server MUST support both durable and transient exchanges.
-
-
- """
-
-
-class DeclareMethodDurableFieldStickyRuleTests(TestBase):
- """
- The server MUST ignore the durable field if the exchange already exists.
-
-
- """
-
-
-class DeclareMethodAutoDeleteFieldStickyRuleTests(TestBase):
- """
- The server MUST ignore the auto-delete field if the exchange already
- exists.
-
-
- """
-
-
-class DeleteMethodTicketFieldValidityRuleTests(TestBase):
- """
- The client MUST provide a valid access ticket giving "active" access
- rights to the exchange's access realm.
-
- Client creates access ticket with wrong access rights and attempts to use
- in this method.
- """
-
-
-class DeleteMethodExchangeFieldExistsRuleTests(TestBase):
- """
- The client MUST NOT attempt to delete an exchange that does not exist.
- """
-
-
-class HeadersExchangeTests(TestBase):
- """
- Tests for headers exchange functionality.
- """
- def setUp(self):
- TestBase.setUp(self)
- self.queue_declare(queue="q")
- self.q = self.consume("q")
-
- def myAssertPublishGet(self, headers):
- self.assertPublishGet(self.q, exchange="amq.match", properties=headers)
-
- def myBasicPublish(self, headers):
- self.channel.message_transfer(destination="amq.match", body="foobar", application_headers=headers)
-
- def testMatchAll(self):
- self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'all', "name":"fred", "age":3})
- self.myAssertPublishGet({"name":"fred", "age":3})
- self.myAssertPublishGet({"name":"fred", "age":3, "extra":"ignoreme"})
-
- # None of these should match
- self.myBasicPublish({})
- self.myBasicPublish({"name":"barney"})
- self.myBasicPublish({"name":10})
- self.myBasicPublish({"name":"fred", "age":2})
- self.assertEmpty(self.q)
-
- def testMatchAny(self):
- self.channel.queue_bind(queue="q", exchange="amq.match", arguments={ 'x-match':'any', "name":"fred", "age":3})
- self.myAssertPublishGet({"name":"fred"})
- self.myAssertPublishGet({"name":"fred", "ignoreme":10})
- self.myAssertPublishGet({"ignoreme":10, "age":3})
-
- # Wont match
- self.myBasicPublish({})
- self.myBasicPublish({"irrelevant":0})
- self.assertEmpty(self.q)
-
-
-class MiscellaneousErrorsTests(TestBase):
- """
- Test some miscellaneous error conditions
- """
- def testTypeNotKnown(self):
- try:
- self.channel.exchange_declare(exchange="test_type_not_known_exchange", type="invalid_type")
- self.fail("Expected 503 for declaration of unknown exchange type.")
- except Closed, e:
- self.assertConnectionException(503, e.args[0])
-
- def testDifferentDeclaredType(self):
- self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="direct")
- try:
- self.channel.exchange_declare(exchange="test_different_declared_type_exchange", type="topic")
- self.fail("Expected 530 for redeclaration of exchange with different type.")
- except Closed, e:
- self.assertConnectionException(530, e.args[0])
- #cleanup
- other = self.connect()
- c2 = other.channel(1)
- c2.channel_open()
- c2.exchange_delete(exchange="test_different_declared_type_exchange")
-
diff --git a/qpid/python/tests_0-9/message.py b/qpid/python/tests_0-9/message.py
deleted file mode 100644
index b25016e680..0000000000
--- a/qpid/python/tests_0-9/message.py
+++ /dev/null
@@ -1,657 +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.
-#
-from qpid.client import Client, Closed
-from qpid.queue import Empty
-from qpid.content import Content
-from qpid.testlib import testrunner, TestBase
-from qpid.reference import Reference, ReferenceId
-
-class MessageTests(TestBase):
- """Tests for 'methods' on the amqp message 'class'"""
-
- def test_consume_no_local(self):
- """
- Test that the no_local flag is honoured in the consume method
- """
- channel = self.channel
- #setup, declare two queues:
- channel.queue_declare(queue="test-queue-1a", exclusive=True)
- channel.queue_declare(queue="test-queue-1b", exclusive=True)
- #establish two consumers one of which excludes delivery of locally sent messages
- channel.message_consume(destination="local_included", queue="test-queue-1a")
- channel.message_consume(destination="local_excluded", queue="test-queue-1b", no_local=True)
-
- #send a message
- channel.message_transfer(routing_key="test-queue-1a", body="consume_no_local")
- channel.message_transfer(routing_key="test-queue-1b", body="consume_no_local")
-
- #check the queues of the two consumers
- excluded = self.client.queue("local_excluded")
- included = self.client.queue("local_included")
- msg = included.get(timeout=1)
- self.assertEqual("consume_no_local", msg.body)
- try:
- excluded.get(timeout=1)
- self.fail("Received locally published message though no_local=true")
- except Empty: None
-
-
- def test_consume_exclusive(self):
- """
- Test that the exclusive flag is honoured in the consume method
- """
- channel = self.channel
- #setup, declare a queue:
- channel.queue_declare(queue="test-queue-2", exclusive=True)
-
- #check that an exclusive consumer prevents other consumer being created:
- channel.message_consume(destination="first", queue="test-queue-2", exclusive=True)
- try:
- channel.message_consume(destination="second", queue="test-queue-2")
- self.fail("Expected consume request to fail due to previous exclusive consumer")
- except Closed, e:
- self.assertChannelException(403, e.args[0])
-
- #open new channel and cleanup last consumer:
- channel = self.client.channel(2)
- channel.channel_open()
-
- #check that an exclusive consumer cannot be created if a consumer already exists:
- channel.message_consume(destination="first", queue="test-queue-2")
- try:
- channel.message_consume(destination="second", queue="test-queue-2", exclusive=True)
- self.fail("Expected exclusive consume request to fail due to previous consumer")
- except Closed, e:
- self.assertChannelException(403, e.args[0])
-
- def test_consume_queue_errors(self):
- """
- Test error conditions associated with the queue field of the consume method:
- """
- channel = self.channel
- try:
- #queue specified but doesn't exist:
- channel.message_consume(queue="invalid-queue")
- self.fail("Expected failure when consuming from non-existent queue")
- except Closed, e:
- self.assertChannelException(404, e.args[0])
-
- channel = self.client.channel(2)
- channel.channel_open()
- try:
- #queue not specified and none previously declared for channel:
- channel.message_consume(queue="")
- self.fail("Expected failure when consuming from unspecified queue")
- except Closed, e:
- self.assertConnectionException(530, e.args[0])
-
- def test_consume_unique_consumers(self):
- """
- Ensure unique consumer tags are enforced
- """
- channel = self.channel
- #setup, declare a queue:
- channel.queue_declare(queue="test-queue-3", exclusive=True)
-
- #check that attempts to use duplicate tags are detected and prevented:
- channel.message_consume(destination="first", queue="test-queue-3")
- try:
- channel.message_consume(destination="first", queue="test-queue-3")
- self.fail("Expected consume request to fail due to non-unique tag")
- except Closed, e:
- self.assertConnectionException(530, e.args[0])
-
- def test_cancel(self):
- """
- Test compliance of the basic.cancel method
- """
- channel = self.channel
- #setup, declare a queue:
- channel.queue_declare(queue="test-queue-4", exclusive=True)
- channel.message_consume(destination="my-consumer", queue="test-queue-4")
- channel.message_transfer(routing_key="test-queue-4", body="One")
-
- #cancel should stop messages being delivered
- channel.message_cancel(destination="my-consumer")
- channel.message_transfer(routing_key="test-queue-4", body="Two")
- myqueue = self.client.queue("my-consumer")
- msg = myqueue.get(timeout=1)
- self.assertEqual("One", msg.body)
- try:
- msg = myqueue.get(timeout=1)
- self.fail("Got message after cancellation: " + msg)
- except Empty: None
-
- #cancellation of non-existant consumers should be handled without error
- channel.message_cancel(destination="my-consumer")
- channel.message_cancel(destination="this-never-existed")
-
-
- def test_ack(self):
- """
- Test basic ack/recover behaviour
- """
- channel = self.channel
- channel.queue_declare(queue="test-ack-queue", exclusive=True)
-
- channel.message_consume(queue="test-ack-queue", destination="consumer_tag", no_ack=False)
- queue = self.client.queue("consumer_tag")
-
- channel.message_transfer(routing_key="test-ack-queue", body="One")
- channel.message_transfer(routing_key="test-ack-queue", body="Two")
- channel.message_transfer(routing_key="test-ack-queue", body="Three")
- channel.message_transfer(routing_key="test-ack-queue", body="Four")
- channel.message_transfer(routing_key="test-ack-queue", body="Five")
-
- msg1 = queue.get(timeout=1)
- msg2 = queue.get(timeout=1)
- msg3 = queue.get(timeout=1)
- msg4 = queue.get(timeout=1)
- msg5 = queue.get(timeout=1)
-
- self.assertEqual("One", msg1.body)
- self.assertEqual("Two", msg2.body)
- self.assertEqual("Three", msg3.body)
- self.assertEqual("Four", msg4.body)
- self.assertEqual("Five", msg5.body)
-
- msg1.ok(batchoffset=1)#One and Two
- msg4.ok()
-
- channel.message_recover(requeue=False)
-
- msg3b = queue.get(timeout=1)
- msg5b = queue.get(timeout=1)
-
- self.assertEqual("Three", msg3b.body)
- self.assertEqual("Five", msg5b.body)
-
- try:
- extra = queue.get(timeout=1)
- self.fail("Got unexpected message: " + extra.body)
- except Empty: None
-
- def test_recover_requeue(self):
- """
- Test requeing on recovery
- """
- channel = self.channel
- channel.queue_declare(queue="test-requeue", exclusive=True)
-
- channel.message_consume(queue="test-requeue", destination="consumer_tag", no_ack=False)
- queue = self.client.queue("consumer_tag")
-
- channel.message_transfer(routing_key="test-requeue", body="One")
- channel.message_transfer(routing_key="test-requeue", body="Two")
- channel.message_transfer(routing_key="test-requeue", body="Three")
- channel.message_transfer(routing_key="test-requeue", body="Four")
- channel.message_transfer(routing_key="test-requeue", body="Five")
-
- msg1 = queue.get(timeout=1)
- msg2 = queue.get(timeout=1)
- msg3 = queue.get(timeout=1)
- msg4 = queue.get(timeout=1)
- msg5 = queue.get(timeout=1)
-
- self.assertEqual("One", msg1.body)
- self.assertEqual("Two", msg2.body)
- self.assertEqual("Three", msg3.body)
- self.assertEqual("Four", msg4.body)
- self.assertEqual("Five", msg5.body)
-
- msg1.ok(batchoffset=1) #One and Two
- msg4.ok() #Four
-
- channel.message_cancel(destination="consumer_tag")
-
- #publish a new message
- channel.message_transfer(routing_key="test-requeue", body="Six")
- #requeue unacked messages (Three and Five)
- channel.message_recover(requeue=True)
-
- channel.message_consume(queue="test-requeue", destination="consumer_tag")
- queue2 = self.client.queue("consumer_tag")
-
- msg3b = queue2.get(timeout=1)
- msg5b = queue2.get(timeout=1)
-
- self.assertEqual("Three", msg3b.body)
- self.assertEqual("Five", msg5b.body)
-
- self.assertEqual(True, msg3b.redelivered)
- self.assertEqual(True, msg5b.redelivered)
-
- self.assertEqual("Six", queue2.get(timeout=1).body)
-
- try:
- extra = queue2.get(timeout=1)
- self.fail("Got unexpected message in second queue: " + extra.body)
- except Empty: None
- try:
- extra = queue.get(timeout=1)
- self.fail("Got unexpected message in original queue: " + extra.body)
- except Empty: None
-
-
- def test_qos_prefetch_count(self):
- """
- Test that the prefetch count specified is honoured
- """
- #setup: declare queue and subscribe
- channel = self.channel
- channel.queue_declare(queue="test-prefetch-count", exclusive=True)
- subscription = channel.message_consume(queue="test-prefetch-count", destination="consumer_tag", no_ack=False)
- queue = self.client.queue("consumer_tag")
-
- #set prefetch to 5:
- channel.message_qos(prefetch_count=5)
-
- #publish 10 messages:
- for i in range(1, 11):
- channel.message_transfer(routing_key="test-prefetch-count", body="Message %d" % i)
-
- #only 5 messages should have been delivered:
- for i in range(1, 6):
- msg = queue.get(timeout=1)
- self.assertEqual("Message %d" % i, msg.body)
- try:
- extra = queue.get(timeout=1)
- self.fail("Got unexpected 6th message in original queue: " + extra.body)
- except Empty: None
-
- #ack messages and check that the next set arrive ok:
- #todo: once batching is implmented, send a single response for all messages
- msg.ok(batchoffset=-4)#1-5
-
- for i in range(6, 11):
- msg = queue.get(timeout=1)
- self.assertEqual("Message %d" % i, msg.body)
-
- msg.ok(batchoffset=-4)#6-10
-
- try:
- extra = queue.get(timeout=1)
- self.fail("Got unexpected 11th message in original queue: " + extra.body)
- except Empty: None
-
-
-
- def test_qos_prefetch_size(self):
- """
- Test that the prefetch size specified is honoured
- """
- #setup: declare queue and subscribe
- channel = self.channel
- channel.queue_declare(queue="test-prefetch-size", exclusive=True)
- subscription = channel.message_consume(queue="test-prefetch-size", destination="consumer_tag", no_ack=False)
- queue = self.client.queue("consumer_tag")
-
- #set prefetch to 50 bytes (each message is 9 or 10 bytes):
- channel.message_qos(prefetch_size=50)
-
- #publish 10 messages:
- for i in range(1, 11):
- channel.message_transfer(routing_key="test-prefetch-size", body="Message %d" % i)
-
- #only 5 messages should have been delivered (i.e. 45 bytes worth):
- for i in range(1, 6):
- msg = queue.get(timeout=1)
- self.assertEqual("Message %d" % i, msg.body)
-
- try:
- extra = queue.get(timeout=1)
- self.fail("Got unexpected 6th message in original queue: " + extra.body)
- except Empty: None
-
- #ack messages and check that the next set arrive ok:
- msg.ok(batchoffset=-4)#1-5
-
- for i in range(6, 11):
- msg = queue.get(timeout=1)
- self.assertEqual("Message %d" % i, msg.body)
-
- msg.ok(batchoffset=-4)#6-10
-
- try:
- extra = queue.get(timeout=1)
- self.fail("Got unexpected 11th message in original queue: " + extra.body)
- except Empty: None
-
- #make sure that a single oversized message still gets delivered
- large = "abcdefghijklmnopqrstuvwxyz"
- large = large + "-" + large;
- channel.message_transfer(routing_key="test-prefetch-size", body=large)
- msg = queue.get(timeout=1)
- self.assertEqual(large, msg.body)
-
- def test_get(self):
- """
- Test message_get method
- """
- channel = self.channel
- channel.queue_declare(queue="test-get", exclusive=True)
-
- #publish some messages (no_ack=True)
- for i in range(1, 11):
- channel.message_transfer(routing_key="test-get", body="Message %d" % i)
-
- #use message_get to read back the messages, and check that we get an empty at the end
- for i in range(1, 11):
- tag = "queue %d" % i
- reply = channel.message_get(no_ack=True, queue="test-get", destination=tag)
- self.assertEqual(reply.method.klass.name, "message")
- self.assertEqual(reply.method.name, "ok")
- self.assertEqual("Message %d" % i, self.client.queue(tag).get(timeout=1).body)
-
- reply = channel.message_get(no_ack=True, queue="test-get")
- self.assertEqual(reply.method.klass.name, "message")
- self.assertEqual(reply.method.name, "empty")
-
- #repeat for no_ack=False
- for i in range(11, 21):
- channel.message_transfer(routing_key="test-get", body="Message %d" % i)
-
- for i in range(11, 21):
- tag = "queue %d" % i
- reply = channel.message_get(no_ack=False, queue="test-get", destination=tag)
- self.assertEqual(reply.method.klass.name, "message")
- self.assertEqual(reply.method.name, "ok")
- msg = self.client.queue(tag).get(timeout=1)
- self.assertEqual("Message %d" % i, msg.body)
-
- if (i==13):
- msg.ok(batchoffset=-2)#11, 12 & 13
- if(i in [15, 17, 19]):
- msg.ok()
-
- reply = channel.message_get(no_ack=True, queue="test-get")
- self.assertEqual(reply.method.klass.name, "message")
- self.assertEqual(reply.method.name, "empty")
-
- #recover(requeue=True)
- channel.message_recover(requeue=True)
-
- #get the unacked messages again (14, 16, 18, 20)
- for i in [14, 16, 18, 20]:
- tag = "queue %d" % i
- reply = channel.message_get(no_ack=False, queue="test-get", destination=tag)
- self.assertEqual(reply.method.klass.name, "message")
- self.assertEqual(reply.method.name, "ok")
- msg = self.client.queue(tag).get(timeout=1)
- self.assertEqual("Message %d" % i, msg.body)
- msg.ok()
- #channel.message_ack(delivery_tag=reply.delivery_tag)
-
- reply = channel.message_get(no_ack=True, queue="test-get")
- self.assertEqual(reply.method.klass.name, "message")
- self.assertEqual(reply.method.name, "empty")
-
- channel.message_recover(requeue=True)
-
- reply = channel.message_get(no_ack=True, queue="test-get")
- self.assertEqual(reply.method.klass.name, "message")
- self.assertEqual(reply.method.name, "empty")
-
- def test_reference_simple(self):
- """
- Test basic ability to handle references
- """
- channel = self.channel
- channel.queue_declare(queue="ref_queue", exclusive=True)
- channel.message_consume(queue="ref_queue", destination="c1")
- queue = self.client.queue("c1")
-
- refId = "myref"
- channel.message_open(reference=refId)
- channel.message_append(reference=refId, bytes="abcd")
- channel.synchronous = False
- ack = channel.message_transfer(routing_key="ref_queue", body=ReferenceId(refId))
- channel.synchronous = True
-
- channel.message_append(reference=refId, bytes="efgh")
- channel.message_append(reference=refId, bytes="ijkl")
- channel.message_close(reference=refId)
-
- #first, wait for the ok for the transfer
- ack.get_response(timeout=1)
-
- self.assertDataEquals(channel, queue.get(timeout=1), "abcdefghijkl")
-
-
- def test_reference_large(self):
- """
- Test basic ability to handle references whose content exceeds max frame size
- """
- channel = self.channel
- self.queue_declare(queue="ref_queue")
-
- #generate a big data string (> max frame size of consumer):
- data = "0123456789"
- for i in range(0, 10):
- data += data
- #send it inline
- channel.synchronous = False
- ack = channel.message_transfer(routing_key="ref_queue", body=data)
- channel.synchronous = True
- #first, wait for the ok for the transfer
- ack.get_response(timeout=1)
-
- #create a new connection for consumer, with specific max frame size (< data)
- other = self.connect(tune_params={"channel_max":10, "frame_max":5120, "heartbeat":0})
- ch2 = other.channel(1)
- ch2.channel_open()
- ch2.message_consume(queue="ref_queue", destination="c1")
- queue = other.queue("c1")
-
- msg = queue.get(timeout=1)
- self.assertTrue(isinstance(msg.body, ReferenceId))
- self.assertTrue(msg.reference)
- self.assertEquals(data, msg.reference.get_complete())
-
- def test_reference_completion(self):
- """
- Test that reference transfer are not deemed complete until
- closed (therefore are not acked or routed until that point)
- """
- channel = self.channel
- channel.queue_declare(queue="ref_queue", exclusive=True)
- channel.message_consume(queue="ref_queue", destination="c1")
- queue = self.client.queue("c1")
-
- refId = "myref"
- channel.message_open(reference=refId)
- channel.message_append(reference=refId, bytes="abcd")
- channel.synchronous = False
- ack = channel.message_transfer(routing_key="ref_queue", body=ReferenceId(refId))
- channel.synchronous = True
-
- try:
- msg = queue.get(timeout=1)
- self.fail("Got unexpected message on queue: " + msg)
- except Empty: None
-
- self.assertTrue(not ack.is_complete())
-
- channel.message_close(reference=refId)
-
- #first, wait for the ok for the transfer
- ack.get_response(timeout=1)
-
- self.assertDataEquals(channel, queue.get(timeout=1), "abcd")
-
- def test_reference_multi_transfer(self):
- """
- Test that multiple transfer requests for the same reference are
- correctly handled.
- """
- channel = self.channel
- #declare and consume from two queues
- channel.queue_declare(queue="q-one", exclusive=True)
- channel.queue_declare(queue="q-two", exclusive=True)
- channel.message_consume(queue="q-one", destination="q-one")
- channel.message_consume(queue="q-two", destination="q-two")
- queue1 = self.client.queue("q-one")
- queue2 = self.client.queue("q-two")
-
- #transfer a single ref to both queues (in separate commands)
- channel.message_open(reference="my-ref")
- channel.synchronous = False
- ack1 = channel.message_transfer(routing_key="q-one", body=ReferenceId("my-ref"))
- channel.message_append(reference="my-ref", bytes="my data")
- ack2 = channel.message_transfer(routing_key="q-two", body=ReferenceId("my-ref"))
- channel.synchronous = True
- channel.message_close(reference="my-ref")
-
- #check that both queues have the message
- self.assertDataEquals(channel, queue1.get(timeout=1), "my data")
- self.assertDataEquals(channel, queue2.get(timeout=1), "my data")
- self.assertEmpty(queue1)
- self.assertEmpty(queue2)
-
- #transfer a single ref to the same queue twice (in separate commands)
- channel.message_open(reference="my-ref")
- channel.synchronous = False
- ack1 = channel.message_transfer(routing_key="q-one", message_id="abc", body=ReferenceId("my-ref"))
- channel.message_append(reference="my-ref", bytes="second message")
- ack2 = channel.message_transfer(routing_key="q-one", message_id="xyz", body=ReferenceId("my-ref"))
- channel.synchronous = True
- channel.message_close(reference="my-ref")
-
- msg1 = queue1.get(timeout=1)
- msg2 = queue1.get(timeout=1)
- #order is undefined
- if msg1.message_id == "abc":
- self.assertEquals(msg2.message_id, "xyz")
- else:
- self.assertEquals(msg1.message_id, "xyz")
- self.assertEquals(msg2.message_id, "abc")
-
- #would be legal for the incoming messages to be transfered
- #inline or by reference in any combination
-
- if isinstance(msg1.body, ReferenceId):
- self.assertEquals("second message", msg1.reference.get_complete())
- if isinstance(msg2.body, ReferenceId):
- if msg1.body != msg2.body:
- self.assertEquals("second message", msg2.reference.get_complete())
- #else ok, as same ref as msg1
- else:
- self.assertEquals("second message", msg1.body)
- if isinstance(msg2.body, ReferenceId):
- self.assertEquals("second message", msg2.reference.get_complete())
- else:
- self.assertEquals("second message", msg2.body)
-
- self.assertEmpty(queue1)
-
- def test_reference_unopened_on_append_error(self):
- channel = self.channel
- try:
- channel.message_append(reference="unopened")
- except Closed, e:
- self.assertConnectionException(503, e.args[0])
-
- def test_reference_unopened_on_close_error(self):
- channel = self.channel
- try:
- channel.message_close(reference="unopened")
- except Closed, e:
- self.assertConnectionException(503, e.args[0])
-
- def test_reference_unopened_on_transfer_error(self):
- channel = self.channel
- try:
- channel.message_transfer(body=ReferenceId("unopened"))
- except Closed, e:
- self.assertConnectionException(503, e.args[0])
-
- def test_reference_already_opened_error(self):
- channel = self.channel
- channel.message_open(reference="a")
- try:
- channel.message_open(reference="a")
- except Closed, e:
- self.assertConnectionException(503, e.args[0])
-
- def test_empty_reference(self):
- channel = self.channel
- channel.queue_declare(queue="ref_queue", exclusive=True)
- channel.message_consume(queue="ref_queue", destination="c1")
- queue = self.client.queue("c1")
-
- refId = "myref"
- channel.message_open(reference=refId)
- channel.synchronous = False
- ack = channel.message_transfer(routing_key="ref_queue", message_id="empty-msg", body=ReferenceId(refId))
- channel.synchronous = True
- channel.message_close(reference=refId)
-
- #first, wait for the ok for the transfer
- ack.get_response(timeout=1)
-
- msg = queue.get(timeout=1)
- self.assertEquals(msg.message_id, "empty-msg")
- self.assertDataEquals(channel, msg, "")
-
- def test_reject(self):
- channel = self.channel
- channel.queue_declare(queue = "q", exclusive=True)
-
- channel.message_consume(queue = "q", destination = "consumer")
- channel.message_transfer(routing_key = "q", body="blah, blah")
- msg = self.client.queue("consumer").get(timeout = 1)
- self.assertEquals(msg.body, "blah, blah")
- channel.message_cancel(destination = "consumer")
- msg.reject()
-
- channel.message_consume(queue = "q", destination = "checker")
- msg = self.client.queue("checker").get(timeout = 1)
- self.assertEquals(msg.body, "blah, blah")
-
- def test_checkpoint(self):
- channel = self.channel
- channel.queue_declare(queue = "q", exclusive=True)
-
- channel.message_open(reference="my-ref")
- channel.message_append(reference="my-ref", bytes="abcdefgh")
- channel.message_append(reference="my-ref", bytes="ijklmnop")
- channel.message_checkpoint(reference="my-ref", identifier="my-checkpoint")
- channel.channel_close()
-
- channel = self.client.channel(2)
- channel.channel_open()
- channel.message_consume(queue = "q", destination = "consumer")
- offset = channel.message_resume(reference="my-ref", identifier="my-checkpoint").value
- self.assertTrue(offset<=16)
- channel.message_append(reference="my-ref", bytes="qrstuvwxyz")
- channel.synchronous = False
- channel.message_transfer(routing_key="q-one", message_id="abcd", body=ReferenceId("my-ref"))
- channel.synchronous = True
- channel.message_close(reference="my-ref")
-
- self.assertDataEquals(channel, self.client.queue("consumer").get(timeout = 1), "abcdefghijklmnopqrstuvwxyz")
- self.assertEmpty(self.client.queue("consumer"))
-
-
- def assertDataEquals(self, channel, msg, expected):
- if isinstance(msg.body, ReferenceId):
- data = msg.reference.get_complete()
- else:
- data = msg.body
- self.assertEquals(expected, data)
diff --git a/qpid/python/tests_0-9/query.py b/qpid/python/tests_0-9/query.py
index c2e08c003c..cb66d079e5 100644
--- a/qpid/python/tests_0-9/query.py
+++ b/qpid/python/tests_0-9/query.py
@@ -19,7 +19,7 @@
from qpid.client import Client, Closed
from qpid.queue import Empty
from qpid.content import Content
-from qpid.testlib import testrunner, TestBase
+from qpid.testlib import TestBase
class QueryTests(TestBase):
"""Tests for various query methods introduced in 0-10 and available in 0-9 for preview"""
diff --git a/qpid/python/tests_0-9/queue.py b/qpid/python/tests_0-9/queue.py
index e7fe0b3ed4..de1153307c 100644
--- a/qpid/python/tests_0-9/queue.py
+++ b/qpid/python/tests_0-9/queue.py
@@ -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
@@ -19,137 +19,11 @@
from qpid.client import Client, Closed
from qpid.queue import Empty
from qpid.content import Content
-from qpid.testlib import testrunner, TestBase
+from qpid.testlib import TestBase
class QueueTests(TestBase):
"""Tests for 'methods' on the amqp queue 'class'"""
- def test_purge(self):
- """
- Test that the purge method removes messages from the queue
- """
- channel = self.channel
- #setup, declare a queue and add some messages to it:
- channel.exchange_declare(exchange="test-exchange", type="direct")
- channel.queue_declare(queue="test-queue", exclusive=True)
- channel.queue_bind(queue="test-queue", exchange="test-exchange", routing_key="key")
- channel.message_transfer(destination="test-exchange", routing_key="key", body="one")
- channel.message_transfer(destination="test-exchange", routing_key="key", body="two")
- channel.message_transfer(destination="test-exchange", routing_key="key", body="three")
-
- #check that the queue now reports 3 messages:
- reply = channel.queue_declare(queue="test-queue")
- self.assertEqual(3, reply.message_count)
-
- #now do the purge, then test that three messages are purged and the count drops to 0
- reply = channel.queue_purge(queue="test-queue");
- self.assertEqual(3, reply.message_count)
- reply = channel.queue_declare(queue="test-queue")
- self.assertEqual(0, reply.message_count)
-
- #send a further message and consume it, ensuring that the other messages are really gone
- channel.message_transfer(destination="test-exchange", routing_key="key", body="four")
- channel.message_consume(queue="test-queue", destination="tag", no_ack=True)
- queue = self.client.queue("tag")
- msg = queue.get(timeout=1)
- self.assertEqual("four", msg.body)
-
- #check error conditions (use new channels):
- channel = self.client.channel(2)
- channel.channel_open()
- try:
- #queue specified but doesn't exist:
- channel.queue_purge(queue="invalid-queue")
- self.fail("Expected failure when purging non-existent queue")
- except Closed, e:
- self.assertChannelException(404, e.args[0])
-
- channel = self.client.channel(3)
- channel.channel_open()
- try:
- #queue not specified and none previously declared for channel:
- channel.queue_purge()
- self.fail("Expected failure when purging unspecified queue")
- except Closed, e:
- self.assertConnectionException(530, e.args[0])
-
- #cleanup
- other = self.connect()
- channel = other.channel(1)
- channel.channel_open()
- channel.exchange_delete(exchange="test-exchange")
-
- def test_declare_exclusive(self):
- """
- Test that the exclusive field is honoured in queue.declare
- """
- # TestBase.setUp has already opened channel(1)
- c1 = self.channel
- # Here we open a second separate connection:
- other = self.connect()
- c2 = other.channel(1)
- c2.channel_open()
-
- #declare an exclusive queue:
- c1.queue_declare(queue="exclusive-queue", exclusive="True")
- try:
- #other connection should not be allowed to declare this:
- c2.queue_declare(queue="exclusive-queue", exclusive="True")
- self.fail("Expected second exclusive queue_declare to raise a channel exception")
- except Closed, e:
- self.assertChannelException(405, e.args[0])
-
-
- def test_declare_passive(self):
- """
- Test that the passive field is honoured in queue.declare
- """
- channel = self.channel
- #declare an exclusive queue:
- channel.queue_declare(queue="passive-queue-1", exclusive="True")
- channel.queue_declare(queue="passive-queue-1", passive="True")
- try:
- #other connection should not be allowed to declare this:
- channel.queue_declare(queue="passive-queue-2", passive="True")
- self.fail("Expected passive declaration of non-existant queue to raise a channel exception")
- except Closed, e:
- self.assertChannelException(404, e.args[0])
-
-
- def test_bind(self):
- """
- Test various permutations of the queue.bind method
- """
- channel = self.channel
- channel.queue_declare(queue="queue-1", exclusive="True")
-
- #straightforward case, both exchange & queue exist so no errors expected:
- channel.queue_bind(queue="queue-1", exchange="amq.direct", routing_key="key1")
-
- #bind the default queue for the channel (i.e. last one declared):
- channel.queue_bind(exchange="amq.direct", routing_key="key2")
-
- #use the queue name where neither routing key nor queue are specified:
- channel.queue_bind(exchange="amq.direct")
-
- #try and bind to non-existant exchange
- try:
- channel.queue_bind(queue="queue-1", exchange="an-invalid-exchange", routing_key="key1")
- self.fail("Expected bind to non-existant exchange to fail")
- except Closed, e:
- self.assertChannelException(404, e.args[0])
-
- #need to reopen a channel:
- channel = self.client.channel(2)
- channel.channel_open()
-
- #try and bind non-existant queue:
- try:
- channel.queue_bind(queue="queue-2", exchange="amq.direct", routing_key="key1")
- self.fail("Expected bind of non-existant queue to fail")
- except Closed, e:
- self.assertChannelException(404, e.args[0])
-
def test_unbind_direct(self):
self.unbind_test(exchange="amq.direct", routing_key="key")
@@ -165,12 +39,12 @@ class QueueTests(TestBase):
def unbind_test(self, exchange, routing_key="", args=None, headers={}):
#bind two queues and consume from them
channel = self.channel
-
+
channel.queue_declare(queue="queue-1", exclusive="True")
channel.queue_declare(queue="queue-2", exclusive="True")
- channel.message_consume(queue="queue-1", destination="queue-1", no_ack=True)
- channel.message_consume(queue="queue-2", destination="queue-2", no_ack=True)
+ channel.basic_consume(queue="queue-1", consumer_tag="queue-1", no_ack=True)
+ channel.basic_consume(queue="queue-2", consumer_tag="queue-2", no_ack=True)
queue1 = self.client.queue("queue-1")
queue2 = self.client.queue("queue-2")
@@ -179,130 +53,29 @@ class QueueTests(TestBase):
channel.queue_bind(exchange=exchange, queue="queue-2", routing_key=routing_key, arguments=args)
#send a message that will match both bindings
- channel.message_transfer(destination=exchange, routing_key=routing_key, application_headers=headers, body="one")
-
+ channel.basic_publish(exchange=exchange, routing_key=routing_key,
+ content=Content("one", properties={"headers": headers}))
+
#unbind first queue
channel.queue_unbind(exchange=exchange, queue="queue-1", routing_key=routing_key, arguments=args)
-
+
#send another message
- channel.message_transfer(destination=exchange, routing_key=routing_key, application_headers=headers, body="two")
+ channel.basic_publish(exchange=exchange, routing_key=routing_key,
+ content=Content("two", properties={"headers": headers}))
#check one queue has both messages and the other has only one
- self.assertEquals("one", queue1.get(timeout=1).body)
+ self.assertEquals("one", queue1.get(timeout=1).content.body)
try:
msg = queue1.get(timeout=1)
self.fail("Got extra message: %s" % msg.body)
except Empty: pass
- self.assertEquals("one", queue2.get(timeout=1).body)
- self.assertEquals("two", queue2.get(timeout=1).body)
+ self.assertEquals("one", queue2.get(timeout=1).content.body)
+ self.assertEquals("two", queue2.get(timeout=1).content.body)
try:
msg = queue2.get(timeout=1)
self.fail("Got extra message: " + msg)
- except Empty: pass
-
-
- def test_delete_simple(self):
- """
- Test core queue deletion behaviour
- """
- channel = self.channel
-
- #straight-forward case:
- channel.queue_declare(queue="delete-me")
- channel.message_transfer(routing_key="delete-me", body="a")
- channel.message_transfer(routing_key="delete-me", body="b")
- channel.message_transfer(routing_key="delete-me", body="c")
- reply = channel.queue_delete(queue="delete-me")
- self.assertEqual(3, reply.message_count)
- #check that it has gone be declaring passively
- try:
- channel.queue_declare(queue="delete-me", passive="True")
- self.fail("Queue has not been deleted")
- except Closed, e:
- self.assertChannelException(404, e.args[0])
-
- #check attempted deletion of non-existant queue is handled correctly:
- channel = self.client.channel(2)
- channel.channel_open()
- try:
- channel.queue_delete(queue="i-dont-exist", if_empty="True")
- self.fail("Expected delete of non-existant queue to fail")
- except Closed, e:
- self.assertChannelException(404, e.args[0])
-
-
-
- def test_delete_ifempty(self):
- """
- Test that if_empty field of queue_delete is honoured
- """
- channel = self.channel
-
- #create a queue and add a message to it (use default binding):
- channel.queue_declare(queue="delete-me-2")
- channel.queue_declare(queue="delete-me-2", passive="True")
- channel.message_transfer(routing_key="delete-me-2", body="message")
-
- #try to delete, but only if empty:
- try:
- channel.queue_delete(queue="delete-me-2", if_empty="True")
- self.fail("Expected delete if_empty to fail for non-empty queue")
- except Closed, e:
- self.assertChannelException(406, e.args[0])
-
- #need new channel now:
- channel = self.client.channel(2)
- channel.channel_open()
-
- #empty queue:
- channel.message_consume(destination="consumer_tag", queue="delete-me-2", no_ack=True)
- queue = self.client.queue("consumer_tag")
- msg = queue.get(timeout=1)
- self.assertEqual("message", msg.body)
- channel.message_cancel(destination="consumer_tag")
-
- #retry deletion on empty queue:
- channel.queue_delete(queue="delete-me-2", if_empty="True")
-
- #check that it has gone by declaring passively:
- try:
- channel.queue_declare(queue="delete-me-2", passive="True")
- self.fail("Queue has not been deleted")
- except Closed, e:
- self.assertChannelException(404, e.args[0])
-
- def test_delete_ifunused(self):
- """
- Test that if_unused field of queue_delete is honoured
- """
- channel = self.channel
-
- #create a queue and register a consumer:
- channel.queue_declare(queue="delete-me-3")
- channel.queue_declare(queue="delete-me-3", passive="True")
- channel.message_consume(destination="consumer_tag", queue="delete-me-3", no_ack=True)
-
- #need new channel now:
- channel2 = self.client.channel(2)
- channel2.channel_open()
- #try to delete, but only if empty:
- try:
- channel2.queue_delete(queue="delete-me-3", if_unused="True")
- self.fail("Expected delete if_unused to fail for queue with existing consumer")
- except Closed, e:
- self.assertChannelException(406, e.args[0])
-
-
- channel.message_cancel(destination="consumer_tag")
- channel.queue_delete(queue="delete-me-3", if_unused="True")
- #check that it has gone by declaring passively:
- try:
- channel.queue_declare(queue="delete-me-3", passive="True")
- self.fail("Queue has not been deleted")
- except Closed, e:
- self.assertChannelException(404, e.args[0])
-
+ except Empty: pass
def test_autodelete_shared(self):
"""
@@ -336,5 +109,3 @@ class QueueTests(TestBase):
self.fail("Expected queue to have been deleted")
except Closed, e:
self.assertChannelException(404, e.args[0])
-
-
diff --git a/qpid/python/tests_0-9/testlib.py b/qpid/python/tests_0-9/testlib.py
deleted file mode 100644
index f345fbbd80..0000000000
--- a/qpid/python/tests_0-9/testlib.py
+++ /dev/null
@@ -1,66 +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.
-#
-
-#
-# Tests for the testlib itself.
-#
-
-from qpid.content import Content
-from qpid.testlib import testrunner, TestBase
-from Queue import Empty
-
-import sys
-from traceback import *
-
-def mytrace(frame, event, arg):
- print_stack(frame);
- print "===="
- return mytrace
-
-class TestBaseTest(TestBase):
- """Verify TestBase functions work as expected"""
-
- def testAssertEmptyPass(self):
- """Test assert empty works"""
- self.queue_declare(queue="empty")
- q = self.consume("empty")
- self.assertEmpty(q)
- try:
- q.get(timeout=1)
- self.fail("Queue is not empty.")
- except Empty: None # Ignore
-
- def testAssertEmptyFail(self):
- self.queue_declare(queue="full")
- q = self.consume("full")
- self.channel.message_transfer(routing_key="full", body="")
- try:
- self.assertEmpty(q);
- self.fail("assertEmpty did not assert on non-empty queue")
- except AssertionError: None # Ignore
-
- def testMessageProperties(self):
- """Verify properties are passed with message"""
- props={"x":1, "y":2}
- self.queue_declare(queue="q")
- q = self.consume("q")
- self.assertPublishGet(q, routing_key="q", properties=props)
-
-
-
diff --git a/qpid/python/tests_0-9/tx.py b/qpid/python/tests_0-9/tx.py
deleted file mode 100644
index 0f6b4f5cd1..0000000000
--- a/qpid/python/tests_0-9/tx.py
+++ /dev/null
@@ -1,188 +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.
-#
-from qpid.client import Client, Closed
-from qpid.queue import Empty
-from qpid.content import Content
-from qpid.testlib import testrunner, TestBase
-
-class TxTests(TestBase):
- """
- Tests for 'methods' on the amqp tx 'class'
- """
-
- def test_commit(self):
- """
- Test that commited publishes are delivered and commited acks are not re-delivered
- """
- channel = self.channel
- queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-commit-a", "tx-commit-b", "tx-commit-c")
- channel.tx_commit()
-
- #check results
- for i in range(1, 5):
- msg = queue_c.get(timeout=1)
- self.assertEqual("TxMessage %d" % i, msg.body)
- msg.ok()
-
- msg = queue_b.get(timeout=1)
- self.assertEqual("TxMessage 6", msg.body)
- msg.ok()
-
- msg = queue_a.get(timeout=1)
- self.assertEqual("TxMessage 7", msg.body)
- msg.ok()
-
- for q in [queue_a, queue_b, queue_c]:
- try:
- extra = q.get(timeout=1)
- self.fail("Got unexpected message: " + extra.body)
- except Empty: None
-
- #cleanup
- channel.tx_commit()
-
- def test_auto_rollback(self):
- """
- Test that a channel closed with an open transaction is effectively rolled back
- """
- channel = self.channel
- queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-autorollback-a", "tx-autorollback-b", "tx-autorollback-c")
-
- for q in [queue_a, queue_b, queue_c]:
- try:
- extra = q.get(timeout=1)
- self.fail("Got unexpected message: " + extra.body)
- except Empty: None
-
- channel.tx_rollback()
-
- #check results
- for i in range(1, 5):
- msg = queue_a.get(timeout=1)
- self.assertEqual("Message %d" % i, msg.body)
- msg.ok()
-
- msg = queue_b.get(timeout=1)
- self.assertEqual("Message 6", msg.body)
- msg.ok()
-
- msg = queue_c.get(timeout=1)
- self.assertEqual("Message 7", msg.body)
- msg.ok()
-
- for q in [queue_a, queue_b, queue_c]:
- try:
- extra = q.get(timeout=1)
- self.fail("Got unexpected message: " + extra.body)
- except Empty: None
-
- #cleanup
- channel.tx_commit()
-
- def test_rollback(self):
- """
- Test that rolled back publishes are not delivered and rolled back acks are re-delivered
- """
- channel = self.channel
- queue_a, queue_b, queue_c = self.perform_txn_work(channel, "tx-rollback-a", "tx-rollback-b", "tx-rollback-c")
-
- for q in [queue_a, queue_b, queue_c]:
- try:
- extra = q.get(timeout=1)
- self.fail("Got unexpected message: " + extra.body)
- except Empty: None
-
- channel.tx_rollback()
-
- #check results
- for i in range(1, 5):
- msg = queue_a.get(timeout=1)
- self.assertEqual("Message %d" % i, msg.body)
- msg.ok()
-
- msg = queue_b.get(timeout=1)
- self.assertEqual("Message 6", msg.body)
- msg.ok()
-
- msg = queue_c.get(timeout=1)
- self.assertEqual("Message 7", msg.body)
- msg.ok()
-
- for q in [queue_a, queue_b, queue_c]:
- try:
- extra = q.get(timeout=1)
- self.fail("Got unexpected message: " + extra.body)
- except Empty: None
-
- #cleanup
- channel.tx_commit()
-
- def perform_txn_work(self, channel, name_a, name_b, name_c):
- """
- Utility method that does some setup and some work under a transaction. Used for testing both
- commit and rollback
- """
- #setup:
- channel.queue_declare(queue=name_a, exclusive=True)
- channel.queue_declare(queue=name_b, exclusive=True)
- channel.queue_declare(queue=name_c, exclusive=True)
-
- key = "my_key_" + name_b
- topic = "my_topic_" + name_c
-
- channel.queue_bind(queue=name_b, exchange="amq.direct", routing_key=key)
- channel.queue_bind(queue=name_c, exchange="amq.topic", routing_key=topic)
-
- for i in range(1, 5):
- channel.message_transfer(routing_key=name_a, body="Message %d" % i)
-
- channel.message_transfer(routing_key=key, destination="amq.direct", body="Message 6")
- channel.message_transfer(routing_key=topic, destination="amq.topic", body="Message 7")
-
- channel.tx_select()
-
- #consume and ack messages
- channel.message_consume(queue=name_a, destination="sub_a", no_ack=False)
- queue_a = self.client.queue("sub_a")
- for i in range(1, 5):
- msg = queue_a.get(timeout=1)
- self.assertEqual("Message %d" % i, msg.body)
-
- msg.ok(batchoffset=-3)
-
- channel.message_consume(queue=name_b, destination="sub_b", no_ack=False)
- queue_b = self.client.queue("sub_b")
- msg = queue_b.get(timeout=1)
- self.assertEqual("Message 6", msg.body)
- msg.ok()
-
- sub_c = channel.message_consume(queue=name_c, destination="sub_c", no_ack=False)
- queue_c = self.client.queue("sub_c")
- msg = queue_c.get(timeout=1)
- self.assertEqual("Message 7", msg.body)
- msg.ok()
-
- #publish messages
- for i in range(1, 5):
- channel.message_transfer(routing_key=topic, destination="amq.topic", body="TxMessage %d" % i)
-
- channel.message_transfer(routing_key=key, destination="amq.direct", body="TxMessage 6")
- channel.message_transfer(routing_key=name_a, body="TxMessage 7")
-
- return queue_a, queue_b, queue_c
diff --git a/qpid/review/agenda.py b/qpid/review/agenda.py
index 22948e3ced..6ad20362cb 100755
--- a/qpid/review/agenda.py
+++ b/qpid/review/agenda.py
@@ -21,17 +21,36 @@
import sys, re
from popen2 import popen2, popen3
from optparse import OptionParser
+from xml.dom.minidom import parse, parseString
prereqs = ["tr", "svn", "xsltproc", "sed", "grep", "wget"]
-svncmd = "svn log https://svn.apache.org/repos/asf/incubator/qpid --xml -r %s:HEAD | tr '\\n\\r|' ' -' | xsltproc svnlog2wiki.xsl - | grep r | sed -e 's/^ *//' | sed -e 's/\\(QPID-[0-9]*\\)/\\[\\1 | https:\\/\\/issues.apache.org\\/jira\\/browse\\/\\1 \]/g'"
+svncmd = "svn log https://svn.apache.org/repos/asf/qpid/trunk/qpid/java --xml -r %s:HEAD | tr '\\n\\r|' ' -' | xsltproc svnlog2wiki.xsl - | grep r | sed -e 's/^ *//' | sed -e 's/\\(QPID-[0-9]*\\)/\\[\\1 | https:\\/\\/issues.apache.org\\/jira\\/browse\\/\\1 \]/g'"
jiracmd = "wget -q -O - http://issues.apache.org/jira/sr/jira.issueviews:searchrequest-xml/12312564/SearchRequest-12312564.xml?tempMax=1000 | tr '[]|' '()-' | xsltproc jiraRSS2wiki.xsl - | grep '|' | sed -e 's/^ *//'"
def get_commits(revision):
(stdout, stdin) = popen2(svncmd % revision)
- return stdout.read()
+ return add_jira_status(stdout.read())
+
+def add_jira_status(commits):
+ commit_lines = commits.split("\n")
+ new_commits = []
+ for commit in commit_lines:
+ if re.match(".*https://issues.apache.org/.*", commit):
+ jira = re.findall("QPID-[0-9]*", commit)[0]
+ jira_xml_url = "http://issues.apache.org/jira/si/jira.issueviews:issue-xml/%s/%s.xml" % (jira, jira)
+ (stdout, stdin) = popen2("wget -q -O - %s" % jira_xml_url)
+
+ jira_dom = parse(stdout)
+ status = jira_dom.getElementsByTagName("status")[0]
+ new_commits.append("%s %s | " % (commit, status.lastChild.data))
+ else:
+ new_commits.append(commit)
+
+ return "\n".join(new_commits)
+
def get_jiras():
(stdout, stdin) = popen2(jiracmd)
diff --git a/qpid/review/svnlog2wiki.xsl b/qpid/review/svnlog2wiki.xsl
index b705115cfd..8fe2fbf033 100644
--- a/qpid/review/svnlog2wiki.xsl
+++ b/qpid/review/svnlog2wiki.xsl
@@ -22,7 +22,7 @@
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"></xsl:output>
<xsl:template match="/">
-|| revision || committer || date || comment || review notes ||
+|| revision || committer || date || comment || review notes || jira status ||
<xsl:apply-templates select="log/logentry"></xsl:apply-templates>
</xsl:template>
<xsl:template match="logentry">
diff --git a/qpid/specs/amqp.0-9.xml b/qpid/specs/amqp.0-9.xml
index 1615fea99d..73cace7015 100644
--- a/qpid/specs/amqp.0-9.xml
+++ b/qpid/specs/amqp.0-9.xml
@@ -1659,29 +1659,28 @@
<doc>This method confirms the deletion of an exchange.</doc>
<chassis name = "client" implement = "MUST" />
</method>
-
- <!-- RG : Added Exchange.bound and Exchange.bound-ok -->
- <method name="bound" synchronous="1" index="22">
- <chassis name="server" implement="SHOULD"/>
- <field name="exchange" domain="exchange-name"/>
- <field name = "routing-key" type = "shortstr">
- Message routing key
- <doc>
- Specifies the routing key for the message. The routing key is
- used for routing messages depending on the exchange configuration.
- </doc>
- </field>
- <field name = "queue" domain = "queue name"/>
+
+ <!-- RG : Added Exchange.bound and Exchange.bound-ok -->
+ <method name="bound" synchronous="1" index="22">
+ <chassis name="server" implement="SHOULD"/>
+ <response name="bound-ok"/>
+ <field name="exchange" domain="exchange-name"/>
+ <field name = "routing-key" type = "shortstr">
+ Message routing key
+ <doc>
+ Specifies the routing key for the message. The routing key is
+ used for routing messages depending on the exchange configuration.
+ </doc>
+ </field>
+ <field name = "queue" domain = "queue name"/>
</method>
<method name="bound-ok" synchronous="1" index="23">
- <field name="reply-code" domain="reply-code"/>
- <field name="reply-text" domain="reply-text"/>
- <chassis name="client" implement="SHOULD"/>
+ <field name="reply-code" domain="reply-code"/>
+ <field name="reply-text" domain="reply-text"/>
+ <chassis name="client" implement="SHOULD"/>
</method>
-
-
</class>
<!-- == QUEUE ============================================================ -->