diff options
Diffstat (limited to 'qpid')
316 files changed, 7116 insertions, 3723 deletions
diff --git a/qpid/QPID_VERSION.txt b/qpid/QPID_VERSION.txt index b63ba696b7..51176c7c89 100644 --- a/qpid/QPID_VERSION.txt +++ b/qpid/QPID_VERSION.txt @@ -1 +1 @@ -0.9 +0.11 diff --git a/qpid/bin/release.sh b/qpid/bin/release.sh index 31c12e630c..e323b40e51 100755 --- a/qpid/bin/release.sh +++ b/qpid/bin/release.sh @@ -41,6 +41,8 @@ usage() echo "--ruby |-r : Generate the ruby artefacts" echo "--python|-p : Generate the python artefacts" echo "--wcf |-w : Generate the WCF artefacts" + echo "--tools |-t : Generate the tools artefacts" + echo "--qmf |-q : Generate the QMF artefacts" echo "--source|-e : Generate the source artefact" echo "--sign |-s : Sign generated artefacts" echo "--upload|-u : Upload the artifacts directory to people.apache.org as qpid-\$VER" @@ -79,6 +81,8 @@ for arg in $* ; do RUBY="RUBY" PYTHON="PYTHON" WCF="WCF" + TOOLS="TOOLS" + QMF="QMF" SOURCE="SOURCE" ;; --cpp|-c) @@ -99,6 +103,12 @@ for arg in $* ; do --wcf|-w) WCF="WCF" ;; + --tools|-t) + TOOLS="TOOLS" + ;; + --qmf|-q) + QMF="QMF" + ;; --source|-e) SOURCE="SOURCE" ;; @@ -218,8 +228,8 @@ if [ "JAVA" == "$JAVA" ] ; then cp qpid-${VER}/java/management/eclipse-plugin/release/*.tar.gz qpid-${VER}/java/management/eclipse-plugin/release/*.zip artifacts/ # copy the Maven artifacts - cp qpid-${VER}/java/client/release/maven artifacts/ - cp qpid-${VER}/java/common/release/maven artifacts/ + cp -a qpid-${VER}/java/client/release/maven artifacts/ + cp -a qpid-${VER}/java/common/release/maven artifacts/ fi if [ "DOTNET" == "$DOTNET" ] ; then @@ -239,6 +249,26 @@ if [ "DOTNET" == "$DOTNET" ] ; then cp qpid-${VER}/dotnet/client-010/bin/mono-2.0/debug/*.zip artifacts/qpid-dotnet-0-10-${VER}.zip fi +if [ "TOOLS" = "$TOOLS" ] ; then + pushd qpid-${VER}/tools + + python setup.py sdist + + popd + + cp qpid-${VER}/tools/dist/*.tar.gz artifacts/qpid-tools-${VER}.tar.gz +fi + +if [ "QMF" = "$QMF" ]; then + pushd qpid-${VER}/extras/qmf + + python setup.py sdist + + popd + + cp qpid-${VER}/extras/qmf/dist/*.tar.gz artifacts/qpid-qmf-${VER}.tar.gz +fi + if [ "SIGN" == "$SIGN" ] ; then pushd artifacts sha1sum *.zip *.gz *.svnversion > SHA1SUM diff --git a/qpid/cpp/bindings/qpid/Makefile.am b/qpid/cpp/bindings/qpid/Makefile.am index ca9eda0c73..31bce5d1d5 100644 --- a/qpid/cpp/bindings/qpid/Makefile.am +++ b/qpid/cpp/bindings/qpid/Makefile.am @@ -21,7 +21,7 @@ SUBDIRS = dotnet if HAVE_SWIG -EXTRA_DIST = qpid.i +EXTRA_DIST = CMakeLists.txt qpid.i if HAVE_RUBY_DEVEL SUBDIRS += ruby diff --git a/qpid/cpp/bindings/qpid/perl/Makefile.am b/qpid/cpp/bindings/qpid/perl/Makefile.am index 13ab66f032..9e47786b8a 100644 --- a/qpid/cpp/bindings/qpid/perl/Makefile.am +++ b/qpid/cpp/bindings/qpid/perl/Makefile.am @@ -21,7 +21,7 @@ if HAVE_PERL_DEVEL INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src -I$(top_builddir)/src -I$(PERL_INC) -EXTRA_DIST = perl.i +EXTRA_DIST = CMakeLists.txt perl.i BUILT_SOURCES = cqpid_perl.cpp SWIG_FLAGS = -w362,401 diff --git a/qpid/cpp/bindings/qpid/python/Makefile.am b/qpid/cpp/bindings/qpid/python/Makefile.am index 7fa4106be0..9aef179db7 100644 --- a/qpid/cpp/bindings/qpid/python/Makefile.am +++ b/qpid/cpp/bindings/qpid/python/Makefile.am @@ -25,7 +25,7 @@ generated_file_list = \ cqpid.cpp \ cqpid.py -EXTRA_DIST = python.i +EXTRA_DIST = CMakeLists.txt python.i BUILT_SOURCES = $(generated_file_list) SWIG_FLAGS = -w362,401 diff --git a/qpid/cpp/bindings/qpid/ruby/Makefile.am b/qpid/cpp/bindings/qpid/ruby/Makefile.am index 67a3615362..34f9990574 100644 --- a/qpid/cpp/bindings/qpid/ruby/Makefile.am +++ b/qpid/cpp/bindings/qpid/ruby/Makefile.am @@ -21,7 +21,7 @@ if HAVE_RUBY_DEVEL INCLUDES = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_srcdir)/src -I$(top_builddir)/src -EXTRA_DIST = ruby.i +EXTRA_DIST = CMakeLists.txt ruby.i BUILT_SOURCES = cqpid.cpp SWIG_FLAGS = -w362,401 diff --git a/qpid/cpp/bld-winsdk.ps1 b/qpid/cpp/bld-winsdk.ps1 index 8f0a5886dc..bea46da28f 100644 --- a/qpid/cpp/bld-winsdk.ps1 +++ b/qpid/cpp/bld-winsdk.ps1 @@ -186,9 +186,6 @@ function BuildAPlatform 'examples/qmf-console', 'examples/request-response', 'examples/tradedemo', - 'examples/old-examples.sln', - 'examples/README.*', - 'examples/verify*', 'include', 'plugins') diff --git a/qpid/cpp/configure.ac b/qpid/cpp/configure.ac index ea1a1b49ea..43a32d3ad7 100644 --- a/qpid/cpp/configure.ac +++ b/qpid/cpp/configure.ac @@ -521,18 +521,19 @@ AM_PATH_PYTHON() builddir_lib_suffix="/.libs" AC_SUBST([builddir_lib_suffix]) -# Files to generate +# Files to generate AC_CONFIG_FILES([ Makefile examples/Makefile - examples/direct/Makefile - examples/fanout/Makefile - examples/pub-sub/Makefile - examples/request-response/Makefile - examples/failover/Makefile - examples/xml-exchange/Makefile + examples/old_api/Makefile + examples/old_api/direct/Makefile + examples/old_api/fanout/Makefile + examples/old_api/pub-sub/Makefile + examples/old_api/request-response/Makefile + examples/old_api/failover/Makefile + examples/old_api/xml-exchange/Makefile examples/qmf-console/Makefile - examples/tradedemo/Makefile + examples/old_api/tradedemo/Makefile examples/messaging/Makefile bindings/qpid/Makefile bindings/qpid/ruby/Makefile diff --git a/qpid/cpp/examples/CMakeLists.txt b/qpid/cpp/examples/CMakeLists.txt index da8e39e944..1b28cfd031 100644 --- a/qpid/cpp/examples/CMakeLists.txt +++ b/qpid/cpp/examples/CMakeLists.txt @@ -77,25 +77,14 @@ macro(add_example subdir example) endmacro(add_example) install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/README.txt - ${CMAKE_CURRENT_SOURCE_DIR}/README.verify - ${CMAKE_CURRENT_SOURCE_DIR}/verify - ${CMAKE_CURRENT_SOURCE_DIR}/verify_all DESTINATION ${QPID_INSTALL_EXAMPLESDIR} COMPONENT ${QPID_COMPONENT_EXAMPLES}) if (MSVC) install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/examples.sln - ${CMAKE_CURRENT_SOURCE_DIR}/old-examples.sln DESTINATION ${QPID_INSTALL_EXAMPLESDIR} COMPONENT ${QPID_COMPONENT_EXAMPLES}) endif (MSVC) -add_subdirectory(direct) -add_subdirectory(failover) -add_subdirectory(fanout) -add_subdirectory(pub-sub) -#add_subdirectory(qmf-agent) add_subdirectory(qmf-console) -add_subdirectory(request-response) -add_subdirectory(tradedemo) -add_subdirectory(xml-exchange) add_subdirectory(messaging) +add_subdirectory(old_api) diff --git a/qpid/cpp/examples/Makefile.am b/qpid/cpp/examples/Makefile.am index c6cc308d98..6b2bb73587 100644 --- a/qpid/cpp/examples/Makefile.am +++ b/qpid/cpp/examples/Makefile.am @@ -16,15 +16,7 @@ # specific language governing permissions and limitations # under the License. # -SUBDIRS = direct fanout pub-sub request-response failover qmf-console tradedemo messaging -if HAVE_XML - SUBDIRS += xml-exchange - broker_args = "--no-module-dir --data-dir \"\" --auth no --load-module $(top_builddir)/src/.libs/xml.so" -endif -if !HAVE_XML - exclude_examples_regexp="xml" # Exclude XML examples. - broker_args = "--no-module-dir --data-dir \"\" --auth no" -endif +SUBDIRS = qmf-console messaging old_api MAKEDIST=.libs/Makefile @@ -37,13 +29,9 @@ $(MAKEDIST): Makefile examplesdir=$(pkgdatadir)/examples dist_examples_DATA = README.txt $(MAKEDIST) -EXTRA_DIST = README.verify verify verify_all examples.sln CMakeLists.txt +EXTRA_DIST = examples.sln CMakeLists.txt # For older versions of automake abs_top_srcdir = @abs_top_srcdir@ abs_top_builddir = @abs_top_builddir@ -# Verify the examples in the buid tree. -check-local: - $(srcdir)/verify_all $(abs_top_srcdir)/.. $(abs_top_builddir) $(broker_args) $(exclude_examples_regexp) - diff --git a/qpid/cpp/examples/examples.sln b/qpid/cpp/examples/examples.sln index 8511fe3cce..6f96105d97 100644 --- a/qpid/cpp/examples/examples.sln +++ b/qpid/cpp/examples/examples.sln @@ -32,6 +32,14 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "messaging_drain", "messagin EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "messaging_spout", "messaging\messaging_spout.vcproj", "{D3115AC9-91C4-4D79-BCAC-DE837C70F1EA}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_console", "qmf-console\qmf-console_console.vcproj", "{490473E1-FECA-1BAD-2E13-3FFA2B8669C3}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_ping", "qmf-console\qmf-console_ping.vcproj", "{C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_printevents", "qmf-console\qmf-console_printevents.vcproj", "{72C74624-FECA-1BAD-2E13-3FFA2B8669C3}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_queuestats", "qmf-console\qmf-console_queuestats.vcproj", "{B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -88,6 +96,22 @@ Global {D3115AC9-91C4-4D79-BCAC-DE837C70F1EA}.Release|Win32.Build.0 = Release|Win32
{D3115AC9-91C4-4D79-BCAC-DE837C70F1EA}.Release|x64.ActiveCfg = Release|x64
{D3115AC9-91C4-4D79-BCAC-DE837C70F1EA}.Release|x64.Build.0 = Release|x64
+ {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.Build.0 = Debug|Win32
+ {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.ActiveCfg = Release|Win32
+ {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.Build.0 = Release|Win32
+ {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Debug|Win32.Build.0 = Debug|Win32
+ {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Release|Win32.ActiveCfg = Release|Win32
+ {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Release|Win32.Build.0 = Release|Win32
+ {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.Build.0 = Debug|Win32
+ {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.ActiveCfg = Release|Win32
+ {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.Build.0 = Release|Win32
+ {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.ActiveCfg = Debug|Win32
+ {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.Build.0 = Debug|Win32
+ {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.ActiveCfg = Release|Win32
+ {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/qpid/cpp/examples/messaging/server.cpp b/qpid/cpp/examples/messaging/server.cpp index ab72694c61..aa271d91f9 100644 --- a/qpid/cpp/examples/messaging/server.cpp +++ b/qpid/cpp/examples/messaging/server.cpp @@ -39,8 +39,8 @@ using std::string; int main(int argc, char** argv) { const char* url = argc>1 ? argv[1] : "amqp:tcp:127.0.0.1:5672"; - std::string connectionOptions = argc > 3 ? argv[3] : ""; - + std::string connectionOptions = argc > 2 ? argv[2] : ""; + Connection connection(url, connectionOptions); try { connection.open(); diff --git a/qpid/cpp/examples/old_api/CMakeLists.txt b/qpid/cpp/examples/old_api/CMakeLists.txt new file mode 100644 index 0000000000..701f9be860 --- /dev/null +++ b/qpid/cpp/examples/old_api/CMakeLists.txt @@ -0,0 +1,25 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +add_subdirectory(direct) +add_subdirectory(failover) +add_subdirectory(fanout) +add_subdirectory(pub-sub) +add_subdirectory(request-response) +add_subdirectory(tradedemo) +add_subdirectory(xml-exchange) diff --git a/qpid/cpp/examples/old_api/Makefile.am b/qpid/cpp/examples/old_api/Makefile.am new file mode 100644 index 0000000000..466eee22e1 --- /dev/null +++ b/qpid/cpp/examples/old_api/Makefile.am @@ -0,0 +1,48 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +SUBDIRS = direct pub-sub fanout request-response failover tradedemo +if HAVE_XML + SUBDIRS += xml-exchange + broker_args = "--no-module-dir --data-dir \"\" --auth no --load-module $(top_builddir)/src/.libs/xml.so" +endif +if !HAVE_XML + exclude_examples_regexp="xml" # Exclude XML examples. + broker_args = "--no-module-dir --data-dir \"\" --auth no" +endif + +MAKEDIST=.libs/Makefile + +$(MAKEDIST): Makefile + mkdir -p .libs + @(echo 'all clean:' ; \ + echo ' for d in $(SUBDIRS) ; do $$(MAKE) -C $$$$d $$@ ; done' ; \ + ) > $(MAKEDIST) + +examplesdir=$(pkgdatadir)/examples/old_api +dist_examples_DATA = $(MAKEDIST) +EXTRA_DIST = README.verify verify verify_all + +# For older versions of automake +abs_top_srcdir = @abs_top_srcdir@ +abs_top_builddir = @abs_top_builddir@ + +# Verify the examples in the buid tree. +check-local: + $(srcdir)/verify_all $(abs_top_srcdir)/.. $(abs_top_builddir) $(broker_args) $(exclude_examples_regexp) + diff --git a/qpid/cpp/examples/README.verify b/qpid/cpp/examples/old_api/README.verify index e1370764c9..e1370764c9 100644 --- a/qpid/cpp/examples/README.verify +++ b/qpid/cpp/examples/old_api/README.verify diff --git a/qpid/cpp/examples/direct/CMakeLists.txt b/qpid/cpp/examples/old_api/direct/CMakeLists.txt index 2ec1b2b813..2ec1b2b813 100644 --- a/qpid/cpp/examples/direct/CMakeLists.txt +++ b/qpid/cpp/examples/old_api/direct/CMakeLists.txt diff --git a/qpid/cpp/examples/direct/Makefile.am b/qpid/cpp/examples/old_api/direct/Makefile.am index b07db2cfd6..24f783fcc7 100644 --- a/qpid/cpp/examples/direct/Makefile.am +++ b/qpid/cpp/examples/old_api/direct/Makefile.am @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. # -examplesdir=$(pkgdatadir)/examples/direct +examplesdir=$(pkgdatadir)/examples/old_api/direct MAKELDFLAGS=$(CLIENTFLAGS) include $(top_srcdir)/examples/makedist.mk diff --git a/qpid/cpp/examples/direct/declare_queues.cpp b/qpid/cpp/examples/old_api/direct/declare_queues.cpp index 9a51d1982b..9a51d1982b 100644 --- a/qpid/cpp/examples/direct/declare_queues.cpp +++ b/qpid/cpp/examples/old_api/direct/declare_queues.cpp diff --git a/qpid/cpp/examples/direct/direct_declare_queues.vcproj b/qpid/cpp/examples/old_api/direct/direct_declare_queues.vcproj index 083474b9ef..083474b9ef 100644 --- a/qpid/cpp/examples/direct/direct_declare_queues.vcproj +++ b/qpid/cpp/examples/old_api/direct/direct_declare_queues.vcproj diff --git a/qpid/cpp/examples/direct/direct_direct_producer.vcproj b/qpid/cpp/examples/old_api/direct/direct_direct_producer.vcproj index f091fbf291..f091fbf291 100644 --- a/qpid/cpp/examples/direct/direct_direct_producer.vcproj +++ b/qpid/cpp/examples/old_api/direct/direct_direct_producer.vcproj diff --git a/qpid/cpp/examples/direct/direct_listener.vcproj b/qpid/cpp/examples/old_api/direct/direct_listener.vcproj index dce1d3ec28..dce1d3ec28 100644 --- a/qpid/cpp/examples/direct/direct_listener.vcproj +++ b/qpid/cpp/examples/old_api/direct/direct_listener.vcproj diff --git a/qpid/cpp/examples/direct/direct_producer.cpp b/qpid/cpp/examples/old_api/direct/direct_producer.cpp index ecc9675189..ecc9675189 100644 --- a/qpid/cpp/examples/direct/direct_producer.cpp +++ b/qpid/cpp/examples/old_api/direct/direct_producer.cpp diff --git a/qpid/cpp/examples/direct/listener.cpp b/qpid/cpp/examples/old_api/direct/listener.cpp index 38bf24ec41..38bf24ec41 100644 --- a/qpid/cpp/examples/direct/listener.cpp +++ b/qpid/cpp/examples/old_api/direct/listener.cpp diff --git a/qpid/cpp/examples/direct/verify b/qpid/cpp/examples/old_api/direct/verify index f598bacc1f..f598bacc1f 100644 --- a/qpid/cpp/examples/direct/verify +++ b/qpid/cpp/examples/old_api/direct/verify diff --git a/qpid/cpp/examples/direct/verify.in b/qpid/cpp/examples/old_api/direct/verify.in index d1e95f1151..d1e95f1151 100644 --- a/qpid/cpp/examples/direct/verify.in +++ b/qpid/cpp/examples/old_api/direct/verify.in diff --git a/qpid/cpp/examples/failover/CMakeLists.txt b/qpid/cpp/examples/old_api/failover/CMakeLists.txt index 05db8fad51..05db8fad51 100644 --- a/qpid/cpp/examples/failover/CMakeLists.txt +++ b/qpid/cpp/examples/old_api/failover/CMakeLists.txt diff --git a/qpid/cpp/examples/failover/Makefile.am b/qpid/cpp/examples/old_api/failover/Makefile.am index 48846fdf79..8b1da80f2c 100644 --- a/qpid/cpp/examples/failover/Makefile.am +++ b/qpid/cpp/examples/old_api/failover/Makefile.am @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. # -examplesdir=$(pkgdatadir)/examples/failover +examplesdir=$(pkgdatadir)/examples/old_api/failover MAKELDFLAGS=$(CLIENTFLAGS) include $(top_srcdir)/examples/makedist.mk diff --git a/qpid/cpp/examples/failover/declare_queues.cpp b/qpid/cpp/examples/old_api/failover/declare_queues.cpp index a677870c53..a677870c53 100644 --- a/qpid/cpp/examples/failover/declare_queues.cpp +++ b/qpid/cpp/examples/old_api/failover/declare_queues.cpp diff --git a/qpid/cpp/examples/failover/failover_declare_queues.vcproj b/qpid/cpp/examples/old_api/failover/failover_declare_queues.vcproj index c87c72affd..c87c72affd 100644 --- a/qpid/cpp/examples/failover/failover_declare_queues.vcproj +++ b/qpid/cpp/examples/old_api/failover/failover_declare_queues.vcproj diff --git a/qpid/cpp/examples/failover/failover_replaying_sender.vcproj b/qpid/cpp/examples/old_api/failover/failover_replaying_sender.vcproj index 6d22fa6770..6d22fa6770 100644 --- a/qpid/cpp/examples/failover/failover_replaying_sender.vcproj +++ b/qpid/cpp/examples/old_api/failover/failover_replaying_sender.vcproj diff --git a/qpid/cpp/examples/failover/failover_resuming_receiver.vcproj b/qpid/cpp/examples/old_api/failover/failover_resuming_receiver.vcproj index ba5061e248..ba5061e248 100644 --- a/qpid/cpp/examples/failover/failover_resuming_receiver.vcproj +++ b/qpid/cpp/examples/old_api/failover/failover_resuming_receiver.vcproj diff --git a/qpid/cpp/examples/failover/replaying_sender.cpp b/qpid/cpp/examples/old_api/failover/replaying_sender.cpp index 22a7e1ebd3..22a7e1ebd3 100644 --- a/qpid/cpp/examples/failover/replaying_sender.cpp +++ b/qpid/cpp/examples/old_api/failover/replaying_sender.cpp diff --git a/qpid/cpp/examples/failover/resuming_receiver.cpp b/qpid/cpp/examples/old_api/failover/resuming_receiver.cpp index d1886ce861..d1886ce861 100644 --- a/qpid/cpp/examples/failover/resuming_receiver.cpp +++ b/qpid/cpp/examples/old_api/failover/resuming_receiver.cpp diff --git a/qpid/cpp/examples/fanout/CMakeLists.txt b/qpid/cpp/examples/old_api/fanout/CMakeLists.txt index 3f89d67650..3f89d67650 100644 --- a/qpid/cpp/examples/fanout/CMakeLists.txt +++ b/qpid/cpp/examples/old_api/fanout/CMakeLists.txt diff --git a/qpid/cpp/examples/fanout/Makefile.am b/qpid/cpp/examples/old_api/fanout/Makefile.am index 6e2e821eae..3ab43b0279 100644 --- a/qpid/cpp/examples/fanout/Makefile.am +++ b/qpid/cpp/examples/old_api/fanout/Makefile.am @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. # -examplesdir=$(pkgdatadir)/examples/fanout +examplesdir=$(pkgdatadir)/examples/old_api/fanout MAKELDFLAGS=$(CLIENTFLAGS) include $(top_srcdir)/examples/makedist.mk diff --git a/qpid/cpp/examples/fanout/fanout_fanout_producer.vcproj b/qpid/cpp/examples/old_api/fanout/fanout_fanout_producer.vcproj index daff5f3cf0..daff5f3cf0 100644 --- a/qpid/cpp/examples/fanout/fanout_fanout_producer.vcproj +++ b/qpid/cpp/examples/old_api/fanout/fanout_fanout_producer.vcproj diff --git a/qpid/cpp/examples/fanout/fanout_listener.vcproj b/qpid/cpp/examples/old_api/fanout/fanout_listener.vcproj index f0e91b7dc6..f0e91b7dc6 100644 --- a/qpid/cpp/examples/fanout/fanout_listener.vcproj +++ b/qpid/cpp/examples/old_api/fanout/fanout_listener.vcproj diff --git a/qpid/cpp/examples/fanout/fanout_producer.cpp b/qpid/cpp/examples/old_api/fanout/fanout_producer.cpp index decd4d314d..decd4d314d 100644 --- a/qpid/cpp/examples/fanout/fanout_producer.cpp +++ b/qpid/cpp/examples/old_api/fanout/fanout_producer.cpp diff --git a/qpid/cpp/examples/fanout/listener.cpp b/qpid/cpp/examples/old_api/fanout/listener.cpp index cd3071c29a..cd3071c29a 100644 --- a/qpid/cpp/examples/fanout/listener.cpp +++ b/qpid/cpp/examples/old_api/fanout/listener.cpp diff --git a/qpid/cpp/examples/fanout/verify b/qpid/cpp/examples/old_api/fanout/verify index 2eaadff56b..2eaadff56b 100644 --- a/qpid/cpp/examples/fanout/verify +++ b/qpid/cpp/examples/old_api/fanout/verify diff --git a/qpid/cpp/examples/fanout/verify.in b/qpid/cpp/examples/old_api/fanout/verify.in index 8f8612ce67..8f8612ce67 100644 --- a/qpid/cpp/examples/fanout/verify.in +++ b/qpid/cpp/examples/old_api/fanout/verify.in diff --git a/qpid/cpp/examples/old-examples.sln b/qpid/cpp/examples/old_api/old-examples.sln index 7f2fa3e8b0..e6ec9a0d66 100644 --- a/qpid/cpp/examples/old-examples.sln +++ b/qpid/cpp/examples/old_api/old-examples.sln @@ -40,14 +40,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pub_sub_topic_listener", "p EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pub_sub_topic_publisher", "pub-sub\pub-sub_topic_publisher.vcproj", "{05158653-FECA-1BAD-A430-FD5330E23A2D}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_console", "qmf-console\qmf-console_console.vcproj", "{490473E1-FECA-1BAD-2E13-3FFA2B8669C3}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_ping", "qmf-console\qmf-console_ping.vcproj", "{C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_printevents", "qmf-console\qmf-console_printevents.vcproj", "{72C74624-FECA-1BAD-2E13-3FFA2B8669C3}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qmf_console_queuestats", "qmf-console\qmf-console_queuestats.vcproj", "{B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}"
-EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "request_response_client", "request-response\request-response_client.vcproj", "{2691FE1E-FECA-1BAD-BD3A-8A467D0C5CCC}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "request_response_server", "request-response\request-response_server.vcproj", "{46817425-FECA-1BAD-BD3A-8A467D0C5CCC}"
@@ -104,22 +96,6 @@ Global {05158653-FECA-1BAD-A430-FD5330E23A2D}.Debug|Win32.Build.0 = Debug|Win32
{05158653-FECA-1BAD-A430-FD5330E23A2D}.Release|Win32.ActiveCfg = Release|Win32
{05158653-FECA-1BAD-A430-FD5330E23A2D}.Release|Win32.Build.0 = Release|Win32
- {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.ActiveCfg = Debug|Win32
- {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.Build.0 = Debug|Win32
- {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.ActiveCfg = Release|Win32
- {490473E1-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.Build.0 = Release|Win32
- {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Debug|Win32.ActiveCfg = Debug|Win32
- {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Debug|Win32.Build.0 = Debug|Win32
- {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Release|Win32.ActiveCfg = Release|Win32
- {C1FFDE95-3442-49AE-9985-7EEE3D45B4A3}.Release|Win32.Build.0 = Release|Win32
- {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.ActiveCfg = Debug|Win32
- {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.Build.0 = Debug|Win32
- {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.ActiveCfg = Release|Win32
- {72C74624-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.Build.0 = Release|Win32
- {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.ActiveCfg = Debug|Win32
- {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Debug|Win32.Build.0 = Debug|Win32
- {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.ActiveCfg = Release|Win32
- {B21825EA-FECA-1BAD-2E13-3FFA2B8669C3}.Release|Win32.Build.0 = Release|Win32
{2691FE1E-FECA-1BAD-BD3A-8A467D0C5CCC}.Debug|Win32.ActiveCfg = Debug|Win32
{2691FE1E-FECA-1BAD-BD3A-8A467D0C5CCC}.Debug|Win32.Build.0 = Debug|Win32
{2691FE1E-FECA-1BAD-BD3A-8A467D0C5CCC}.Release|Win32.ActiveCfg = Release|Win32
diff --git a/qpid/cpp/examples/pub-sub/CMakeLists.txt b/qpid/cpp/examples/old_api/pub-sub/CMakeLists.txt index 961de06d5a..961de06d5a 100644 --- a/qpid/cpp/examples/pub-sub/CMakeLists.txt +++ b/qpid/cpp/examples/old_api/pub-sub/CMakeLists.txt diff --git a/qpid/cpp/examples/pub-sub/Makefile.am b/qpid/cpp/examples/old_api/pub-sub/Makefile.am index 62658ebe94..8f42ee0211 100644 --- a/qpid/cpp/examples/pub-sub/Makefile.am +++ b/qpid/cpp/examples/old_api/pub-sub/Makefile.am @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. # -examplesdir=$(pkgdatadir)/examples/pub-sub +examplesdir=$(pkgdatadir)/examples/old_api/pub-sub MAKELDFLAGS=$(CLIENTFLAGS) include $(top_srcdir)/examples/makedist.mk diff --git a/qpid/cpp/examples/pub-sub/pub-sub_topic_listener.vcproj b/qpid/cpp/examples/old_api/pub-sub/pub-sub_topic_listener.vcproj index aa0b3bcaa3..aa0b3bcaa3 100644 --- a/qpid/cpp/examples/pub-sub/pub-sub_topic_listener.vcproj +++ b/qpid/cpp/examples/old_api/pub-sub/pub-sub_topic_listener.vcproj diff --git a/qpid/cpp/examples/pub-sub/pub-sub_topic_publisher.vcproj b/qpid/cpp/examples/old_api/pub-sub/pub-sub_topic_publisher.vcproj index 76e51df4df..76e51df4df 100644 --- a/qpid/cpp/examples/pub-sub/pub-sub_topic_publisher.vcproj +++ b/qpid/cpp/examples/old_api/pub-sub/pub-sub_topic_publisher.vcproj diff --git a/qpid/cpp/examples/pub-sub/topic_listener.cpp b/qpid/cpp/examples/old_api/pub-sub/topic_listener.cpp index d38a806303..d38a806303 100644 --- a/qpid/cpp/examples/pub-sub/topic_listener.cpp +++ b/qpid/cpp/examples/old_api/pub-sub/topic_listener.cpp diff --git a/qpid/cpp/examples/pub-sub/topic_publisher.cpp b/qpid/cpp/examples/old_api/pub-sub/topic_publisher.cpp index aed5f8f033..aed5f8f033 100644 --- a/qpid/cpp/examples/pub-sub/topic_publisher.cpp +++ b/qpid/cpp/examples/old_api/pub-sub/topic_publisher.cpp diff --git a/qpid/cpp/examples/pub-sub/verify b/qpid/cpp/examples/old_api/pub-sub/verify index 528d2f401e..528d2f401e 100644 --- a/qpid/cpp/examples/pub-sub/verify +++ b/qpid/cpp/examples/old_api/pub-sub/verify diff --git a/qpid/cpp/examples/pub-sub/verify.in b/qpid/cpp/examples/old_api/pub-sub/verify.in index 6413c5c788..6413c5c788 100644 --- a/qpid/cpp/examples/pub-sub/verify.in +++ b/qpid/cpp/examples/old_api/pub-sub/verify.in diff --git a/qpid/cpp/examples/request-response/CMakeLists.txt b/qpid/cpp/examples/old_api/request-response/CMakeLists.txt index 873a0cfa86..873a0cfa86 100644 --- a/qpid/cpp/examples/request-response/CMakeLists.txt +++ b/qpid/cpp/examples/old_api/request-response/CMakeLists.txt diff --git a/qpid/cpp/examples/request-response/Makefile.am b/qpid/cpp/examples/old_api/request-response/Makefile.am index 48b3d989f0..f48762da51 100644 --- a/qpid/cpp/examples/request-response/Makefile.am +++ b/qpid/cpp/examples/old_api/request-response/Makefile.am @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. # -examplesdir=$(pkgdatadir)/examples/request-response +examplesdir=$(pkgdatadir)/examples/old_api/request-response MAKELDFLAGS=$(CLIENTFLAGS) include $(top_srcdir)/examples/makedist.mk diff --git a/qpid/cpp/examples/request-response/client.cpp b/qpid/cpp/examples/old_api/request-response/client.cpp index 679d1c5fc2..679d1c5fc2 100644 --- a/qpid/cpp/examples/request-response/client.cpp +++ b/qpid/cpp/examples/old_api/request-response/client.cpp diff --git a/qpid/cpp/examples/request-response/request-response_client.vcproj b/qpid/cpp/examples/old_api/request-response/request-response_client.vcproj index 5f9eadde36..5f9eadde36 100644 --- a/qpid/cpp/examples/request-response/request-response_client.vcproj +++ b/qpid/cpp/examples/old_api/request-response/request-response_client.vcproj diff --git a/qpid/cpp/examples/request-response/request-response_server.vcproj b/qpid/cpp/examples/old_api/request-response/request-response_server.vcproj index 54352b9f46..54352b9f46 100644 --- a/qpid/cpp/examples/request-response/request-response_server.vcproj +++ b/qpid/cpp/examples/old_api/request-response/request-response_server.vcproj diff --git a/qpid/cpp/examples/request-response/server.cpp b/qpid/cpp/examples/old_api/request-response/server.cpp index 65a4717b35..65a4717b35 100644 --- a/qpid/cpp/examples/request-response/server.cpp +++ b/qpid/cpp/examples/old_api/request-response/server.cpp diff --git a/qpid/cpp/examples/request-response/verify b/qpid/cpp/examples/old_api/request-response/verify index dee82413e7..dee82413e7 100644 --- a/qpid/cpp/examples/request-response/verify +++ b/qpid/cpp/examples/old_api/request-response/verify diff --git a/qpid/cpp/examples/request-response/verify.in b/qpid/cpp/examples/old_api/request-response/verify.in index 7925dc5671..7925dc5671 100644 --- a/qpid/cpp/examples/request-response/verify.in +++ b/qpid/cpp/examples/old_api/request-response/verify.in diff --git a/qpid/cpp/examples/tradedemo/CMakeLists.txt b/qpid/cpp/examples/old_api/tradedemo/CMakeLists.txt index e61fc1467d..e61fc1467d 100644 --- a/qpid/cpp/examples/tradedemo/CMakeLists.txt +++ b/qpid/cpp/examples/old_api/tradedemo/CMakeLists.txt diff --git a/qpid/cpp/examples/tradedemo/Makefile.am b/qpid/cpp/examples/old_api/tradedemo/Makefile.am index f4d8686d05..445b15b367 100644 --- a/qpid/cpp/examples/tradedemo/Makefile.am +++ b/qpid/cpp/examples/old_api/tradedemo/Makefile.am @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. # -examplesdir=$(pkgdatadir)/examples/tradedemo +examplesdir=$(pkgdatadir)/examples/old_api/tradedemo MAKELDFLAGS=$(CLIENTFLAGS) include $(top_srcdir)/examples/makedist.mk diff --git a/qpid/cpp/examples/tradedemo/declare_queues.cpp b/qpid/cpp/examples/old_api/tradedemo/declare_queues.cpp index b1f2cc3510..b1f2cc3510 100644 --- a/qpid/cpp/examples/tradedemo/declare_queues.cpp +++ b/qpid/cpp/examples/old_api/tradedemo/declare_queues.cpp diff --git a/qpid/cpp/examples/tradedemo/topic_listener.cpp b/qpid/cpp/examples/old_api/tradedemo/topic_listener.cpp index c488e7fb69..c488e7fb69 100644 --- a/qpid/cpp/examples/tradedemo/topic_listener.cpp +++ b/qpid/cpp/examples/old_api/tradedemo/topic_listener.cpp diff --git a/qpid/cpp/examples/tradedemo/topic_publisher.cpp b/qpid/cpp/examples/old_api/tradedemo/topic_publisher.cpp index e22c185bc7..e22c185bc7 100644 --- a/qpid/cpp/examples/tradedemo/topic_publisher.cpp +++ b/qpid/cpp/examples/old_api/tradedemo/topic_publisher.cpp diff --git a/qpid/cpp/examples/tradedemo/tradedemo_declare_queues.vcproj b/qpid/cpp/examples/old_api/tradedemo/tradedemo_declare_queues.vcproj index 34b5cb3b2b..34b5cb3b2b 100644 --- a/qpid/cpp/examples/tradedemo/tradedemo_declare_queues.vcproj +++ b/qpid/cpp/examples/old_api/tradedemo/tradedemo_declare_queues.vcproj diff --git a/qpid/cpp/examples/tradedemo/tradedemo_topic_listener.vcproj b/qpid/cpp/examples/old_api/tradedemo/tradedemo_topic_listener.vcproj index 965be2e88b..965be2e88b 100644 --- a/qpid/cpp/examples/tradedemo/tradedemo_topic_listener.vcproj +++ b/qpid/cpp/examples/old_api/tradedemo/tradedemo_topic_listener.vcproj diff --git a/qpid/cpp/examples/tradedemo/tradedemo_topic_publisher.vcproj b/qpid/cpp/examples/old_api/tradedemo/tradedemo_topic_publisher.vcproj index 77fd511e15..77fd511e15 100644 --- a/qpid/cpp/examples/tradedemo/tradedemo_topic_publisher.vcproj +++ b/qpid/cpp/examples/old_api/tradedemo/tradedemo_topic_publisher.vcproj diff --git a/qpid/cpp/examples/verify b/qpid/cpp/examples/old_api/verify index 9a1ed078d6..9a1ed078d6 100755 --- a/qpid/cpp/examples/verify +++ b/qpid/cpp/examples/old_api/verify diff --git a/qpid/cpp/examples/verify_all b/qpid/cpp/examples/old_api/verify_all index cb4c5283fa..fbe51377b6 100755 --- a/qpid/cpp/examples/verify_all +++ b/qpid/cpp/examples/old_api/verify_all @@ -32,7 +32,7 @@ trap "$qpidd -q" exit QPID_PORT=`$qpidd -dp0 $broker_args` || { echo "Can't run qpidd" ; exit 1; } export QPID_PORT -find="find $topsrcdir/cpp/examples" +find="find $topsrcdir/cpp/examples/old_api" find="$find -mindepth 2 -name verify" all_examples=`$find` diff --git a/qpid/cpp/examples/xml-exchange/CMakeLists.txt b/qpid/cpp/examples/old_api/xml-exchange/CMakeLists.txt index 3fea47a208..3fea47a208 100644 --- a/qpid/cpp/examples/xml-exchange/CMakeLists.txt +++ b/qpid/cpp/examples/old_api/xml-exchange/CMakeLists.txt diff --git a/qpid/cpp/examples/xml-exchange/Makefile.am b/qpid/cpp/examples/old_api/xml-exchange/Makefile.am index 9001e3fa61..3e1082cdb2 100644 --- a/qpid/cpp/examples/xml-exchange/Makefile.am +++ b/qpid/cpp/examples/old_api/xml-exchange/Makefile.am @@ -16,7 +16,7 @@ # specific language governing permissions and limitations # under the License. # -examplesdir=$(pkgdatadir)/examples/xml-exchange +examplesdir=$(pkgdatadir)/examples/old_api/xml-exchange MAKELDFLAGS=$(CLIENTFLAGS) include $(top_srcdir)/examples/makedist.mk diff --git a/qpid/cpp/examples/xml-exchange/README.txt b/qpid/cpp/examples/old_api/xml-exchange/README.txt index 85caebe352..85caebe352 100644 --- a/qpid/cpp/examples/xml-exchange/README.txt +++ b/qpid/cpp/examples/old_api/xml-exchange/README.txt diff --git a/qpid/cpp/examples/xml-exchange/declare_queues.cpp b/qpid/cpp/examples/old_api/xml-exchange/declare_queues.cpp index ad08642019..ad08642019 100644 --- a/qpid/cpp/examples/xml-exchange/declare_queues.cpp +++ b/qpid/cpp/examples/old_api/xml-exchange/declare_queues.cpp diff --git a/qpid/cpp/examples/xml-exchange/listener.cpp b/qpid/cpp/examples/old_api/xml-exchange/listener.cpp index 11bcb9f669..11bcb9f669 100644 --- a/qpid/cpp/examples/xml-exchange/listener.cpp +++ b/qpid/cpp/examples/old_api/xml-exchange/listener.cpp diff --git a/qpid/cpp/examples/xml-exchange/xml_producer.cpp b/qpid/cpp/examples/old_api/xml-exchange/xml_producer.cpp index af1a7e60c7..af1a7e60c7 100644 --- a/qpid/cpp/examples/xml-exchange/xml_producer.cpp +++ b/qpid/cpp/examples/old_api/xml-exchange/xml_producer.cpp diff --git a/qpid/cpp/include/qmf/AgentSession.h b/qpid/cpp/include/qmf/AgentSession.h index 9e29d6b54b..39d921a8ec 100644 --- a/qpid/cpp/include/qmf/AgentSession.h +++ b/qpid/cpp/include/qmf/AgentSession.h @@ -72,14 +72,14 @@ namespace qmf { * strict-security:{True,False} - If True: Cooperate with the broker to enforce strict access control to the network * - If False: Operate more flexibly with regard to use of messaging facilities [default] */ - QMF_EXTERN AgentSession(qpid::messaging::Connection&, const std::string& options=""); + QMF_EXTERN AgentSession(qpid::messaging::Connection& conn, const std::string& options=""); /** * setDomain - Change the QMF domain that this agent will operate in. If this is not called, * the domain will be "default". Agents in a domain can be seen only by consoles in the same domain. * This must be called prior to opening the agent session. */ - QMF_EXTERN void setDomain(const std::string&); + QMF_EXTERN void setDomain(const std::string& domain); /** * Set identifying attributes of this agent. @@ -88,16 +88,16 @@ namespace qmf { * setInstance - Set the unique instance name (if not set, a UUID will be assigned) * These must be called prior to opening the agent session. */ - QMF_EXTERN void setVendor(const std::string&); - QMF_EXTERN void setProduct(const std::string&); - QMF_EXTERN void setInstance(const std::string&); + QMF_EXTERN void setVendor(const std::string& vendor); + QMF_EXTERN void setProduct(const std::string& product); + QMF_EXTERN void setInstance(const std::string& instance); /** * setAttribute - Set an arbitrary attribute for this agent. The attributes are not used * to uniquely identify the agent but can be used as a search criteria when looking for agents. * This must be called prior to opening the agent session. */ - QMF_EXTERN void setAttribute(const std::string&, const qpid::types::Variant&); + QMF_EXTERN void setAttribute(const std::string& key, const qpid::types::Variant& value); /** * Get the identifying name of the agent. @@ -119,13 +119,19 @@ namespace qmf { * Get the next event from the agent session. Events represent actions that must be acted upon by the * agent application. This method blocks for up to the timeout if there are no events to be handled. * This method will typically be the focus of the agent application's main execution loop. + * If the timeout is set to Duration::IMMEDIATE, the call will not block. */ - QMF_EXTERN bool nextEvent(AgentEvent&, qpid::messaging::Duration timeout=qpid::messaging::Duration::FOREVER); + QMF_EXTERN bool nextEvent(AgentEvent& outEvent, qpid::messaging::Duration timeout=qpid::messaging::Duration::FOREVER); + + /** + * Return the number of events pending for nextEvent. This method will never block. + */ + QMF_EXTERN int pendingEvents() const; /** * Register a schema to be exposed by this agent. */ - QMF_EXTERN void registerSchema(Schema&); + QMF_EXTERN void registerSchema(Schema& schema); /** * Add data to be managed internally by the agent. If the option external:True is selected, this call @@ -138,12 +144,12 @@ namespace qmf { * across different sessions. If persistent, it is the agent application's * responsibility to ensure the name is the same each time it is added. */ - QMF_EXTERN DataAddr addData(Data&, const std::string& name="", bool persistent=false); + QMF_EXTERN DataAddr addData(Data& data, const std::string& name="", bool persistent=false); /** * Delete data from internal agent management. */ - QMF_EXTERN void delData(const DataAddr&); + QMF_EXTERN void delData(const DataAddr& dataAddr); /** * The following methods are used to respond to events received in nextEvent. @@ -155,13 +161,13 @@ namespace qmf { * complete - Indicate that the response to a query is complete (external:True only) * methodSuccess - Indicate the successful completion of a method call. */ - QMF_EXTERN void authAccept(AgentEvent&); - QMF_EXTERN void authReject(AgentEvent&, const std::string& diag=""); - QMF_EXTERN void raiseException(AgentEvent&, const std::string&); - QMF_EXTERN void raiseException(AgentEvent&, const Data&); - QMF_EXTERN void response(AgentEvent&, const Data&); - QMF_EXTERN void complete(AgentEvent&); - QMF_EXTERN void methodSuccess(AgentEvent&); + QMF_EXTERN void authAccept(AgentEvent& event); + QMF_EXTERN void authReject(AgentEvent& event, const std::string& diag=""); + QMF_EXTERN void raiseException(AgentEvent& event, const std::string& errorText); + QMF_EXTERN void raiseException(AgentEvent& event, const Data& errorData); + QMF_EXTERN void response(AgentEvent& event, const Data& responseData); + QMF_EXTERN void complete(AgentEvent& event); + QMF_EXTERN void methodSuccess(AgentEvent& event); /** * Raise an event to be sent into the QMF network. diff --git a/qpid/cpp/include/qmf/ConsoleSession.h b/qpid/cpp/include/qmf/ConsoleSession.h index 0c73e7a6db..896f8ffda7 100644 --- a/qpid/cpp/include/qmf/ConsoleSession.h +++ b/qpid/cpp/include/qmf/ConsoleSession.h @@ -62,14 +62,48 @@ namespace qmf { * strict-security:{True,False} - If True: Cooperate with the broker to enforce strict access control to the network * - If False: Operate more flexibly with regard to use of messaging facilities [default] */ - QMF_EXTERN ConsoleSession(qpid::messaging::Connection&, const std::string& options=""); - QMF_EXTERN void setDomain(const std::string&); - QMF_EXTERN void setAgentFilter(const std::string&); + QMF_EXTERN ConsoleSession(qpid::messaging::Connection& conn, const std::string& options=""); + + /** + * setDomain - Change the QMF domain that this console will operate in. If this is not called, + * the domain will be "default". Agents in a domain can be seen only by consoles in the same domain. + * This must be called prior to opening the console session. + */ + QMF_EXTERN void setDomain(const std::string& domain); + QMF_EXTERN void setAgentFilter(const std::string& filter); + + /** + * Open the console session. After opening the session, the domain cannot be changed. + */ QMF_EXTERN void open(); + + /** + * Close the session. Once closed, the session no longer communicates on the messaging network. + */ QMF_EXTERN void close(); - QMF_EXTERN bool nextEvent(ConsoleEvent&, qpid::messaging::Duration timeout=qpid::messaging::Duration::FOREVER); + + /** + * Get the next event from the console session. Events represent actions that must be acted upon by the + * console application. This method blocks for up to the timeout if there are no events to be handled. + * This method will typically be the focus of the console application's main execution loop. + * If the timeout is set to Duration::IMMEDIATE, the call will not block. + */ + QMF_EXTERN bool nextEvent(ConsoleEvent& outEvent, qpid::messaging::Duration timeout=qpid::messaging::Duration::FOREVER); + + /** + * Return the number of events pending for nextEvent. This method will never block. + */ + QMF_EXTERN int pendingEvents() const; + + /** + * getAgentCount, getAgent - Retrieve the set of agents that match the console session's agent filter. + */ QMF_EXTERN uint32_t getAgentCount() const; - QMF_EXTERN Agent getAgent(uint32_t) const; + QMF_EXTERN Agent getAgent(uint32_t agentIndex) const; + + /** + * Get the agent for the connected broker (i.e. the agent embedded in the broker to which we have a connection). + */ QMF_EXTERN Agent getConnectedBrokerAgent() const; /** @@ -79,8 +113,8 @@ namespace qmf { * will involve all known agents. If agentFilter is non-empty, it will be applied only to the set of known * agents. A subscription cannot be created that involves an agent not known by the session. */ - QMF_EXTERN Subscription subscribe(const Query&, const std::string& agentFilter = "", const std::string& options = ""); - QMF_EXTERN Subscription subscribe(const std::string&, const std::string& agentFilter = "", const std::string& options = ""); + QMF_EXTERN Subscription subscribe(const Query& query, const std::string& agentFilter = "", const std::string& options = ""); + QMF_EXTERN Subscription subscribe(const std::string& query, const std::string& agentFilter = "", const std::string& options = ""); #ifndef SWIG private: diff --git a/qpid/cpp/include/qpid/types/Variant.h b/qpid/cpp/include/qpid/types/Variant.h index 9ae672b7c2..d9260133a7 100644 --- a/qpid/cpp/include/qpid/types/Variant.h +++ b/qpid/cpp/include/qpid/types/Variant.h @@ -60,6 +60,8 @@ enum VariantType { VAR_UUID }; +std::string getTypeName(VariantType type); + class VariantImpl; /** diff --git a/qpid/cpp/src/CMakeWinVersions.cmake b/qpid/cpp/src/CMakeWinVersions.cmake index 9bffd2ba0e..0bac7cab47 100644 --- a/qpid/cpp/src/CMakeWinVersions.cmake +++ b/qpid/cpp/src/CMakeWinVersions.cmake @@ -34,11 +34,11 @@ # set ("winver_PACKAGE_NAME" "qpid-cpp")
# set ("winver_DESCRIPTION_SUMMARY" "Apache Qpid C++")
# set ("winver_FILE_VERSION_N1" "0")
-# set ("winver_FILE_VERSION_N2" "9")
+# set ("winver_FILE_VERSION_N2" "11")
# set ("winver_FILE_VERSION_N3" "0")
# set ("winver_FILE_VERSION_N4" "0")
# set ("winver_PRODUCT_VERSION_N1" "0")
-# set ("winver_PRODUCT_VERSION_N2" "9")
+# set ("winver_PRODUCT_VERSION_N2" "11")
# set ("winver_PRODUCT_VERSION_N3" "0")
# set ("winver_PRODUCT_VERSION_N4" "0")
# set ("winver_LEGAL_COPYRIGHT" "")
@@ -46,10 +46,10 @@ #
# Specification of per-project settings:
#
-# set ("winver_${projectName}_FileVersionBinary" "0,9,0,0")
-# set ("winver_${projectName}_ProductVersionBinary" "0,9,0,0")
-# set ("winver_${projectName}_FileVersionString" "0, 9, 0, 0")
-# set ("winver_${projectName}_ProductVersionString" "0, 9, 0, 0")
+# set ("winver_${projectName}_FileVersionBinary" "0,11,0,0")
+# set ("winver_${projectName}_ProductVersionBinary" "0,11,0,0")
+# set ("winver_${projectName}_FileVersionString" "0, 11, 0, 0")
+# set ("winver_${projectName}_ProductVersionString" "0, 11, 0, 0")
# set ("winver_${projectName}_FileDescription" "qpid-cpp-qpidcommon Library")
# set ("winver_${projectName}_LegalCopyright" "")
# set ("winver_${projectName}_InternalName" "qpidcommon")
diff --git a/qpid/cpp/src/qmf/AgentSession.cpp b/qpid/cpp/src/qmf/AgentSession.cpp index 4c5a72a467..71d369325f 100644 --- a/qpid/cpp/src/qmf/AgentSession.cpp +++ b/qpid/cpp/src/qmf/AgentSession.cpp @@ -72,6 +72,7 @@ namespace qmf { void open(); void close(); bool nextEvent(AgentEvent& e, Duration t); + int pendingEvents() const; void registerSchema(Schema& s); DataAddr addData(Data& d, const string& n, bool persist); @@ -161,6 +162,7 @@ const string& AgentSession::getName() const { return impl->getName(); } void AgentSession::open() { impl->open(); } void AgentSession::close() { impl->close(); } bool AgentSession::nextEvent(AgentEvent& e, Duration t) { return impl->nextEvent(e, t); } +int AgentSession::pendingEvents() const { return impl->pendingEvents(); } void AgentSession::registerSchema(Schema& s) { impl->registerSchema(s); } DataAddr AgentSession::addData(Data& d, const string& n, bool p) { return impl->addData(d, n, p); } void AgentSession::delData(const DataAddr& a) { impl->delData(a); } @@ -318,7 +320,7 @@ bool AgentSessionImpl::nextEvent(AgentEvent& event, Duration timeout) uint64_t milliseconds = timeout.getMilliseconds(); qpid::sys::Mutex::ScopedLock l(lock); - if (eventQueue.empty()) + if (eventQueue.empty() && milliseconds > 0) cond.wait(lock, qpid::sys::AbsTime(qpid::sys::now(), qpid::sys::Duration(milliseconds * qpid::sys::TIME_MSEC))); @@ -332,6 +334,13 @@ bool AgentSessionImpl::nextEvent(AgentEvent& event, Duration timeout) } +int AgentSessionImpl::pendingEvents() const +{ + qpid::sys::Mutex::ScopedLock l(lock); + return eventQueue.size(); +} + + void AgentSessionImpl::registerSchema(Schema& schema) { if (!schema.isFinalized()) diff --git a/qpid/cpp/src/qmf/ConsoleSession.cpp b/qpid/cpp/src/qmf/ConsoleSession.cpp index e12c1152f6..7b839930e1 100644 --- a/qpid/cpp/src/qmf/ConsoleSession.cpp +++ b/qpid/cpp/src/qmf/ConsoleSession.cpp @@ -54,6 +54,7 @@ void ConsoleSession::setAgentFilter(const string& f) { impl->setAgentFilter(f); void ConsoleSession::open() { impl->open(); } void ConsoleSession::close() { impl->close(); } bool ConsoleSession::nextEvent(ConsoleEvent& e, Duration t) { return impl->nextEvent(e, t); } +int ConsoleSession::pendingEvents() const { return impl->pendingEvents(); } uint32_t ConsoleSession::getAgentCount() const { return impl->getAgentCount(); } Agent ConsoleSession::getAgent(uint32_t i) const { return impl->getAgent(i); } Agent ConsoleSession::getConnectedBrokerAgent() const { return impl->getConnectedBrokerAgent(); } @@ -213,7 +214,7 @@ bool ConsoleSessionImpl::nextEvent(ConsoleEvent& event, Duration timeout) uint64_t milliseconds = timeout.getMilliseconds(); qpid::sys::Mutex::ScopedLock l(lock); - if (eventQueue.empty()) + if (eventQueue.empty() && milliseconds > 0) cond.wait(lock, qpid::sys::AbsTime(qpid::sys::now(), qpid::sys::Duration(milliseconds * qpid::sys::TIME_MSEC))); @@ -227,6 +228,13 @@ bool ConsoleSessionImpl::nextEvent(ConsoleEvent& event, Duration timeout) } +int ConsoleSessionImpl::pendingEvents() const +{ + qpid::sys::Mutex::ScopedLock l(lock); + return eventQueue.size(); +} + + uint32_t ConsoleSessionImpl::getAgentCount() const { qpid::sys::Mutex::ScopedLock l(lock); @@ -421,7 +429,23 @@ void ConsoleSessionImpl::handleAgentUpdate(const string& agentName, const Varian iter = content.find("_values"); if (iter == content.end()) return; - Variant::Map attrs(iter->second.asMap()); + const Variant::Map& in_attrs(iter->second.asMap()); + Variant::Map attrs; + + // + // Copy the map from the message to "attrs". Translate any old-style + // keys to their new key values in the process. + // + for (iter = in_attrs.begin(); iter != in_attrs.end(); iter++) { + if (iter->first == "epoch") + attrs[protocol::AGENT_ATTR_EPOCH] = iter->second; + else if (iter->first == "timestamp") + attrs[protocol::AGENT_ATTR_TIMESTAMP] = iter->second; + else if (iter->first == "heartbeat_interval") + attrs[protocol::AGENT_ATTR_HEARTBEAT_INTERVAL] = iter->second; + else + attrs[iter->first] = iter->second; + } iter = attrs.find(protocol::AGENT_ATTR_EPOCH); if (iter != attrs.end()) diff --git a/qpid/cpp/src/qmf/ConsoleSessionImpl.h b/qpid/cpp/src/qmf/ConsoleSessionImpl.h index 675c8bcfb5..411b3f016a 100644 --- a/qpid/cpp/src/qmf/ConsoleSessionImpl.h +++ b/qpid/cpp/src/qmf/ConsoleSessionImpl.h @@ -58,6 +58,7 @@ namespace qmf { void open(); void close(); bool nextEvent(ConsoleEvent& e, qpid::messaging::Duration t); + int pendingEvents() const; uint32_t getAgentCount() const; Agent getAgent(uint32_t i) const; Agent getConnectedBrokerAgent() const { return connectedBrokerAgent; } diff --git a/qpid/cpp/src/qpid/broker/AsyncCompletion.h b/qpid/cpp/src/qpid/broker/AsyncCompletion.h index 1f3d11e0ee..fef994438f 100644 --- a/qpid/cpp/src/qpid/broker/AsyncCompletion.h +++ b/qpid/cpp/src/qpid/broker/AsyncCompletion.h @@ -22,6 +22,8 @@ * */ +#include <boost/intrusive_ptr.hpp> + #include "qpid/broker/BrokerImportExport.h" #include "qpid/sys/AtomicValue.h" #include "qpid/sys/Mutex.h" @@ -77,6 +79,22 @@ namespace broker { class AsyncCompletion { + public: + + /** Supplied by the Initiator to the end() method, allows for a callback + * when all outstanding completers are done. If the callback cannot be + * made during the end() call, the clone() method must supply a copy of + * this callback object that persists after end() returns. The cloned + * callback object will be used by the last completer thread, and + * released when the callback returns. + */ + class Callback : public RefCounted + { + public: + virtual void completed(bool) = 0; + virtual boost::intrusive_ptr<Callback> clone() = 0; + }; + private: mutable qpid::sys::AtomicValue<uint32_t> completionsNeeded; mutable qpid::sys::Monitor callbackLock; @@ -85,14 +103,17 @@ class AsyncCompletion void invokeCallback(bool sync) { qpid::sys::Mutex::ScopedLock l(callbackLock); if (active) { - inCallback = true; - { - qpid::sys::Mutex::ScopedUnlock ul(callbackLock); - completed(sync); + if (callback.get()) { + inCallback = true; + { + qpid::sys::Mutex::ScopedUnlock ul(callbackLock); + callback->completed(sync); + } + inCallback = false; + callback = boost::intrusive_ptr<Callback>(); + callbackLock.notifyAll(); } - inCallback = false; active = false; - callbackLock.notifyAll(); } } @@ -100,17 +121,17 @@ class AsyncCompletion /** Invoked when all completers have signalled that they have completed * (via calls to finishCompleter()). bool == true if called via end() */ - virtual void completed(bool) = 0; + boost::intrusive_ptr<Callback> callback; public: AsyncCompletion() : completionsNeeded(0), inCallback(false), active(true) {}; virtual ~AsyncCompletion() { cancel(); } + /** True when all outstanding operations have compeleted */ bool isDone() { - qpid::sys::Mutex::ScopedLock l(callbackLock); return !active; } @@ -135,17 +156,32 @@ class AsyncCompletion */ void begin() { - qpid::sys::Mutex::ScopedLock l(callbackLock); ++completionsNeeded; } /** called by initiator after all potential completers have called * startCompleter(). */ - void end() + void end(Callback& cb) { assert(completionsNeeded.get() > 0); // ensure begin() has been called! + // the following only "decrements" the count if it is 1. This means + // there are no more outstanding completers and we are done. + if (completionsNeeded.boolCompareAndSwap(1, 0)) { + // done! Complete immediately + cb.completed(true); + return; + } + + // the compare-and-swap did not succeed. This means there are + // outstanding completers pending (count > 1). Get a persistent + // Callback object to use when the last completer is done. + // Decrement after setting up the callback ensures that pending + // completers cannot touch the callback until it is ready. + callback = cb.clone(); if (--completionsNeeded == 0) { + // note that a completer may have completed during the + // callback setup or decrement: invokeCallback(true); } } @@ -156,14 +192,9 @@ class AsyncCompletion virtual void cancel() { qpid::sys::Mutex::ScopedLock l(callbackLock); while (inCallback) callbackLock.wait(); + callback = boost::intrusive_ptr<Callback>(); active = false; } - - /** may be called by Initiator after all completers have been added but - * prior to calling end(). Allows initiator to determine if it _really_ - * needs to wait for pending Completers (e.g. count > 1). - */ - //uint32_t getPendingCompleters() { return completionsNeeded.get(); } }; }} // qpid::broker:: diff --git a/qpid/cpp/src/qpid/broker/Broker.cpp b/qpid/cpp/src/qpid/broker/Broker.cpp index 93e176fc0d..1b64fb2a52 100644 --- a/qpid/cpp/src/qpid/broker/Broker.cpp +++ b/qpid/cpp/src/qpid/broker/Broker.cpp @@ -122,7 +122,8 @@ Broker::Options::Options(const std::string& name) : qmf2Support(true), qmf1Support(true), queueFlowStopRatio(80), - queueFlowResumeRatio(70) + queueFlowResumeRatio(70), + queueThresholdEventRatio(80) { int c = sys::SystemInfo::concurrency(); workerThreads=c+1; @@ -153,11 +154,12 @@ Broker::Options::Options(const std::string& name) : ("tcp-nodelay", optValue(tcpNoDelay), "Set TCP_NODELAY on TCP connections") ("require-encryption", optValue(requireEncrypted), "Only accept connections that are encrypted") ("known-hosts-url", optValue(knownHosts, "URL or 'none'"), "URL to send as 'known-hosts' to clients ('none' implies empty list)") - ("sasl-config", optValue(saslConfigPath, "FILE"), "gets sasl config from nonstandard location") + ("sasl-config", optValue(saslConfigPath, "DIR"), "gets sasl config info from nonstandard location") ("max-session-rate", optValue(maxSessionRate, "MESSAGES/S"), "Sets the maximum message rate per session (0=unlimited)") ("async-queue-events", optValue(asyncQueueEvents, "yes|no"), "Set Queue Events async, used for services like replication") ("default-flow-stop-threshold", optValue(queueFlowStopRatio, "%MESSAGES"), "Queue capacity level at which flow control is activated.") - ("default-flow-resume-threshold", optValue(queueFlowResumeRatio, "%MESSAGES"), "Queue capacity level at which flow control is de-activated."); + ("default-flow-resume-threshold", optValue(queueFlowResumeRatio, "%MESSAGES"), "Queue capacity level at which flow control is de-activated.") + ("default-event-threshold-ratio", optValue(queueThresholdEventRatio, "%age of limit"), "The ratio of any specified queue limit at which an event will be raised"); } const std::string empty; @@ -595,7 +597,7 @@ void Broker::createObject(const std::string& type, const std::string& name, } } else if (type == TYPE_EXCHANGE || type == TYPE_TOPIC) { bool durable(false); - std::string exchangeType; + std::string exchangeType("topic"); std::string alternateExchange; Variant::Map extensions; for (Variant::Map::const_iterator i = properties.begin(); i != properties.end(); ++i) { @@ -788,18 +790,11 @@ std::pair<boost::shared_ptr<Queue>, bool> Broker::createQueue( Exchange::shared_ptr alternate; if (!alternateExchange.empty()) { alternate = exchanges.get(alternateExchange); - if (!alternate) framing::NotFoundException(QPID_MSG("Alternate exchange does not exist: " << alternateExchange)); + if (!alternate) throw framing::NotFoundException(QPID_MSG("Alternate exchange does not exist: " << alternateExchange)); } - std::pair<Queue::shared_ptr, bool> result = queues.declare(name, durable, autodelete, owner); + std::pair<Queue::shared_ptr, bool> result = queues.declare(name, durable, autodelete, owner, alternate, arguments); if (result.second) { - if (alternate) { - result.first->setAlternateExchange(alternate); - alternate->incAlternateUsers(); - } - - //apply settings & create persistent record if required - result.first->create(arguments); //add default binding: result.first->bind(exchanges.getDefault(), name); @@ -861,7 +856,7 @@ std::pair<Exchange::shared_ptr, bool> Broker::createExchange( Exchange::shared_ptr alternate; if (!alternateExchange.empty()) { alternate = exchanges.get(alternateExchange); - if (!alternate) framing::NotFoundException(QPID_MSG("Alternate exchange does not exist: " << alternateExchange)); + if (!alternate) throw framing::NotFoundException(QPID_MSG("Alternate exchange does not exist: " << alternateExchange)); } std::pair<Exchange::shared_ptr, bool> result; diff --git a/qpid/cpp/src/qpid/broker/Broker.h b/qpid/cpp/src/qpid/broker/Broker.h index 5fd230d9bd..d165c7ea70 100644 --- a/qpid/cpp/src/qpid/broker/Broker.h +++ b/qpid/cpp/src/qpid/broker/Broker.h @@ -121,6 +121,7 @@ public: bool qmf1Support; uint queueFlowStopRatio; // producer flow control: on uint queueFlowResumeRatio; // producer flow control: off + uint16_t queueThresholdEventRatio; private: std::string getHome(); @@ -154,7 +155,7 @@ public: void setLogLevel(const std::string& level); std::string getLogLevel(); void createObject(const std::string& type, const std::string& name, - const qpid::types::Variant::Map& properties, bool lenient, const ConnectionState* context); + const qpid::types::Variant::Map& properties, bool strict, const ConnectionState* context); void deleteObject(const std::string& type, const std::string& name, const qpid::types::Variant::Map& options, const ConnectionState* context); diff --git a/qpid/cpp/src/qpid/broker/Connection.cpp b/qpid/cpp/src/qpid/broker/Connection.cpp index 67713a6eb7..c07e63e68c 100644 --- a/qpid/cpp/src/qpid/broker/Connection.cpp +++ b/qpid/cpp/src/qpid/broker/Connection.cpp @@ -331,31 +331,30 @@ void Connection::closed(){ // Physically closed, suspend open sessions. try { while (!channels.empty()) ptr_map_ptr(channels.begin())->handleDetach(); - while (!exclusiveQueues.empty()) { - boost::shared_ptr<Queue> q(exclusiveQueues.front()); - q->releaseExclusiveOwnership(); - if (q->canAutoDelete()) { - Queue::tryAutoDelete(broker, q); - } - exclusiveQueues.erase(exclusiveQueues.begin()); - } } catch(std::exception& e) { QPID_LOG(error, QPID_MSG("While closing connection: " << e.what())); assert(0); } } +void Connection::doIoCallbacks() { + { + ScopedLock<Mutex> l(ioCallbackLock); + // Although IO callbacks execute in the connection thread context, they are + // not cluster safe because they are queued for execution in non-IO threads. + ClusterUnsafeScope cus; + while (!ioCallbacks.empty()) { + boost::function0<void> cb = ioCallbacks.front(); + ioCallbacks.pop(); + ScopedUnlock<Mutex> ul(ioCallbackLock); + cb(); // Lend the IO thread for management processing + } + } +} + bool Connection::doOutput() { try { - { - ScopedLock<Mutex> l(ioCallbackLock); - while (!ioCallbacks.empty()) { - boost::function0<void> cb = ioCallbacks.front(); - ioCallbacks.pop(); - ScopedUnlock<Mutex> ul(ioCallbackLock); - cb(); // Lend the IO thread for management processing - } - } + doIoCallbacks(); if (mgmtClosing) { closed(); close(connection::CLOSE_CODE_CONNECTION_FORCED, "Closed by Management Request"); @@ -475,8 +474,8 @@ void Connection::OutboundFrameTracker::abort() { next->abort(); } void Connection::OutboundFrameTracker::activateOutput() { next->activateOutput(); } void Connection::OutboundFrameTracker::giveReadCredit(int32_t credit) { next->giveReadCredit(credit); } void Connection::OutboundFrameTracker::send(framing::AMQFrame& f) -{ - next->send(f); +{ + next->send(f); con.sent(f); } void Connection::OutboundFrameTracker::wrap(sys::ConnectionOutputHandlerPtr& p) diff --git a/qpid/cpp/src/qpid/broker/Connection.h b/qpid/cpp/src/qpid/broker/Connection.h index b751848d73..8f1aa701ef 100644 --- a/qpid/cpp/src/qpid/broker/Connection.h +++ b/qpid/cpp/src/qpid/broker/Connection.h @@ -153,13 +153,16 @@ class Connection : public sys::ConnectionInputHandler, void addManagementObject(); const qpid::sys::SecuritySettings& getExternalSecuritySettings() const - { + { return securitySettings; } /** @return true if the initial connection negotiation is complete. */ bool isOpen(); + // Used by cluster during catch-up, see cluster::OutputInterceptor + void doIoCallbacks(); + private: typedef boost::ptr_map<framing::ChannelId, SessionHandler> ChannelMap; typedef std::vector<boost::shared_ptr<Queue> >::iterator queue_iterator; @@ -201,7 +204,7 @@ class Connection : public sys::ConnectionInputHandler, sys::ConnectionOutputHandler* next; }; OutboundFrameTracker outboundTracker; - + void sent(const framing::AMQFrame& f); public: diff --git a/qpid/cpp/src/qpid/broker/ConnectionState.h b/qpid/cpp/src/qpid/broker/ConnectionState.h index 774c37408d..9c31a931d8 100644 --- a/qpid/cpp/src/qpid/broker/ConnectionState.h +++ b/qpid/cpp/src/qpid/broker/ConnectionState.h @@ -79,7 +79,6 @@ class ConnectionState : public ConnectionToken, public management::Manageable Broker& getBroker() { return broker; } Broker& broker; - std::vector<boost::shared_ptr<Queue> > exclusiveQueues; //contained output tasks sys::AggregateOutput outputTasks; diff --git a/qpid/cpp/src/qpid/broker/DeliveryRecord.cpp b/qpid/cpp/src/qpid/broker/DeliveryRecord.cpp index 64760bea36..58dcc6d7c7 100644 --- a/qpid/cpp/src/qpid/broker/DeliveryRecord.cpp +++ b/qpid/cpp/src/qpid/broker/DeliveryRecord.cpp @@ -135,7 +135,7 @@ void DeliveryRecord::reject() Exchange::shared_ptr alternate = queue->getAlternateExchange(); if (alternate) { DeliverableMessage delivery(msg.payload); - alternate->route(delivery, msg.payload->getRoutingKey(), msg.payload->getApplicationHeaders()); + alternate->routeWithAlternate(delivery); QPID_LOG(info, "Routed rejected message from " << queue->getName() << " to " << alternate->getName()); } else { diff --git a/qpid/cpp/src/qpid/broker/Exchange.cpp b/qpid/cpp/src/qpid/broker/Exchange.cpp index b499171418..8ebe865867 100644 --- a/qpid/cpp/src/qpid/broker/Exchange.cpp +++ b/qpid/cpp/src/qpid/broker/Exchange.cpp @@ -356,3 +356,12 @@ bool Exchange::MatchQueue::operator()(Exchange::Binding::shared_ptr b) void Exchange::setProperties(const boost::intrusive_ptr<Message>& msg) { msg->getProperties<DeliveryProperties>()->setExchange(getName()); } + +bool Exchange::routeWithAlternate(Deliverable& msg) +{ + route(msg, msg.getMessage().getRoutingKey(), msg.getMessage().getApplicationHeaders()); + if (!msg.delivered && alternate) { + alternate->route(msg, msg.getMessage().getRoutingKey(), msg.getMessage().getApplicationHeaders()); + } + return msg.delivered; +} diff --git a/qpid/cpp/src/qpid/broker/Exchange.h b/qpid/cpp/src/qpid/broker/Exchange.h index 3c8b5ca2cd..9c4e6be192 100644 --- a/qpid/cpp/src/qpid/broker/Exchange.h +++ b/qpid/cpp/src/qpid/broker/Exchange.h @@ -222,6 +222,8 @@ public: */ void recoveryComplete(ExchangeRegistry& exchanges); + bool routeWithAlternate(Deliverable& message); + protected: qpid::sys::Mutex bridgeLock; std::vector<DynamicBridge*> bridgeVector; diff --git a/qpid/cpp/src/qpid/broker/Fairshare.cpp b/qpid/cpp/src/qpid/broker/Fairshare.cpp index e6bbf86691..17270ffd8d 100644 --- a/qpid/cpp/src/qpid/broker/Fairshare.cpp +++ b/qpid/cpp/src/qpid/broker/Fairshare.cpp @@ -24,6 +24,7 @@ #include "qpid/log/Statement.h" #include <boost/format.hpp> #include <boost/lexical_cast.hpp> +#include <boost/assign/list_of.hpp> namespace qpid { namespace broker { @@ -104,51 +105,80 @@ bool Fairshare::setState(Messages& m, uint priority, uint count) return fairshare && fairshare->setState(priority, count); } -int getIntegerSetting(const qpid::framing::FieldTable& settings, const std::string& key) +int getIntegerSetting(const qpid::framing::FieldTable& settings, const std::vector<std::string>& keys) { - qpid::framing::FieldTable::ValuePtr v = settings.get(key); + qpid::framing::FieldTable::ValuePtr v; + std::vector<std::string>::const_iterator i = keys.begin(); + while (!v && i != keys.end()) { + v = settings.get(*i++); + } + if (!v) { return 0; } else if (v->convertsTo<int>()) { return v->get<int>(); } else if (v->convertsTo<std::string>()){ std::string s = v->get<std::string>(); - try { - return boost::lexical_cast<int>(s); + try { + return boost::lexical_cast<int>(s); } catch(const boost::bad_lexical_cast&) { - QPID_LOG(warning, "Ignoring invalid integer value for " << key << ": " << s); + QPID_LOG(warning, "Ignoring invalid integer value for " << *i << ": " << s); return 0; } } else { - QPID_LOG(warning, "Ignoring invalid integer value for " << key << ": " << *v); + QPID_LOG(warning, "Ignoring invalid integer value for " << *i << ": " << *v); return 0; } } -int getSetting(const qpid::framing::FieldTable& settings, const std::string& key, int minvalue, int maxvalue) +int getIntegerSettingForKey(const qpid::framing::FieldTable& settings, const std::string& key) +{ + return getIntegerSetting(settings, boost::assign::list_of<std::string>(key)); +} + +int getSetting(const qpid::framing::FieldTable& settings, const std::vector<std::string>& keys, int minvalue, int maxvalue) +{ + return std::max(minvalue,std::min(getIntegerSetting(settings, keys), maxvalue)); +} + +std::auto_ptr<Fairshare> getFairshareForKey(const qpid::framing::FieldTable& settings, uint levels, const std::string& key) +{ + uint defaultLimit = getIntegerSettingForKey(settings, key); + std::auto_ptr<Fairshare> fairshare(new Fairshare(levels, defaultLimit)); + for (uint i = 0; i < levels; i++) { + std::string levelKey = (boost::format("%1%-%2%") % key % i).str(); + if(settings.isSet(levelKey)) { + fairshare->setLimit(i, getIntegerSettingForKey(settings, levelKey)); + } + } + if (!fairshare->isNull()) { + return fairshare; + } else { + return std::auto_ptr<Fairshare>(); + } +} + +std::auto_ptr<Fairshare> getFairshare(const qpid::framing::FieldTable& settings, + uint levels, + const std::vector<std::string>& keys) { - return std::max(minvalue,std::min(getIntegerSetting(settings, key), maxvalue)); + std::auto_ptr<Fairshare> fairshare; + for (std::vector<std::string>::const_iterator i = keys.begin(); i != keys.end() && !fairshare.get(); ++i) { + fairshare = getFairshareForKey(settings, levels, *i); + } + return fairshare; } std::auto_ptr<Messages> Fairshare::create(const qpid::framing::FieldTable& settings) { + using boost::assign::list_of; std::auto_ptr<Messages> result; - size_t levels = getSetting(settings, "x-qpid-priorities", 1, 100); + size_t levels = getSetting(settings, list_of<std::string>("qpid.priorities")("x-qpid-priorities"), 1, 100); if (levels) { - uint defaultLimit = getIntegerSetting(settings, "x-qpid-fairshare"); - std::auto_ptr<Fairshare> fairshare(new Fairshare(levels, defaultLimit)); - for (uint i = 0; i < levels; i++) { - std::string key = (boost::format("x-qpid-fairshare-%1%") % i).str(); - if(settings.isSet(key)) { - fairshare->setLimit(i, getIntegerSetting(settings, key)); - } - } - - if (fairshare->isNull()) { - result = std::auto_ptr<Messages>(new PriorityQueue(levels)); - } else { - result = fairshare; - } + std::auto_ptr<Fairshare> fairshare = + getFairshare(settings, levels, list_of<std::string>("qpid.fairshare")("x-qpid-fairshare")); + if (fairshare.get()) result = fairshare; + else result = std::auto_ptr<Messages>(new PriorityQueue(levels)); } return result; } diff --git a/qpid/cpp/src/qpid/broker/Fairshare.h b/qpid/cpp/src/qpid/broker/Fairshare.h index 6c4b87f857..1b25721e0c 100644 --- a/qpid/cpp/src/qpid/broker/Fairshare.h +++ b/qpid/cpp/src/qpid/broker/Fairshare.h @@ -41,18 +41,18 @@ class Fairshare : public PriorityQueue bool getState(uint& priority, uint& count) const; bool setState(uint priority, uint count); void setLimit(size_t level, uint limit); + bool isNull(); static std::auto_ptr<Messages> create(const qpid::framing::FieldTable& settings); static bool getState(const Messages&, uint& priority, uint& count); static bool setState(Messages&, uint priority, uint count); private: std::vector<uint> limits; - + uint priority; uint count; - + uint currentLevel(); uint nextLevel(); - bool isNull(); bool limitReached(); bool findFrontLevel(uint& p, PriorityLevels&); }; diff --git a/qpid/cpp/src/qpid/broker/Message.cpp b/qpid/cpp/src/qpid/broker/Message.cpp index 122c5b9c1a..812d856416 100644 --- a/qpid/cpp/src/qpid/broker/Message.cpp +++ b/qpid/cpp/src/qpid/broker/Message.cpp @@ -51,7 +51,7 @@ Message::Message(const framing::SequenceNumber& id) : frames(id), persistenceId(0), redelivered(false), loaded(false), staged(false), forcePersistentPolicy(false), publisher(0), adapter(0), expiration(FAR_FUTURE), dequeueCallback(0), - inCallback(false), requiredCredit(0) + inCallback(false), requiredCredit(0), isManagementMessage(false) {} Message::Message(const Message& original) : @@ -443,4 +443,7 @@ framing::FieldTable& Message::getOrInsertHeaders() return getProperties<MessageProperties>()->getApplicationHeaders(); } +bool Message::getIsManagementMessage() const { return isManagementMessage; } +void Message::setIsManagementMessage(bool b) { isManagementMessage = b; } + }} // namespace qpid::broker diff --git a/qpid/cpp/src/qpid/broker/Message.h b/qpid/cpp/src/qpid/broker/Message.h index 2d0de27823..0e9ae7ba12 100644 --- a/qpid/cpp/src/qpid/broker/Message.h +++ b/qpid/cpp/src/qpid/broker/Message.h @@ -159,7 +159,8 @@ public: void resetDequeueCompleteCallback(); uint8_t getPriority() const; - + bool getIsManagementMessage() const; + void setIsManagementMessage(bool b); private: MessageAdapter& getAdapter() const; void allDequeuesComplete(); @@ -186,6 +187,7 @@ public: bool inCallback; uint32_t requiredCredit; + bool isManagementMessage; }; }} diff --git a/qpid/cpp/src/qpid/broker/PersistableMessage.h b/qpid/cpp/src/qpid/broker/PersistableMessage.h index a84aa45d76..e50b52c09a 100644 --- a/qpid/cpp/src/qpid/broker/PersistableMessage.h +++ b/qpid/cpp/src/qpid/broker/PersistableMessage.h @@ -56,7 +56,7 @@ class PersistableMessage : public Persistable * operations have completed, the transfer of this message from the client * may be considered complete. */ - boost::shared_ptr<AsyncCompletion> ingressCompletion; + AsyncCompletion ingressCompletion; /** * Tracks the number of outstanding asynchronous dequeue @@ -115,12 +115,11 @@ class PersistableMessage : public Persistable virtual QPID_BROKER_EXTERN bool isPersistent() const = 0; /** track the progress of a message received by the broker - see ingressCompletion above */ - QPID_BROKER_EXTERN bool isIngressComplete() { return !ingressCompletion || ingressCompletion->isDone(); } - QPID_BROKER_EXTERN boost::shared_ptr<AsyncCompletion>& getIngressCompletion() { return ingressCompletion; } - QPID_BROKER_EXTERN void setIngressCompletion(boost::shared_ptr<AsyncCompletion>& ic) { ingressCompletion = ic; } + QPID_BROKER_EXTERN bool isIngressComplete() { return ingressCompletion.isDone(); } + QPID_BROKER_EXTERN AsyncCompletion& getIngressCompletion() { return ingressCompletion; } - QPID_BROKER_EXTERN void enqueueStart() { if (ingressCompletion) ingressCompletion->startCompleter(); } - QPID_BROKER_EXTERN void enqueueComplete() { if (ingressCompletion) ingressCompletion->finishCompleter(); } + QPID_BROKER_EXTERN void enqueueStart() { ingressCompletion.startCompleter(); } + QPID_BROKER_EXTERN void enqueueComplete() { ingressCompletion.finishCompleter(); } QPID_BROKER_EXTERN void enqueueAsync(PersistableQueue::shared_ptr queue, MessageStore* _store); diff --git a/qpid/cpp/src/qpid/broker/Queue.cpp b/qpid/cpp/src/qpid/broker/Queue.cpp index f2322767f6..bd061ac214 100644 --- a/qpid/cpp/src/qpid/broker/Queue.cpp +++ b/qpid/cpp/src/qpid/broker/Queue.cpp @@ -517,8 +517,7 @@ uint32_t Queue::purge(const uint32_t purge_request, boost::shared_ptr<Exchange> while (!rerouteQueue.empty()) { DeliverableMessage msg(rerouteQueue.front()); rerouteQueue.pop_front(); - dest->route(msg, msg.getMessage().getRoutingKey(), - msg.getMessage().getApplicationHeaders()); + dest->routeWithAlternate(msg); } return count; @@ -758,7 +757,7 @@ void Queue::create(const FieldTable& _settings) if (store) { store->create(*this, _settings); } - configure(_settings); + configureImpl(_settings); if (broker) broker->getCluster().create(*this); } @@ -784,9 +783,14 @@ int getIntegerSetting(const qpid::framing::FieldTable& settings, const std::stri } } -void Queue::configure(const FieldTable& _settings, bool recovering) +void Queue::configure(const FieldTable& _settings) { + settings = _settings; + configureImpl(settings); +} +void Queue::configureImpl(const FieldTable& _settings) +{ eventMode = _settings.getAsInt(qpidQueueEventGeneration); if (eventMode && broker) { broker->getQueueEvents().observe(*this, eventMode == ENQUEUE_ONLY); @@ -805,7 +809,7 @@ void Queue::configure(const FieldTable& _settings, bool recovering) setPolicy(QueuePolicy::createQueuePolicy(getName(), _settings)); } if (broker && broker->getManagementAgent()) { - ThresholdAlerts::observe(*this, *(broker->getManagementAgent()), _settings); + ThresholdAlerts::observe(*this, *(broker->getManagementAgent()), _settings, broker->getOptions().queueThresholdEventRatio); } //set this regardless of owner to allow use of no-local with exclusive consumers also @@ -852,9 +856,6 @@ void Queue::configure(const FieldTable& _settings, bool recovering) mgmtObject->set_arguments(ManagementAgent::toMap(_settings)); } - if ( isDurable() && ! getPersistenceId() && ! recovering ) - store->create(*this, _settings); - QueueFlowLimit::observe(*this, _settings); } @@ -865,8 +866,7 @@ void Queue::destroyed() Mutex::ScopedLock locker(messageLock); while(!messages->empty()){ DeliverableMessage msg(messages->front().payload); - alternateExchange->route(msg, msg.getMessage().getRoutingKey(), - msg.getMessage().getApplicationHeaders()); + alternateExchange->routeWithAlternate(msg); popAndDequeue(); } alternateExchange->decAlternateUsers(); @@ -949,13 +949,14 @@ uint32_t Queue::encodedSize() const + (policy.get() ? (*policy).encodedSize() : 0); } -Queue::shared_ptr Queue::decode ( QueueRegistry& queues, Buffer& buffer, bool recovering ) +Queue::shared_ptr Queue::restore( QueueRegistry& queues, Buffer& buffer ) { string name; buffer.getShortString(name); - std::pair<Queue::shared_ptr, bool> result = queues.declare(name, true); - buffer.get(result.first->settings); - result.first->configure(result.first->settings, recovering ); + FieldTable settings; + buffer.get(settings); + boost::shared_ptr<Exchange> alternate; + std::pair<Queue::shared_ptr, bool> result = queues.declare(name, true, false, 0, alternate, settings, true); if (result.first->policy.get() && buffer.available() >= result.first->policy->encodedSize()) { buffer.get ( *(result.first->policy) ); } diff --git a/qpid/cpp/src/qpid/broker/Queue.h b/qpid/cpp/src/qpid/broker/Queue.h index 331c9eaa4e..73d52ec9ca 100644 --- a/qpid/cpp/src/qpid/broker/Queue.h +++ b/qpid/cpp/src/qpid/broker/Queue.h @@ -149,6 +149,7 @@ class Queue : public boost::enable_shared_from_this<Queue>, QueuedMessage getFront(); void forcePersistent(QueuedMessage& msg); int getEventMode(); + void configureImpl(const qpid::framing::FieldTable& settings); inline void mgntEnqStats(const boost::intrusive_ptr<Message>& msg) { @@ -192,11 +193,17 @@ class Queue : public boost::enable_shared_from_this<Queue>, QPID_BROKER_EXTERN bool dispatch(Consumer::shared_ptr); - void create(const qpid::framing::FieldTable& settings); + /** + * Used to configure a new queue and create a persistent record + * for it in store if required. + */ + QPID_BROKER_EXTERN void create(const qpid::framing::FieldTable& settings); - // "recovering" means we are doing a MessageStore recovery. - QPID_BROKER_EXTERN void configure(const qpid::framing::FieldTable& settings, - bool recovering = false); + /** + * Used to reconfigure a recovered queue (does not create + * persistent record in store). + */ + QPID_BROKER_EXTERN void configure(const qpid::framing::FieldTable& settings); void destroyed(); QPID_BROKER_EXTERN void bound(const std::string& exchange, const std::string& key, @@ -314,8 +321,13 @@ class Queue : public boost::enable_shared_from_this<Queue>, void encode(framing::Buffer& buffer) const; uint32_t encodedSize() const; - // "recovering" means we are doing a MessageStore recovery. - static Queue::shared_ptr decode(QueueRegistry& queues, framing::Buffer& buffer, bool recovering = false ); + /** + * Restores a queue from encoded data (used in recovery) + * + * Note: restored queue will be neither auto-deleted or have an + * exclusive owner + */ + static Queue::shared_ptr restore(QueueRegistry& queues, framing::Buffer& buffer); static void tryAutoDelete(Broker& broker, Queue::shared_ptr); virtual void setExternalQueueStore(ExternalQueueStore* inst); diff --git a/qpid/cpp/src/qpid/broker/QueueFlowLimit.cpp b/qpid/cpp/src/qpid/broker/QueueFlowLimit.cpp index a99c9de7df..3494288f7b 100644 --- a/qpid/cpp/src/qpid/broker/QueueFlowLimit.cpp +++ b/qpid/cpp/src/qpid/broker/QueueFlowLimit.cpp @@ -138,13 +138,10 @@ void QueueFlowLimit::enqueued(const QueuedMessage& msg) flowStopped = true; QPID_LOG(info, "Queue \"" << queueName << "\": has reached " << flowStopSize << " enqueued bytes. Producer flow control activated." ); } - if (flowStopped && queueMgmtObj) + if (flowStopped && queueMgmtObj) { queueMgmtObj->set_flowStopped(true); - } - - /** @todo KAG: - REMOVE ONCE STABLE */ - if (index.find(msg.payload) != index.end()) { - QPID_LOG(error, "Queue \"" << queueName << "\": has enqueued a msg twice: " << msg.position); + queueMgmtObj->inc_flowStoppedCount(); + } } if (flowStopped || !index.empty()) { @@ -154,7 +151,7 @@ void QueueFlowLimit::enqueued(const QueuedMessage& msg) return; } QPID_LOG(trace, "Queue \"" << queueName << "\": setting flow control for msg pos=" << msg.position); - msg.payload->getIngressCompletion()->startCompleter(); // don't complete until flow resumes + msg.payload->getIngressCompletion().startCompleter(); // don't complete until flow resumes index.insert(msg.payload); } } @@ -194,14 +191,14 @@ void QueueFlowLimit::dequeued(const QueuedMessage& msg) // flow enabled - release all pending msgs while (!index.empty()) { std::set< boost::intrusive_ptr<Message> >::iterator itr = index.begin(); - (*itr)->getIngressCompletion()->finishCompleter(); + (*itr)->getIngressCompletion().finishCompleter(); index.erase(itr); } } else { // even if flow controlled, we must release this msg as it is being dequeued std::set< boost::intrusive_ptr<Message> >::iterator itr = index.find(msg.payload); if (itr != index.end()) { // this msg is flow controlled, release it: - (*itr)->getIngressCompletion()->finishCompleter(); + (*itr)->getIngressCompletion().finishCompleter(); index.erase(itr); } } diff --git a/qpid/cpp/src/qpid/broker/QueuePolicy.cpp b/qpid/cpp/src/qpid/broker/QueuePolicy.cpp index 4168221ad0..b39ea3614b 100644 --- a/qpid/cpp/src/qpid/broker/QueuePolicy.cpp +++ b/qpid/cpp/src/qpid/broker/QueuePolicy.cpp @@ -136,11 +136,10 @@ uint32_t QueuePolicy::getCapacity(const FieldTable& settings, const std::string& string s(v->get<string>()); QPID_LOG(debug, "Got string value for " << key << ": " << s); std::istringstream convert(s); - if (convert >> result && result >= 0) return result; + if (convert >> result && result >= 0 && convert.eof()) return result; } - QPID_LOG(warning, "Cannot convert " << key << " to unsigned integer, using default (" << defaultValue << ")"); - return defaultValue; + throw IllegalArgumentException(QPID_MSG("Cannot convert " << key << " to unsigned integer: " << *v)); } std::string QueuePolicy::getType(const FieldTable& settings) diff --git a/qpid/cpp/src/qpid/broker/QueueRegistry.cpp b/qpid/cpp/src/qpid/broker/QueueRegistry.cpp index ea2531dae7..135a3543d9 100644 --- a/qpid/cpp/src/qpid/broker/QueueRegistry.cpp +++ b/qpid/cpp/src/qpid/broker/QueueRegistry.cpp @@ -21,6 +21,7 @@ #include "qpid/broker/Queue.h" #include "qpid/broker/QueueRegistry.h" #include "qpid/broker/QueueEvents.h" +#include "qpid/broker/Exchange.h" #include "qpid/log/Statement.h" #include <sstream> #include <assert.h> @@ -36,7 +37,13 @@ QueueRegistry::~QueueRegistry(){} std::pair<Queue::shared_ptr, bool> QueueRegistry::declare(const string& declareName, bool durable, - bool autoDelete, const OwnershipToken* owner) + bool autoDelete, const OwnershipToken* owner, + boost::shared_ptr<Exchange> alternate, + const qpid::framing::FieldTable& arguments, + bool recovering/*true if this declare is a + result of recovering queue + definition from persistente + record*/) { RWlock::ScopedWlock locker(lock); string name = declareName.empty() ? generateName() : declareName; @@ -45,6 +52,17 @@ QueueRegistry::declare(const string& declareName, bool durable, if (i == queues.end()) { Queue::shared_ptr queue(new Queue(name, autoDelete, durable ? store : 0, owner, parent, broker)); + if (alternate) { + queue->setAlternateExchange(alternate);//need to do this *before* create + alternate->incAlternateUsers(); + } + if (!recovering) { + //apply settings & create persistent record if required + queue->create(arguments); + } else { + //i.e. recovering a queue for which we already have a persistent record + queue->configure(arguments); + } queues[name] = queue; if (lastNode) queue->setLastNodeFailure(); diff --git a/qpid/cpp/src/qpid/broker/QueueRegistry.h b/qpid/cpp/src/qpid/broker/QueueRegistry.h index 57859fe639..8a32a64f05 100644 --- a/qpid/cpp/src/qpid/broker/QueueRegistry.h +++ b/qpid/cpp/src/qpid/broker/QueueRegistry.h @@ -24,6 +24,7 @@ #include "qpid/broker/BrokerImportExport.h" #include "qpid/sys/Mutex.h" #include "qpid/management/Manageable.h" +#include "qpid/framing/FieldTable.h" #include <boost/bind.hpp> #include <boost/shared_ptr.hpp> #include <algorithm> @@ -34,6 +35,7 @@ namespace broker { class Queue; class QueueEvents; +class Exchange; class OwnershipToken; class Broker; class MessageStore; @@ -60,7 +62,10 @@ class QueueRegistry { const std::string& name, bool durable = false, bool autodelete = false, - const OwnershipToken* owner = 0); + const OwnershipToken* owner = 0, + boost::shared_ptr<Exchange> alternateExchange = boost::shared_ptr<Exchange>(), + const qpid::framing::FieldTable& args = framing::FieldTable(), + bool recovering = false); /** * Destroy the named queue. diff --git a/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp b/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp index 2a1f58eb91..9aae39ccc3 100644 --- a/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp +++ b/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp @@ -114,7 +114,7 @@ RecoverableExchange::shared_ptr RecoveryManagerImpl::recoverExchange(framing::Bu RecoverableQueue::shared_ptr RecoveryManagerImpl::recoverQueue(framing::Buffer& buffer) { - Queue::shared_ptr queue = Queue::decode(queues, buffer, true); + Queue::shared_ptr queue = Queue::restore(queues, buffer); try { Exchange::shared_ptr exchange = exchanges.getDefault(); if (exchange) { diff --git a/qpid/cpp/src/qpid/broker/SemanticState.cpp b/qpid/cpp/src/qpid/broker/SemanticState.cpp index cfc379f47c..ba1f989f7c 100644 --- a/qpid/cpp/src/qpid/broker/SemanticState.cpp +++ b/qpid/cpp/src/qpid/broker/SemanticState.cpp @@ -88,7 +88,7 @@ void SemanticState::closed() { //prevent requeued messages being redelivered to consumers for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) { disable(i->second); - } + } if (dtxBuffer.get()) { dtxBuffer->fail(); } @@ -107,7 +107,7 @@ bool SemanticState::exists(const string& consumerTag){ return consumers.find(consumerTag) != consumers.end(); } -void SemanticState::consume(const string& tag, +void SemanticState::consume(const string& tag, Queue::shared_ptr queue, bool ackRequired, bool acquire, bool exclusive, const string& resumeId, uint64_t resumeTtl, const FieldTable& arguments) { @@ -116,7 +116,8 @@ void SemanticState::consume(const string& tag, consumers[tag] = c; } -void SemanticState::cancel(const string& tag){ +bool SemanticState::cancel(const string& tag) +{ ConsumerImplMap::iterator i = consumers.find(tag); if (i != consumers.end()) { cancel(i->second); @@ -124,7 +125,9 @@ void SemanticState::cancel(const string& tag){ //should cancel all unacked messages for this consumer so that //they are not redelivered on recovery for_each(unacked.begin(), unacked.end(), boost::bind(&DeliveryRecord::cancel, _1, tag)); - + return true; + } else { + return false; } } @@ -194,7 +197,7 @@ void SemanticState::endDtx(const std::string& xid, bool fail) dtxBuffer->fail(); } else { dtxBuffer->markEnded(); - } + } dtxBuffer.reset(); } @@ -254,9 +257,9 @@ void SemanticState::record(const DeliveryRecord& delivery) const std::string QPID_SYNC_FREQUENCY("qpid.sync_frequency"); -SemanticState::ConsumerImpl::ConsumerImpl(SemanticState* _parent, - const string& _name, - Queue::shared_ptr _queue, +SemanticState::ConsumerImpl::ConsumerImpl(SemanticState* _parent, + const string& _name, + Queue::shared_ptr _queue, bool ack, bool _acquire, bool _exclusive, @@ -265,20 +268,20 @@ SemanticState::ConsumerImpl::ConsumerImpl(SemanticState* _parent, const framing::FieldTable& _arguments -) : +) : Consumer(_acquire), - parent(_parent), - name(_name), - queue(_queue), - ackExpected(ack), + parent(_parent), + name(_name), + queue(_queue), + ackExpected(ack), acquire(_acquire), - blocked(true), + blocked(true), windowing(true), exclusive(_exclusive), resumeId(_resumeId), resumeTtl(_resumeTtl), arguments(_arguments), - msgCredit(0), + msgCredit(0), byteCredit(0), notifyEnabled(true), syncFrequency(_arguments.getAsInt(QPID_SYNC_FREQUENCY)), @@ -289,7 +292,7 @@ SemanticState::ConsumerImpl::ConsumerImpl(SemanticState* _parent, { ManagementAgent* agent = parent->session.getBroker().getManagementAgent(); qpid::management::Manageable* ms = dynamic_cast<qpid::management::Manageable*> (&(parent->session)); - + if (agent != 0) { mgmtObject = new _qmf::Subscription(agent, this, ms , queue->GetManagementObject()->getObjectId() ,name, @@ -331,7 +334,7 @@ bool SemanticState::ConsumerImpl::deliver(QueuedMessage& msg) if (!ackExpected && acquire) record.setEnded();//allows message to be released now its been delivered if (windowing || ackExpected || !acquire) { parent->record(record); - } + } if (acquire && !ackExpected) { queue->dequeue(0, msg); } @@ -351,7 +354,7 @@ bool SemanticState::ConsumerImpl::accept(intrusive_ptr<Message> msg) // checkCredit fails because the message is to big, we should // remain on queue's listener list for possible smaller messages // in future. - // + // blocked = !(filter(msg) && checkCredit(msg)); return !blocked; } @@ -372,7 +375,7 @@ void SemanticState::ConsumerImpl::allocateCredit(intrusive_ptr<Message>& msg) { assertClusterSafe(); uint32_t originalMsgCredit = msgCredit; - uint32_t originalByteCredit = byteCredit; + uint32_t originalByteCredit = byteCredit; if (msgCredit != 0xFFFFFFFF) { msgCredit--; } @@ -382,7 +385,7 @@ void SemanticState::ConsumerImpl::allocateCredit(intrusive_ptr<Message>& msg) QPID_LOG(debug, "Credit allocated for " << ConsumerName(*this) << ", was " << " bytes: " << originalByteCredit << " msgs: " << originalMsgCredit << " now bytes: " << byteCredit << " msgs: " << msgCredit); - + } bool SemanticState::ConsumerImpl::checkCredit(intrusive_ptr<Message>& msg) @@ -396,7 +399,7 @@ bool SemanticState::ConsumerImpl::checkCredit(intrusive_ptr<Message>& msg) return enoughCredit; } -SemanticState::ConsumerImpl::~ConsumerImpl() +SemanticState::ConsumerImpl::~ConsumerImpl() { if (mgmtObject != 0) mgmtObject->resourceDestroy (); @@ -414,7 +417,7 @@ void SemanticState::unsubscribe(ConsumerImpl::shared_ptr c) Queue::shared_ptr queue = c->getQueue(); if(queue) { queue->cancel(c); - if (queue->canAutoDelete() && !queue->hasExclusiveOwner()) { + if (queue->canAutoDelete() && !queue->hasExclusiveOwner()) { Queue::tryAutoDelete(session.getBroker(), queue); } } @@ -457,7 +460,7 @@ const std::string nullstring; void SemanticState::route(intrusive_ptr<Message> msg, Deliverable& strategy) { msg->setTimestamp(getSession().getBroker().getExpiryPolicy()); - + std::string exchangeName = msg->getExchangeName(); if (!cacheExchange || cacheExchange->getName() != exchangeName) cacheExchange = session.getBroker().getExchanges().get(exchangeName); @@ -466,7 +469,7 @@ void SemanticState::route(intrusive_ptr<Message> msg, Deliverable& strategy) { /* verify the userid if specified: */ std::string id = msg->hasProperties<MessageProperties>() ? msg->getProperties<MessageProperties>()->getUserId() : nullstring; - + if (authMsg && !id.empty() && !(id == userID || (isDefaultRealm && id == userName))) { QPID_LOG(debug, "authorised user id : " << userID << " but user id in message declared as " << id); @@ -484,7 +487,7 @@ void SemanticState::route(intrusive_ptr<Message> msg, Deliverable& strategy) { if (!strategy.delivered) { //TODO:if discard-unroutable, just drop it - //TODO:else if accept-mode is explicit, reject it + //TODO:else if accept-mode is explicit, reject it //else route it to alternate exchange if (cacheExchange->getAlternate()) { cacheExchange->getAlternate()->route(strategy, msg->getRoutingKey(), msg->getApplicationHeaders()); @@ -513,7 +516,7 @@ void SemanticState::ConsumerImpl::requestDispatch() } bool SemanticState::complete(DeliveryRecord& delivery) -{ +{ ConsumerImplMap::iterator i = consumers.find(delivery.getTag()); if (i != consumers.end()) { i->second->complete(delivery); @@ -541,7 +544,7 @@ void SemanticState::recover(bool requeue) unacked.clear(); for_each(copy.rbegin(), copy.rend(), mem_fun_ref(&DeliveryRecord::requeue)); }else{ - for_each(unacked.begin(), unacked.end(), boost::bind(&DeliveryRecord::redeliver, _1, this)); + for_each(unacked.begin(), unacked.end(), boost::bind(&DeliveryRecord::redeliver, _1, this)); //unconfirmed messages re redelivered and therefore have their //id adjusted, confirmed messages are not and so the ordering //w.r.t id is lost @@ -673,7 +676,7 @@ Queue::shared_ptr SemanticState::getQueue(const string& name) const { } AckRange SemanticState::findRange(DeliveryId first, DeliveryId last) -{ +{ return DeliveryRecord::findRange(unacked, first, last); } @@ -764,13 +767,13 @@ void SemanticState::accepted(const SequenceSet& commands) { //in transactional mode, don't dequeue or remove, just //maintain set of acknowledged messages: accumulatedAck.add(commands); - + if (dtxBuffer.get()) { //if enlisted in a dtx, copy the relevant slice from //unacked and record it against that transaction TxOp::shared_ptr txAck(new DtxAck(accumulatedAck, unacked)); accumulatedAck.clear(); - dtxBuffer->enlist(txAck); + dtxBuffer->enlist(txAck); //mark the relevant messages as 'ended' in unacked //if the messages are already completed, they can be @@ -792,7 +795,6 @@ void SemanticState::accepted(const SequenceSet& commands) { } void SemanticState::completed(const SequenceSet& commands) { - assertClusterSafe(); DeliveryRecords::iterator removed = remove_if(unacked.begin(), unacked.end(), isInSequenceSetAnd(commands, @@ -803,7 +805,6 @@ void SemanticState::completed(const SequenceSet& commands) { void SemanticState::attached() { - assertClusterSafe(); for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) { i->second->enableNotify(); session.getConnection().outputTasks.addOutputTask(i->second.get()); @@ -813,7 +814,6 @@ void SemanticState::attached() void SemanticState::detached() { - assertClusterSafe(); for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) { i->second->disableNotify(); session.getConnection().outputTasks.removeOutputTask(i->second.get()); diff --git a/qpid/cpp/src/qpid/broker/SemanticState.h b/qpid/cpp/src/qpid/broker/SemanticState.h index b2e648410a..8c69d6b89b 100644 --- a/qpid/cpp/src/qpid/broker/SemanticState.h +++ b/qpid/cpp/src/qpid/broker/SemanticState.h @@ -205,7 +205,7 @@ class SemanticState : private boost::noncopyable { const std::string& resumeId=std::string(), uint64_t resumeTtl=0, const framing::FieldTable& = framing::FieldTable()); - void cancel(const std::string& tag); + bool cancel(const std::string& tag); void setWindowMode(const std::string& destination); void setCreditMode(const std::string& destination); diff --git a/qpid/cpp/src/qpid/broker/SessionAdapter.cpp b/qpid/cpp/src/qpid/broker/SessionAdapter.cpp index 9e4516679e..63c4b660b2 100644 --- a/qpid/cpp/src/qpid/broker/SessionAdapter.cpp +++ b/qpid/cpp/src/qpid/broker/SessionAdapter.cpp @@ -114,7 +114,7 @@ void SessionAdapter::ExchangeHandlerImpl::declare(const string& exchange, const "existing")); } }catch(UnknownExchangeTypeException& /*e*/){ - throw CommandInvalidException(QPID_MSG("Exchange type not implemented: " << type)); + throw NotFoundException(QPID_MSG("Exchange type not implemented: " << type)); } } } @@ -353,12 +353,12 @@ void SessionAdapter::QueueHandlerImpl::checkDelete(Queue::shared_ptr queue, bool } else if(ifUnused && queue->getConsumerCount() > 0) { throw PreconditionFailedException(QPID_MSG("Cannot delete queue " << queue->getName() << "; queue in use")); - } else if (queue->isExclusiveOwner(&getConnection())) { + } else if (queue->isExclusiveOwner(&session)) { //remove the queue from the list of exclusive queues if necessary - QueueVector::iterator i = std::find(getConnection().exclusiveQueues.begin(), - getConnection().exclusiveQueues.end(), + QueueVector::iterator i = std::find(exclusiveQueues.begin(), + exclusiveQueues.end(), queue); - if (i < getConnection().exclusiveQueues.end()) getConnection().exclusiveQueues.erase(i); + if (i < exclusiveQueues.end()) exclusiveQueues.erase(i); } } @@ -431,7 +431,9 @@ SessionAdapter::MessageHandlerImpl::subscribe(const string& queueName, void SessionAdapter::MessageHandlerImpl::cancel(const string& destination ) { - state.cancel(destination); + if (!state.cancel(destination)) { + throw NotFoundException(QPID_MSG("No such subscription: " << destination)); + } ManagementAgent* agent = getBroker().getManagementAgent(); if (agent) diff --git a/qpid/cpp/src/qpid/broker/SessionState.cpp b/qpid/cpp/src/qpid/broker/SessionState.cpp index 11f3e84b70..18dbf63487 100644 --- a/qpid/cpp/src/qpid/broker/SessionState.cpp +++ b/qpid/cpp/src/qpid/broker/SessionState.cpp @@ -62,7 +62,7 @@ SessionState::SessionState( msgBuilder(&broker.getStore()), mgmtObject(0), rateFlowcontrol(0), - scheduledCompleterContext(new ScheduledCompleterContext(this)) + asyncCommandCompleter(new AsyncCommandCompleter(this)) { uint32_t maxRate = broker.getOptions().maxSessionRate; if (maxRate) { @@ -102,25 +102,7 @@ SessionState::~SessionState() { if (flowControlTimer) flowControlTimer->cancel(); - // clean up any outstanding incomplete commands - { - qpid::sys::ScopedLock<Mutex> l(incompleteCmdsLock); - std::map<SequenceNumber, boost::shared_ptr<IncompleteCommandContext> > copy(incompleteCmds); - incompleteCmds.clear(); - while (!copy.empty()) { - boost::shared_ptr<IncompleteCommandContext> ref(copy.begin()->second); - copy.erase(copy.begin()); - { - // note: need to drop lock, as callback may attempt to take it. - qpid::sys::ScopedUnlock<Mutex> ul(incompleteCmdsLock); - ref->cancel(); - } - } - } - - // At this point, we are guaranteed no further completion callbacks will be - // made. Cancel any outstanding scheduledCompleter calls... - scheduledCompleterContext->cancel(); + asyncCommandCompleter->cancel(); } AMQP_ClientProxy& SessionState::getProxy() { @@ -276,13 +258,11 @@ void SessionState::handleContent(AMQFrame& frame, const SequenceNumber& id) msg->getFrames().append(header); } msg->setPublisher(&getConnection()); - - boost::shared_ptr<AsyncCompletion> ac(boost::dynamic_pointer_cast<AsyncCompletion>(createIngressMsgXferContext(msg))); - msg->setIngressCompletion( ac ); - ac->begin(); + msg->getIngressCompletion().begin(); semanticState.handle(msg); msgBuilder.end(); - ac->end(); // allows msg to complete xfer + IncompleteIngressMsgXfer xfer(this, msg); + msg->getIngressCompletion().end(xfer); // allows msg to complete xfer } // Handle producer session flow control @@ -451,110 +431,94 @@ void SessionState::addPendingExecutionSync() } -/** factory for creating IncompleteIngressMsgXfer objects which - * can be references from Messages as ingress AsyncCompletion objects. +/** factory for creating a reference-counted IncompleteIngressMsgXfer object + * which will be attached to a message that will be completed asynchronously. */ -boost::shared_ptr<SessionState::IncompleteIngressMsgXfer> -SessionState::createIngressMsgXferContext(boost::intrusive_ptr<Message> msg) +boost::intrusive_ptr<AsyncCompletion::Callback> +SessionState::IncompleteIngressMsgXfer::clone() { - SequenceNumber id = msg->getCommandId(); - boost::shared_ptr<SessionState::IncompleteIngressMsgXfer> cmd(new SessionState::IncompleteIngressMsgXfer(this, id, msg)); - qpid::sys::ScopedLock<Mutex> l(incompleteCmdsLock); - incompleteCmds[id] = cmd; - return cmd; + boost::intrusive_ptr<SessionState::IncompleteIngressMsgXfer> cb(new SessionState::IncompleteIngressMsgXfer(session, msg)); + return cb; } -/** Invoked by the asynchronous completer associated with - * a received msg that is pending Completion. May be invoked - * by the SessionState directly (sync == true), or some external - * entity (!sync). +/** Invoked by the asynchronous completer associated with a received + * msg that is pending Completion. May be invoked by the IO thread + * (sync == true), or some external thread (!sync). */ void SessionState::IncompleteIngressMsgXfer::completed(bool sync) { if (!sync) { /** note well: this path may execute in any thread. It is safe to access - * the session, as the SessionState destructor will cancel all outstanding - * callbacks before getting destroyed (so we'll never get here). + * the scheduledCompleterContext, since *this has a shared pointer to it. + * but not session or msg! */ + session = 0; msg = 0; QPID_LOG(debug, ": async completion callback scheduled for msg seq=" << id); - if (session->scheduledCompleterContext->scheduleCompletion(id)) - session->getConnection().requestIOProcessing(boost::bind(&scheduledCompleter, - session->scheduledCompleterContext)); - } else { // command is being completed in IO thread. - // this path runs only on the IO thread. - qpid::sys::ScopedLock<Mutex> l(session->incompleteCmdsLock); - std::map<SequenceNumber, boost::shared_ptr<IncompleteCommandContext> >::iterator cmd; - cmd = session->incompleteCmds.find(id); - if (cmd != session->incompleteCmds.end()) { - boost::shared_ptr<IncompleteCommandContext> tmp(cmd->second); - session->incompleteCmds.erase(cmd); - - if (session->isAttached()) { - QPID_LOG(debug, ": receive completed for msg seq=" << id); - qpid::sys::ScopedUnlock<Mutex> ul(session->incompleteCmdsLock); - session->completeRcvMsg(id, requiresAccept, requiresSync); - return; - } + completerContext->scheduleMsgCompletion(id, requiresAccept, requiresSync); + } else { + // this path runs directly from the ac->end() call in handleContent() above, + // so *session and *msg are definately valid. + if (session->isAttached()) { + QPID_LOG(debug, ": receive completed for msg seq=" << id); + session->completeRcvMsg(id, requiresAccept, requiresSync); } } + completerContext = boost::intrusive_ptr<AsyncCommandCompleter>(); } -/** Scheduled from incomplete command's completed callback, safely completes all - * completed commands in the IO Thread. Guaranteed not to be running at the same - * time as the message receive code. +/** Scheduled from an asynchronous command's completed callback to run on + * the IO thread. */ -void SessionState::scheduledCompleter(boost::shared_ptr<SessionState::ScheduledCompleterContext> ctxt) +void SessionState::AsyncCommandCompleter::schedule(boost::intrusive_ptr<AsyncCommandCompleter> ctxt) { ctxt->completeCommands(); } -/** mark a command (sequence) as completed, return True if caller should - * schedule a call to completeCommands() +/** mark an ingress Message.Transfer command as completed. + * This method must be thread safe - it may run on any thread. */ -bool SessionState::ScheduledCompleterContext::scheduleCompletion(SequenceNumber cmd) +void SessionState::AsyncCommandCompleter::scheduleMsgCompletion(SequenceNumber cmd, + bool requiresAccept, + bool requiresSync) { - qpid::sys::ScopedLock<qpid::sys::Mutex> l(completedCmdsLock); - - completedCmds.push_back(cmd); - return (completedCmds.size() == 1); + qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock); + + if (session) { + MessageInfo msg(cmd, requiresAccept, requiresSync); + completedMsgs.push_back(msg); + if (completedMsgs.size() == 1) { + session->getConnection().requestIOProcessing(boost::bind(&schedule, + session->asyncCommandCompleter)); + } + } } -/** Cause the session to complete all completed commands */ -void SessionState::ScheduledCompleterContext::completeCommands() +/** Cause the session to complete all completed commands. + * Executes on the IO thread. + */ +void SessionState::AsyncCommandCompleter::completeCommands() { - qpid::sys::ScopedLock<qpid::sys::Mutex> l(completedCmdsLock); + qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock); // when session is destroyed, it clears the session pointer via cancel(). - if (!session) return; - - while (!completedCmds.empty()) { - SequenceNumber id = completedCmds.front(); - completedCmds.pop_front(); - std::map<SequenceNumber, boost::shared_ptr<IncompleteCommandContext> >::iterator cmd; - { - qpid::sys::ScopedLock<qpid::sys::Mutex> l(session->incompleteCmdsLock); - - cmd = session->incompleteCmds.find(id); - if (cmd !=session->incompleteCmds.end()) { - boost::shared_ptr<IncompleteCommandContext> tmp(cmd->second); - { - qpid::sys::ScopedUnlock<qpid::sys::Mutex> ul(session->incompleteCmdsLock); - tmp->do_completion(); // retakes incompleteCmdslock - } - } + if (session && session->isAttached()) { + for (std::vector<MessageInfo>::iterator msg = completedMsgs.begin(); + msg != completedMsgs.end(); ++msg) { + session->completeRcvMsg(msg->cmd, msg->requiresAccept, msg->requiresSync); } } + completedMsgs.clear(); } /** cancel any pending calls to scheduleComplete */ -void SessionState::ScheduledCompleterContext::cancel() +void SessionState::AsyncCommandCompleter::cancel() { - qpid::sys::ScopedLock<qpid::sys::Mutex> l(completedCmdsLock); + qpid::sys::ScopedLock<qpid::sys::Mutex> l(completerLock); session = 0; } diff --git a/qpid/cpp/src/qpid/broker/SessionState.h b/qpid/cpp/src/qpid/broker/SessionState.h index 5e162e6475..2250940102 100644 --- a/qpid/cpp/src/qpid/broker/SessionState.h +++ b/qpid/cpp/src/qpid/broker/SessionState.h @@ -38,6 +38,7 @@ #include <boost/noncopyable.hpp> #include <boost/scoped_ptr.hpp> +#include <boost/intrusive_ptr.hpp> #include <set> #include <vector> @@ -176,79 +177,84 @@ class SessionState : public qpid::SessionState, std::queue<SequenceNumber> pendingExecutionSyncs; bool currentCommandComplete; - /** Abstract class that represents a command that is pending - * completion. + /** This class provides a context for completing asynchronous commands in a thread + * safe manner. Asynchronous commands save their completion state in this class. + * This class then schedules the completeCommands() method in the IO thread. + * While running in the IO thread, completeCommands() may safely complete all + * saved commands without the risk of colliding with other operations on this + * SessionState. */ - class IncompleteCommandContext : public AsyncCompletion + class AsyncCommandCompleter : public RefCounted { + private: + SessionState *session; + qpid::sys::Mutex completerLock; + + // special-case message.transfer commands for optimization + struct MessageInfo { + SequenceNumber cmd; // message.transfer command id + bool requiresAccept; + bool requiresSync; + MessageInfo(SequenceNumber c, bool a, bool s) + : cmd(c), requiresAccept(a), requiresSync(s) {} + }; + std::vector<MessageInfo> completedMsgs; + + /** complete all pending commands, runs in IO thread */ + void completeCommands(); + + /** for scheduling a run of "completeCommands()" on the IO thread */ + static void schedule(boost::intrusive_ptr<AsyncCommandCompleter>); + + public: + AsyncCommandCompleter(SessionState *s) : session(s) {}; + ~AsyncCommandCompleter() {}; + + /** schedule the completion of an ingress message.transfer command */ + void scheduleMsgCompletion(SequenceNumber cmd, + bool requiresAccept, + bool requiresSync); + void cancel(); // called by SessionState destructor. + }; + boost::intrusive_ptr<AsyncCommandCompleter> asyncCommandCompleter; + + /** Abstract class that represents a single asynchronous command that is + * pending completion. + */ + class AsyncCommandContext : public AsyncCompletion::Callback { public: - IncompleteCommandContext( SessionState *ss, SequenceNumber _id ) - : id(_id), session(ss) {} - virtual ~IncompleteCommandContext() {} - - /* allows manual invokation of completion, used by IO thread to - * complete a command that was originally finished on a different - * thread. - */ - void do_completion() { completed(true); } + AsyncCommandContext( SessionState *ss, SequenceNumber _id ) + : id(_id), completerContext(ss->asyncCommandCompleter) {} + virtual ~AsyncCommandContext() {} protected: SequenceNumber id; - SessionState *session; + boost::intrusive_ptr<AsyncCommandCompleter> completerContext; }; /** incomplete Message.transfer commands - inbound to broker from client */ - class IncompleteIngressMsgXfer : public SessionState::IncompleteCommandContext + class IncompleteIngressMsgXfer : public SessionState::AsyncCommandContext { public: IncompleteIngressMsgXfer( SessionState *ss, - SequenceNumber _id, - boost::intrusive_ptr<Message> msg ) - : IncompleteCommandContext(ss, _id), - requiresAccept(msg->requiresAccept()), - requiresSync(msg->getFrames().getMethod()->isSync()) {}; + boost::intrusive_ptr<Message> m ) + : AsyncCommandContext(ss, m->getCommandId()), + session(ss), + msg(m.get()), + requiresAccept(msg->requiresAccept()), + requiresSync(msg->getFrames().getMethod()->isSync()) {}; virtual ~IncompleteIngressMsgXfer() {}; - protected: virtual void completed(bool); + virtual boost::intrusive_ptr<AsyncCompletion::Callback> clone(); private: - /** meta-info required to complete the message */ + SessionState *session; // only valid if sync == true + Message *msg; // only valid if sync == true bool requiresAccept; - bool requiresSync; // method's isSync() flag + bool requiresSync; }; - /** creates a command context suitable for use as an AsyncCompletion in a message */ - boost::shared_ptr<SessionState::IncompleteIngressMsgXfer> createIngressMsgXferContext( boost::intrusive_ptr<Message> msg); - - /* A list of commands that are pending completion. These commands are - * awaiting some set of asynchronous operations to finish (eg: store, - * flow-control, etc). before the command can be completed to the client - */ - std::map<SequenceNumber, boost::shared_ptr<IncompleteCommandContext> > incompleteCmds; - qpid::sys::Mutex incompleteCmdsLock; // locks above container - - /** This context is shared between the SessionState and scheduledCompleter, - * holds the sequence numbers of all commands that have completed asynchronously. - */ - class ScheduledCompleterContext { - private: - std::list<SequenceNumber> completedCmds; - // ordering: take this lock first, then incompleteCmdsLock - qpid::sys::Mutex completedCmdsLock; - SessionState *session; - public: - ScheduledCompleterContext(SessionState *s) : session(s) {}; - bool scheduleCompletion(SequenceNumber cmd); - void completeCommands(); - void cancel(); - }; - boost::shared_ptr<ScheduledCompleterContext> scheduledCompleterContext; - - /** The following method runs the in IO thread and completes commands that - * where finished asynchronously. - */ - static void scheduledCompleter(boost::shared_ptr<ScheduledCompleterContext>); friend class SessionManager; }; diff --git a/qpid/cpp/src/qpid/broker/ThresholdAlerts.cpp b/qpid/cpp/src/qpid/broker/ThresholdAlerts.cpp index 4f35884af8..d616abadd6 100644 --- a/qpid/cpp/src/qpid/broker/ThresholdAlerts.cpp +++ b/qpid/cpp/src/qpid/broker/ThresholdAlerts.cpp @@ -28,6 +28,52 @@ namespace qpid { namespace broker { +namespace { +const qmf::org::apache::qpid::broker::EventQueueThresholdExceeded EVENT("dummy", 0, 0); +bool isQMFv2(const boost::intrusive_ptr<Message> message) +{ + const qpid::framing::MessageProperties* props = message->getProperties<qpid::framing::MessageProperties>(); + return props && props->getAppId() == "qmf2"; +} + +bool isThresholdEvent(const boost::intrusive_ptr<Message> message) +{ + if (message->getIsManagementMessage()) { + //is this a qmf event? if so is it a threshold event? + if (isQMFv2(message)) { + const qpid::framing::FieldTable* headers = message->getApplicationHeaders(); + if (headers && headers->getAsString("qmf.content") == "_event") { + //decode as list + std::string content = message->getFrames().getContent(); + qpid::types::Variant::List list; + qpid::amqp_0_10::ListCodec::decode(content, list); + if (list.empty() || list.front().getType() != qpid::types::VAR_MAP) return false; + qpid::types::Variant::Map map = list.front().asMap(); + try { + std::string eventName = map["_schema_id"].asMap()["_class_name"].asString(); + return eventName == EVENT.getEventName(); + } catch (const std::exception& e) { + QPID_LOG(error, "Error checking for recursive threshold alert: " << e.what()); + } + } + } else { + std::string content = message->getFrames().getContent(); + qpid::framing::Buffer buffer(const_cast<char*>(content.data()), content.size()); + if (buffer.getOctet() == 'A' && buffer.getOctet() == 'M' && buffer.getOctet() == '2' && buffer.getOctet() == 'e') { + buffer.getLong();//sequence + std::string packageName; + buffer.getShortString(packageName); + if (packageName != EVENT.getPackageName()) return false; + std::string eventName; + buffer.getShortString(eventName); + return eventName == EVENT.getEventName(); + } + } + } + return false; +} +} + ThresholdAlerts::ThresholdAlerts(const std::string& n, qpid::management::ManagementAgent& a, const uint32_t ct, @@ -44,8 +90,13 @@ void ThresholdAlerts::enqueued(const QueuedMessage& m) if ((countThreshold && count >= countThreshold) || (sizeThreshold && size >= sizeThreshold)) { if ((repeatInterval == 0 && lastAlert == qpid::sys::EPOCH) || qpid::sys::Duration(lastAlert, qpid::sys::now()) > repeatInterval) { - agent.raiseEvent(qmf::org::apache::qpid::broker::EventQueueThresholdExceeded(name, count, size)); + //Note: Raising an event may result in messages being + //enqueued on queues; it may even be that this event + //causes a message to be enqueued on the queue we are + //tracking, and so we need to avoid recursing + if (isThresholdEvent(m.payload)) return; lastAlert = qpid::sys::now(); + agent.raiseEvent(qmf::org::apache::qpid::broker::EventQueueThresholdExceeded(name, count, size)); } } } @@ -75,12 +126,12 @@ void ThresholdAlerts::observe(Queue& queue, qpid::management::ManagementAgent& a } void ThresholdAlerts::observe(Queue& queue, qpid::management::ManagementAgent& agent, - const qpid::framing::FieldTable& settings) + const qpid::framing::FieldTable& settings, uint16_t limitRatio) { qpid::types::Variant::Map map; qpid::amqp_0_10::translate(settings, map); - observe(queue, agent, map); + observe(queue, agent, map, limitRatio); } template <class T> @@ -118,19 +169,19 @@ class Option }; void ThresholdAlerts::observe(Queue& queue, qpid::management::ManagementAgent& agent, - const qpid::types::Variant::Map& settings) + const qpid::types::Variant::Map& settings, uint16_t limitRatio) { //Note: aliases are keys defined by java broker Option<int64_t> repeatInterval("qpid.alert_repeat_gap", 60); repeatInterval.addAlias("x-qpid-minimum-alert-repeat-gap"); - //If no explicit threshold settings were given use 80% of any - //limit from the policy. + //If no explicit threshold settings were given use specified + //percentage of any limit from the policy. const QueuePolicy* policy = queue.getPolicy(); - Option<uint32_t> countThreshold("qpid.alert_count", (uint32_t) (policy ? policy->getMaxCount()*0.8 : 0)); + Option<uint32_t> countThreshold("qpid.alert_count", (uint32_t) (policy && limitRatio ? (policy->getMaxCount()*limitRatio/100) : 0)); countThreshold.addAlias("x-qpid-maximum-message-count"); - Option<uint64_t> sizeThreshold("qpid.alert_size", (uint64_t) (policy ? policy->getMaxSize()*0.8 : 0)); + Option<uint64_t> sizeThreshold("qpid.alert_size", (uint64_t) (policy && limitRatio ? (policy->getMaxSize()*limitRatio/100) : 0)); sizeThreshold.addAlias("x-qpid-maximum-message-size"); observe(queue, agent, countThreshold.get(settings), sizeThreshold.get(settings), repeatInterval.get(settings)); diff --git a/qpid/cpp/src/qpid/broker/ThresholdAlerts.h b/qpid/cpp/src/qpid/broker/ThresholdAlerts.h index e1f59252c4..c77722e700 100644 --- a/qpid/cpp/src/qpid/broker/ThresholdAlerts.h +++ b/qpid/cpp/src/qpid/broker/ThresholdAlerts.h @@ -55,9 +55,9 @@ class ThresholdAlerts : public QueueObserver const uint64_t sizeThreshold, const long repeatInterval); static void observe(Queue& queue, qpid::management::ManagementAgent& agent, - const qpid::framing::FieldTable& settings); + const qpid::framing::FieldTable& settings, uint16_t limitRatio); static void observe(Queue& queue, qpid::management::ManagementAgent& agent, - const qpid::types::Variant::Map& settings); + const qpid::types::Variant::Map& settings, uint16_t limitRatio); private: const std::string name; qpid::management::ManagementAgent& agent; diff --git a/qpid/cpp/src/qpid/broker/TopicExchange.cpp b/qpid/cpp/src/qpid/broker/TopicExchange.cpp index 1b0fe71bcf..f3baf00d1f 100644 --- a/qpid/cpp/src/qpid/broker/TopicExchange.cpp +++ b/qpid/cpp/src/qpid/broker/TopicExchange.cpp @@ -221,6 +221,7 @@ TopicExchange::TopicExchange(const std::string& _name, bool _durable, bool TopicExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* args) { + ClearCache cc(&cacheLock,&bindingCache); // clear the cache on function exit. string fedOp(args ? args->getAsString(qpidFedOp) : fedOpBind); string fedTags(args ? args->getAsString(qpidFedTags) : ""); string fedOrigin(args ? args->getAsString(qpidFedOrigin) : ""); @@ -288,6 +289,7 @@ bool TopicExchange::bind(Queue::shared_ptr queue, const string& routingKey, cons } bool TopicExchange::unbind(Queue::shared_ptr queue, const string& constRoutingKey, const FieldTable* /*args*/){ + ClearCache cc(&cacheLock,&bindingCache); // clear the cache on function exit. RWlock::ScopedWlock l(lock); string routingKey = normalize(constRoutingKey); BindingKey* bk = bindingTree.getBindingKey(routingKey); @@ -333,13 +335,24 @@ bool TopicExchange::isBound(Queue::shared_ptr queue, const string& pattern) void TopicExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/) { // Note: PERFORMANCE CRITICAL!!! - BindingList b(new std::vector<boost::shared_ptr<qpid::broker::Exchange::Binding> >); + BindingList b; + std::map<std::string, BindingList>::iterator it; + { // only lock the cache for read + RWlock::ScopedRlock cl(cacheLock); + it = bindingCache.find(routingKey); + } PreRoute pr(msg, this); - BindingsFinderIter bindingsFinder(b); + if (it == bindingCache.end()) // no cache hit { RWlock::ScopedRlock l(lock); + b = BindingList(new std::vector<boost::shared_ptr<qpid::broker::Exchange::Binding> >); + BindingsFinderIter bindingsFinder(b); bindingTree.iterateMatch(routingKey, bindingsFinder); - } + RWlock::ScopedWlock cwl(cacheLock); + bindingCache[routingKey] = b; // update cache + }else { + b = it->second; + } doRoute(msg, b); } diff --git a/qpid/cpp/src/qpid/broker/TopicExchange.h b/qpid/cpp/src/qpid/broker/TopicExchange.h index a6c457dcb3..3d2b3a95a9 100644 --- a/qpid/cpp/src/qpid/broker/TopicExchange.h +++ b/qpid/cpp/src/qpid/broker/TopicExchange.h @@ -135,7 +135,19 @@ class TopicExchange : public virtual Exchange { BindingNode bindingTree; unsigned long nBindings; qpid::sys::RWlock lock; // protects bindingTree and nBindings - + qpid::sys::RWlock cacheLock; // protects cache + std::map<std::string, BindingList> bindingCache; // cache of matched routes. + class ClearCache { + private: + qpid::sys::RWlock* cacheLock; + std::map<std::string, BindingList>* bindingCache; + public: + ClearCache(qpid::sys::RWlock* l, std::map<std::string, BindingList>* bc): cacheLock(l),bindingCache(bc) {}; + ~ClearCache(){ + qpid::sys::RWlock::ScopedWlock l(*cacheLock); + bindingCache->clear(); + }; + }; bool isBound(Queue::shared_ptr queue, const std::string& pattern); class ReOriginIter; diff --git a/qpid/cpp/src/qpid/client/ConnectionHandler.cpp b/qpid/cpp/src/qpid/client/ConnectionHandler.cpp index 8dc1e8338a..4fbf55aa60 100644 --- a/qpid/cpp/src/qpid/client/ConnectionHandler.cpp +++ b/qpid/cpp/src/qpid/client/ConnectionHandler.cpp @@ -22,6 +22,7 @@ #include "qpid/client/ConnectionHandler.h" #include "qpid/SaslFactory.h" +#include "qpid/StringUtils.h" #include "qpid/client/Bounds.h" #include "qpid/framing/amqp_framing.h" #include "qpid/framing/all_method_bodies.h" @@ -202,6 +203,24 @@ void ConnectionHandler::fail(const std::string& message) namespace { std::string SPACE(" "); + +std::string join(const std::vector<std::string>& in) +{ + std::string result; + for (std::vector<std::string>::const_iterator i = in.begin(); i != in.end(); ++i) { + if (result.size()) result += SPACE; + result += *i; + } + return result; +} + +void intersection(const std::vector<std::string>& a, const std::vector<std::string>& b, std::vector<std::string>& results) +{ + for (std::vector<std::string>::const_iterator i = a.begin(); i != a.end(); ++i) { + if (std::find(b.begin(), b.end(), *i) != b.end()) results.push_back(*i); + } +} + } void ConnectionHandler::start(const FieldTable& /*serverProps*/, const Array& mechanisms, const Array& /*locales*/) @@ -216,25 +235,24 @@ void ConnectionHandler::start(const FieldTable& /*serverProps*/, const Array& me maxSsf ); - std::string mechlist; - bool chosenMechanismSupported = mechanism.empty(); - for (Array::const_iterator i = mechanisms.begin(); i != mechanisms.end(); ++i) { - if (!mechanism.empty() && mechanism == (*i)->get<std::string>()) { - chosenMechanismSupported = true; - mechlist = (*i)->get<std::string>() + SPACE + mechlist; - } else { - if (i != mechanisms.begin()) mechlist += SPACE; - mechlist += (*i)->get<std::string>(); + std::vector<std::string> mechlist; + if (mechanism.empty()) { + //mechlist is simply what the server offers + mechanisms.collect(mechlist); + } else { + //mechlist is the intersection of those indicated by user and + //those supported by server, in the order listed by user + std::vector<std::string> allowed = split(mechanism, " "); + std::vector<std::string> supported; + mechanisms.collect(supported); + intersection(allowed, supported, mechlist); + if (mechlist.empty()) { + throw Exception(QPID_MSG("Desired mechanism(s) not valid: " << mechanism << " (supported: " << join(supported) << ")")); } } - if (!chosenMechanismSupported) { - fail("Selected mechanism not supported: " + mechanism); - } - if (sasl.get()) { - string response = sasl->start(mechanism.empty() ? mechlist : mechanism, - getSecuritySettings ? getSecuritySettings() : 0); + string response = sasl->start(join(mechlist), getSecuritySettings ? getSecuritySettings() : 0); proxy.startOk(properties, sasl->getMechanism(), response, locale); } else { //TODO: verify that desired mechanism and locale are supported diff --git a/qpid/cpp/src/qpid/client/TCPConnector.cpp b/qpid/cpp/src/qpid/client/TCPConnector.cpp index e284d57bec..d90781b365 100644 --- a/qpid/cpp/src/qpid/client/TCPConnector.cpp +++ b/qpid/cpp/src/qpid/client/TCPConnector.cpp @@ -117,7 +117,7 @@ void TCPConnector::connected(const Socket&) { void TCPConnector::start(sys::AsynchIO* aio_) { aio = aio_; - for (int i = 0; i < 32; i++) { + for (int i = 0; i < 4; i++) { aio->queueReadBuffer(new Buff(maxFrameSize)); } diff --git a/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp index 5a545c1f6a..a87a8dea67 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/ConnectionImpl.cpp @@ -39,6 +39,7 @@ using qpid::types::Variant; using qpid::types::VAR_LIST; using qpid::framing::Uuid; +namespace { void convert(const Variant::List& from, std::vector<std::string>& to) { for (Variant::List::const_iterator i = from.begin(); i != from.end(); ++i) { @@ -46,19 +47,6 @@ void convert(const Variant::List& from, std::vector<std::string>& to) } } -template <class T> bool setIfFound(const Variant::Map& map, const std::string& key, T& value) -{ - Variant::Map::const_iterator i = map.find(key); - if (i != map.end()) { - value = (T) i->second; - QPID_LOG(debug, "option " << key << " specified as " << i->second); - return true; - } else { - return false; - } -} - -namespace { std::string asString(const std::vector<std::string>& v) { std::stringstream os; os << "["; @@ -71,47 +59,6 @@ std::string asString(const std::vector<std::string>& v) { } } -template <> bool setIfFound< std::vector<std::string> >(const Variant::Map& map, - const std::string& key, - std::vector<std::string>& value) -{ - Variant::Map::const_iterator i = map.find(key); - if (i != map.end()) { - value.clear(); - if (i->second.getType() == VAR_LIST) { - convert(i->second.asList(), value); - } else { - value.push_back(i->second.asString()); - } - QPID_LOG(debug, "option " << key << " specified as " << asString(value)); - return true; - } else { - return false; - } -} - -void convert(const Variant::Map& from, ConnectionSettings& to) -{ - setIfFound(from, "username", to.username); - setIfFound(from, "password", to.password); - setIfFound(from, "sasl-mechanism", to.mechanism); - setIfFound(from, "sasl-service", to.service); - setIfFound(from, "sasl-min-ssf", to.minSsf); - setIfFound(from, "sasl-max-ssf", to.maxSsf); - - setIfFound(from, "heartbeat", to.heartbeat); - setIfFound(from, "tcp-nodelay", to.tcpNoDelay); - - setIfFound(from, "locale", to.locale); - setIfFound(from, "max-channels", to.maxChannels); - setIfFound(from, "max-frame-size", to.maxFrameSize); - setIfFound(from, "bounds", to.bounds); - - setIfFound(from, "transport", to.protocol); - - setIfFound(from, "ssl-cert-name", to.sslCertName); -} - ConnectionImpl::ConnectionImpl(const std::string& url, const Variant::Map& options) : reconnect(false), timeout(-1), limit(-1), minReconnectInterval(3), maxReconnectInterval(60), @@ -124,27 +71,64 @@ ConnectionImpl::ConnectionImpl(const std::string& url, const Variant::Map& optio void ConnectionImpl::setOptions(const Variant::Map& options) { - sys::Mutex::ScopedLock l(lock); - convert(options, settings); - setIfFound(options, "reconnect", reconnect); - setIfFound(options, "reconnect-timeout", timeout); - setIfFound(options, "reconnect-limit", limit); - int64_t reconnectInterval; - if (setIfFound(options, "reconnect-interval", reconnectInterval)) { - minReconnectInterval = maxReconnectInterval = reconnectInterval; - } else { - setIfFound(options, "reconnect-interval-min", minReconnectInterval); - setIfFound(options, "reconnect-interval-max", maxReconnectInterval); + for (Variant::Map::const_iterator i = options.begin(); i != options.end(); ++i) { + setOption(i->first, i->second); } - setIfFound(options, "reconnect-urls", urls); - setIfFound(options, "x-reconnect-on-limit-exceeded", reconnectOnLimitExceeded); } void ConnectionImpl::setOption(const std::string& name, const Variant& value) { - Variant::Map options; - options[name] = value; - setOptions(options); + sys::Mutex::ScopedLock l(lock); + if (name == "reconnect") { + reconnect = value; + } else if (name == "reconnect-timeout" || name == "reconnect_timeout") { + timeout = value; + } else if (name == "reconnect-limit" || name == "reconnect_limit") { + limit = value; + } else if (name == "reconnect-interval" || name == "reconnect_interval") { + maxReconnectInterval = minReconnectInterval = value; + } else if (name == "reconnect-interval-min" || name == "reconnect_interval_min") { + minReconnectInterval = value; + } else if (name == "reconnect-interval-max" || name == "reconnect_interval_max") { + maxReconnectInterval = value; + } else if (name == "reconnect-urls" || name == "reconnect_urls") { + if (value.getType() == VAR_LIST) { + convert(value.asList(), urls); + } else { + urls.push_back(value.asString()); + } + } else if (name == "username") { + settings.username = value.asString(); + } else if (name == "password") { + settings.password = value.asString(); + } else if (name == "sasl-mechanism" || name == "sasl_mechanism" || + name == "sasl-mechanisms" || name == "sasl_mechanisms") { + settings.mechanism = value.asString(); + } else if (name == "sasl-service" || name == "sasl_service") { + settings.service = value.asString(); + } else if (name == "sasl-min-ssf" || name == "sasl_min_ssf") { + settings.minSsf = value; + } else if (name == "sasl-max-ssf" || name == "sasl_max_ssf") { + settings.maxSsf = value; + } else if (name == "heartbeat") { + settings.heartbeat = value; + } else if (name == "tcp-nodelay" || name == "tcp_nodelay") { + settings.tcpNoDelay = value; + } else if (name == "locale") { + settings.locale = value.asString(); + } else if (name == "max-channels" || name == "max_channels") { + settings.maxChannels = value; + } else if (name == "max-frame-size" || name == "max_frame_size") { + settings.maxFrameSize = value; + } else if (name == "bounds") { + settings.bounds = value; + } else if (name == "transport") { + settings.protocol = value.asString(); + } else if (name == "ssl-cert-name" || name == "ssl_cert_name") { + settings.sslCertName = value.asString(); + } else { + throw qpid::messaging::MessagingException(QPID_MSG("Invalid option: " << name << " not recognised")); + } } diff --git a/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp b/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp index 82358961c8..d93416da75 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/OutgoingMessage.cpp @@ -59,7 +59,9 @@ void OutgoingMessage::convert(const qpid::messaging::Message& from) message.getMessageProperties().setReplyTo(AddressResolution::convert(address)); } translate(from.getProperties(), message.getMessageProperties().getApplicationHeaders()); - message.getDeliveryProperties().setTtl(from.getTtl().getMilliseconds()); + if (from.getTtl().getMilliseconds()) { + message.getDeliveryProperties().setTtl(from.getTtl().getMilliseconds()); + } if (from.getDurable()) { message.getDeliveryProperties().setDeliveryMode(DELIVERY_MODE_PERSISTENT); } diff --git a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp index e1b75ec0cf..f2f0f1a9e5 100644 --- a/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp +++ b/qpid/cpp/src/qpid/client/amqp0_10/SenderImpl.cpp @@ -135,6 +135,7 @@ void SenderImpl::sendUnreliable(const qpid::messaging::Message& m) void SenderImpl::replay(const sys::Mutex::ScopedLock&) { for (OutgoingMessages::iterator i = outgoing.begin(); i != outgoing.end(); ++i) { + i->message.setRedelivered(true); sink->send(session, name, *i); } } @@ -147,7 +148,7 @@ uint32_t SenderImpl::checkPendingSends(bool flush) { uint32_t SenderImpl::checkPendingSends(bool flush, const sys::Mutex::ScopedLock&) { if (flush) { - session.flush(); + session.flush(); flushed = true; } else { flushed = false; diff --git a/qpid/cpp/src/qpid/cluster/Cluster.cpp b/qpid/cpp/src/qpid/cluster/Cluster.cpp index fe5a1c806e..a8a99d83b5 100644 --- a/qpid/cpp/src/qpid/cluster/Cluster.cpp +++ b/qpid/cpp/src/qpid/cluster/Cluster.cpp @@ -943,6 +943,10 @@ void Cluster::checkUpdateIn(Lock& l) { mAgent->suppress(false); // Enable management output. mAgent->clusterUpdate(); } + // Restore alternate exchange settings on exchanges. + broker.getExchanges().eachExchange( + boost::bind(&broker::Exchange::recoveryComplete, _1, + boost::ref(broker.getExchanges()))); enableClusterSafe(); // Enable cluster-safe assertions deliverEventQueue.start(); } diff --git a/qpid/cpp/src/qpid/cluster/Connection.cpp b/qpid/cpp/src/qpid/cluster/Connection.cpp index e9b718e6de..c1304c2b75 100644 --- a/qpid/cpp/src/qpid/cluster/Connection.cpp +++ b/qpid/cpp/src/qpid/cluster/Connection.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -143,7 +143,7 @@ void Connection::init() { // Called when we have consumed a read buffer to give credit to the // connection layer to continue reading. void Connection::giveReadCredit(int credit) { - if (cluster.getSettings().readMax && credit) + if (cluster.getSettings().readMax && credit) output.giveReadCredit(credit); } @@ -201,7 +201,7 @@ void Connection::received(framing::AMQFrame& f) { } else { // Shadow or updated catch-up connection. if (f.getMethod() && f.getMethod()->isA<ConnectionCloseBody>()) { - if (isShadow()) + if (isShadow()) cluster.addShadowConnection(this); AMQFrame ok((ConnectionCloseOkBody())); connection->getOutput().send(ok); @@ -241,7 +241,7 @@ void Connection::deliverDoOutput(uint32_t limit) { void Connection::deliveredFrame(const EventFrame& f) { GiveReadCreditOnExit gc(*this, f.readCredit); assert(!catchUp); - currentChannel = f.frame.getChannel(); + currentChannel = f.frame.getChannel(); if (f.frame.getBody() // frame can be emtpy with just readCredit && !framing::invoke(*this, *f.frame.getBody()).wasHandled() // Connection contol. && !checkUnsupported(*f.frame.getBody())) // Unsupported operation. @@ -287,7 +287,7 @@ void Connection::deliverClose () { cluster.erase(self); } -// Close the connection +// Close the connection void Connection::close() { if (connection.get()) { QPID_LOG(debug, cluster << " closed connection " << *this); @@ -332,9 +332,9 @@ size_t Connection::decode(const char* data, size_t size) { if (!checkProtocolHeader(ptr, size)) // Updates ptr return 0; // Incomplete header - if (!connection->isOpen()) + if (!connection->isOpen()) processInitialFrames(ptr, end-ptr); // Updates ptr - + if (connection->isOpen() && end - ptr > 0) { // We're multi-casting, we will give read credit on delivery. grc.credit = 0; @@ -432,7 +432,7 @@ void Connection::sessionState( unknownCompleted, receivedIncomplete); QPID_LOG(debug, cluster << " received session state update for " << sessionState().getId()); - // The output tasks will be added later in the update process. + // The output tasks will be added later in the update process. connection->getOutputTasks().removeAll(); } @@ -478,7 +478,7 @@ void Connection::retractOffer() { void Connection::closeUpdated() { self.second = 0; // Mark this as completed update connection. - if (connection.get()) + if (connection.get()) connection->close(connection::CLOSE_CODE_NORMAL, "OK"); } @@ -529,7 +529,7 @@ void Connection::deliveryRecord(const string& qname, m = getUpdateMessage(); m.queue = queue.get(); m.position = position; - if (enqueued) queue->updateEnqueued(m); //inform queue of the message + if (enqueued) queue->updateEnqueued(m); //inform queue of the message } else { // Message at original position in original queue m = queue->find(position); } @@ -591,7 +591,7 @@ void Connection::txEnqueue(const std::string& queue) { void Connection::txPublish(const framing::Array& queues, bool delivered) { boost::shared_ptr<broker::TxPublish> txPub(new broker::TxPublish(getUpdateMessage().payload)); - for (framing::Array::const_iterator i = queues.begin(); i != queues.end(); ++i) + for (framing::Array::const_iterator i = queues.begin(); i != queues.end(); ++i) txPub->deliverTo(findQueue((*i)->get<std::string>())); txPub->delivered = delivered; txBuffer->enlist(txPub); @@ -614,12 +614,6 @@ void Connection::exchange(const std::string& encoded) { QPID_LOG(debug, cluster << " updated exchange " << ex->getName()); } -void Connection::queue(const std::string& encoded) { - Buffer buf(const_cast<char*>(encoded.data()), encoded.size()); - broker::Queue::shared_ptr q = broker::Queue::decode(cluster.getBroker().getQueues(), buf); - QPID_LOG(debug, cluster << " updated queue " << q->getName()); -} - void Connection::sessionError(uint16_t , const std::string& msg) { // Ignore errors before isOpen(), we're not multicasting yet. if (connection->isOpen()) @@ -678,6 +672,12 @@ void Connection::config(const std::string& encoded) { else throw Exception(QPID_MSG("Update failed, invalid kind of config: " << kind)); } +void Connection::doCatchupIoCallbacks() { + // We need to process IO callbacks during the catch-up phase in + // order to service asynchronous completions for messages + // transferred during catch-up. + if (catchUp) getBrokerConnection()->doIoCallbacks(); +} }} // Namespace qpid::cluster diff --git a/qpid/cpp/src/qpid/cluster/Connection.h b/qpid/cpp/src/qpid/cluster/Connection.h index 7ee85bf1aa..8a9891deb6 100644 --- a/qpid/cpp/src/qpid/cluster/Connection.h +++ b/qpid/cpp/src/qpid/cluster/Connection.h @@ -10,9 +10,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -62,7 +62,7 @@ class Connection : public sys::ConnectionInputHandler, public framing::AMQP_AllOperations::ClusterConnectionHandler, private broker::Connection::ErrorListener - + { public: @@ -73,7 +73,7 @@ class Connection : Connection(Cluster&, sys::ConnectionOutputHandler& out, const std::string& mgmtId, const ConnectionId& id, const qpid::sys::SecuritySettings& external); ~Connection(); - + ConnectionId getId() const { return self; } broker::Connection* getBrokerConnection() { return connection.get(); } const broker::Connection* getBrokerConnection() const { return connection.get(); } @@ -108,9 +108,9 @@ class Connection : void deliveredFrame(const EventFrame&); void consumerState(const std::string& name, bool blocked, bool notifyEnabled, const qpid::framing::SequenceNumber& position); - + // ==== Used in catch-up mode to build initial state. - // + // // State update methods. void shadowPrepare(const std::string&); @@ -123,9 +123,9 @@ class Connection : const framing::SequenceNumber& received, const framing::SequenceSet& unknownCompleted, const SequenceSet& receivedIncomplete); - + void outputTask(uint16_t channel, const std::string& name); - + void shadowReady(uint64_t memberId, uint64_t connectionId, const std::string& managementId, @@ -163,8 +163,7 @@ class Connection : void txEnd(); void accumulatedAck(const framing::SequenceSet&); - // Encoded queue/exchange replication. - void queue(const std::string& encoded); + // Encoded exchange replication. void exchange(const std::string& encoded); void giveReadCredit(int credit); @@ -189,6 +188,8 @@ class Connection : void setSecureConnection ( broker::SecureConnection * sc ); + void doCatchupIoCallbacks(); + private: struct NullFrameHandler : public framing::FrameHandler { void handle(framing::AMQFrame&) {} @@ -233,7 +234,7 @@ class Connection : // Error listener functions void connectionError(const std::string&); void sessionError(uint16_t channel, const std::string&); - + void init(); bool checkUnsupported(const framing::AMQBody& body); void deliverDoOutput(uint32_t limit); diff --git a/qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp b/qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp index 1354dab17b..13e95d1ec2 100644 --- a/qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp +++ b/qpid/cpp/src/qpid/cluster/OutputInterceptor.cpp @@ -7,9 +7,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -66,11 +66,14 @@ void OutputInterceptor::giveReadCredit(int32_t credit) { } // Called in write thread when the IO layer has no more data to write. -// We do nothing in the write thread, we run doOutput only on delivery -// of doOutput requests. -bool OutputInterceptor::doOutput() { return false; } +// We only process IO callbacks in the write thread during catch-up. +// Normally we run doOutput only on delivery of doOutput requests. +bool OutputInterceptor::doOutput() { + parent.doCatchupIoCallbacks(); + return false; +} -// Send output up to limit, calculate new limit. +// Send output up to limit, calculate new limit. void OutputInterceptor::deliverDoOutput(uint32_t limit) { sentDoOutput = false; sendMax = limit; @@ -78,7 +81,7 @@ void OutputInterceptor::deliverDoOutput(uint32_t limit) { if (parent.isLocal()) { size_t buffered = getBuffered(); if (buffered == 0 && sent == sendMax) // Could have sent more, increase the limit. - newLimit = sendMax*2; + newLimit = sendMax*2; else if (buffered > 0 && sent > 1) // Data left unsent, reduce the limit. newLimit = (sendMax + sent) / 2; } diff --git a/qpid/cpp/src/qpid/cluster/UpdateDataExchange.cpp b/qpid/cpp/src/qpid/cluster/UpdateDataExchange.cpp index 2a079b8881..e5cd82e3d3 100644 --- a/qpid/cpp/src/qpid/cluster/UpdateDataExchange.cpp +++ b/qpid/cpp/src/qpid/cluster/UpdateDataExchange.cpp @@ -36,13 +36,8 @@ const std::string UpdateDataExchange::MANAGEMENT_AGENTS_KEY("management-agents") const std::string UpdateDataExchange::MANAGEMENT_SCHEMAS_KEY("management-schemas"); const std::string UpdateDataExchange::MANAGEMENT_DELETED_OBJECTS_KEY("management-deleted-objects"); -std::ostream& operator<<(std::ostream& o, const UpdateDataExchange& c) { - return o << "cluster(" << c.clusterId << " UPDATER)"; -} - UpdateDataExchange::UpdateDataExchange(Cluster& cluster) : - Exchange(EXCHANGE_NAME, &cluster), - clusterId(cluster.getId()) + Exchange(EXCHANGE_NAME, &cluster) {} void UpdateDataExchange::route(broker::Deliverable& msg, const std::string& routingKey, @@ -62,11 +57,9 @@ void UpdateDataExchange::updateManagementAgent(management::ManagementAgent* agen framing::Buffer buf1(const_cast<char*>(managementAgents.data()), managementAgents.size()); agent->importAgents(buf1); - QPID_LOG(debug, *this << " updated management agents."); framing::Buffer buf2(const_cast<char*>(managementSchemas.data()), managementSchemas.size()); agent->importSchemas(buf2); - QPID_LOG(debug, *this << " updated management schemas."); using amqp_0_10::ListCodec; using types::Variant; @@ -78,7 +71,6 @@ void UpdateDataExchange::updateManagementAgent(management::ManagementAgent* agen new management::ManagementAgent::DeletedObject(*i))); } agent->importDeletedObjects(objects); - QPID_LOG(debug, *this << " updated management deleted objects."); } diff --git a/qpid/cpp/src/qpid/cluster/UpdateDataExchange.h b/qpid/cpp/src/qpid/cluster/UpdateDataExchange.h index 8c493e400a..d2f6c35ad0 100644 --- a/qpid/cpp/src/qpid/cluster/UpdateDataExchange.h +++ b/qpid/cpp/src/qpid/cluster/UpdateDataExchange.h @@ -74,11 +74,9 @@ class UpdateDataExchange : public broker::Exchange void updateManagementAgent(management::ManagementAgent* agent); private: - MemberId clusterId; std::string managementAgents; std::string managementSchemas; std::string managementDeletedObjects; - friend std::ostream& operator<<(std::ostream&, const UpdateDataExchange&); }; }} // namespace qpid::cluster diff --git a/qpid/cpp/src/qpid/management/ManagementAgent.cpp b/qpid/cpp/src/qpid/management/ManagementAgent.cpp index 8b4defaa73..7c9771fe79 100644 --- a/qpid/cpp/src/qpid/management/ManagementAgent.cpp +++ b/qpid/cpp/src/qpid/management/ManagementAgent.cpp @@ -75,6 +75,18 @@ namespace { } return n2; } + +struct ScopedManagementContext +{ + ScopedManagementContext(const qpid::broker::ConnectionState* context) + { + setManagementExecutionContext(context); + } + ~ScopedManagementContext() + { + setManagementExecutionContext(0); + } +}; } @@ -536,6 +548,7 @@ void ManagementAgent::sendBufferLH(Buffer& buf, dp->setRoutingKey(routingKey); msg->getFrames().append(content); + msg->setIsManagementMessage(true); { sys::Mutex::ScopedUnlock u(userLock); @@ -612,6 +625,7 @@ void ManagementAgent::sendBufferLH(const string& data, msg->setTimestamp(broker->getExpiryPolicy()); } msg->getFrames().append(content); + msg->setIsManagementMessage(true); { sys::Mutex::ScopedUnlock u(userLock); @@ -2238,7 +2252,7 @@ void ManagementAgent::dispatchAgentCommandLH(Message& msg, bool viaLocal) uint32_t bufferLen = inBuffer.getPosition(); inBuffer.reset(); - setManagementExecutionContext((const qpid::broker::ConnectionState*) msg.getPublisher()); + ScopedManagementContext context((const qpid::broker::ConnectionState*) msg.getPublisher()); const framing::FieldTable *headers = msg.getApplicationHeaders(); if (headers && msg.getAppId() == "qmf2") { diff --git a/qpid/cpp/src/qpid/sys/ClusterSafe.cpp b/qpid/cpp/src/qpid/sys/ClusterSafe.cpp index b67b04c267..dd37615145 100644 --- a/qpid/cpp/src/qpid/sys/ClusterSafe.cpp +++ b/qpid/cpp/src/qpid/sys/ClusterSafe.cpp @@ -51,6 +51,16 @@ ClusterSafeScope::~ClusterSafeScope() { inContext = save; } +ClusterUnsafeScope::ClusterUnsafeScope() { + save = inContext; + inContext = false; +} + +ClusterUnsafeScope::~ClusterUnsafeScope() { + assert(!inContext); + inContext = save; +} + void enableClusterSafe() { inCluster = true; } }} // namespace qpid::sys diff --git a/qpid/cpp/src/qpid/sys/ClusterSafe.h b/qpid/cpp/src/qpid/sys/ClusterSafe.h index 42e290f4c8..27e4eb46a5 100644 --- a/qpid/cpp/src/qpid/sys/ClusterSafe.h +++ b/qpid/cpp/src/qpid/sys/ClusterSafe.h @@ -53,10 +53,8 @@ QPID_COMMON_EXTERN void assertClusterSafe(); QPID_COMMON_EXTERN bool isClusterSafe(); /** - * Base class for classes that encapsulate state which is replicated - * to all members of a cluster. Acts as a marker for clustered state - * and provides functions to assist detecting bugs in cluster - * behavior. + * Mark a scope as cluster safe. Sets isClusterSafe in constructor and resets + * to previous value in destructor. */ class ClusterSafeScope { public: @@ -67,6 +65,18 @@ class ClusterSafeScope { }; /** + * Mark a scope as cluster unsafe. Clears isClusterSafe in constructor and resets + * to previous value in destructor. + */ +class ClusterUnsafeScope { + public: + QPID_COMMON_EXTERN ClusterUnsafeScope(); + QPID_COMMON_EXTERN ~ClusterUnsafeScope(); + private: + bool save; +}; + +/** * Enable cluster-safe assertions. By default they are no-ops. * Called by cluster code. */ diff --git a/qpid/cpp/src/qpid/sys/SslPlugin.cpp b/qpid/cpp/src/qpid/sys/SslPlugin.cpp index b0e791d60b..0ec051caab 100644 --- a/qpid/cpp/src/qpid/sys/SslPlugin.cpp +++ b/qpid/cpp/src/qpid/sys/SslPlugin.cpp @@ -95,7 +95,7 @@ static struct SslPlugin : public Plugin { // Only provide to a Broker if (broker) { if (options.certDbPath.empty()) { - QPID_LOG(info, "SSL plugin not enabled, you must set --ssl-cert-db to enable it."); + QPID_LOG(notice, "SSL plugin not enabled, you must set --ssl-cert-db to enable it."); } else { try { ssl::initNSS(options, true); diff --git a/qpid/cpp/src/qpid/types/Variant.cpp b/qpid/cpp/src/qpid/types/Variant.cpp index 5d8878bdac..9cc3cfe5fc 100644 --- a/qpid/cpp/src/qpid/types/Variant.cpp +++ b/qpid/cpp/src/qpid/types/Variant.cpp @@ -108,7 +108,6 @@ class VariantImpl } value; std::string encoding;//optional encoding for variable length data - std::string getTypeName(VariantType type) const; template<class T> T convertFromString() const { std::string* s = reinterpret_cast<std::string*>(value.v); @@ -582,7 +581,7 @@ const std::string& VariantImpl::getString() const void VariantImpl::setEncoding(const std::string& s) { encoding = s; } const std::string& VariantImpl::getEncoding() const { return encoding; } -std::string VariantImpl::getTypeName(VariantType type) const +std::string getTypeName(VariantType type) { switch (type) { case VAR_VOID: return "void"; diff --git a/qpid/cpp/src/tests/Makefile.am b/qpid/cpp/src/tests/Makefile.am index ffa4a65ba7..da01ee7588 100644 --- a/qpid/cpp/src/tests/Makefile.am +++ b/qpid/cpp/src/tests/Makefile.am @@ -287,18 +287,6 @@ check_PROGRAMS+=datagen datagen_SOURCES=datagen.cpp datagen_LDADD=$(lib_common) $(lib_client) -check_PROGRAMS+=qrsh_server -qrsh_server_SOURCES=qrsh_server.cpp -qrsh_server_LDADD=$(lib_client) - -check_PROGRAMS+=qrsh_run -qrsh_run_SOURCES=qrsh_run.cpp -qrsh_run_LDADD=$(lib_client) - -check_PROGRAMS+=qrsh -qrsh_SOURCES=qrsh.cpp -qrsh_LDADD=$(lib_client) - check_PROGRAMS+=qpid-stream qpid_stream_INCLUDES=$(PUBLIC_INCLUDES) qpid_stream_SOURCES=qpid-stream.cpp diff --git a/qpid/cpp/src/tests/MessageUtils.h b/qpid/cpp/src/tests/MessageUtils.h index baca14cf4e..a1b140d484 100644 --- a/qpid/cpp/src/tests/MessageUtils.h +++ b/qpid/cpp/src/tests/MessageUtils.h @@ -20,7 +20,6 @@ */ #include "qpid/broker/Message.h" -#include "qpid/broker/AsyncCompletion.h" #include "qpid/framing/AMQFrame.h" #include "qpid/framing/MessageTransferBody.h" #include "qpid/framing/Uuid.h" @@ -29,17 +28,6 @@ using namespace qpid; using namespace broker; using namespace framing; -namespace { - class DummyCompletion : public AsyncCompletion - { - public: - DummyCompletion() {} - virtual ~DummyCompletion() {} - protected: - void completed(bool) {} - }; -} - namespace qpid { namespace tests { @@ -62,8 +50,6 @@ struct MessageUtils msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey); if (durable) msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setDeliveryMode(2); - boost::shared_ptr<AsyncCompletion>dc(new DummyCompletion()); - msg->setIngressCompletion(dc); return msg; } diff --git a/qpid/cpp/src/tests/QueueTest.cpp b/qpid/cpp/src/tests/QueueTest.cpp index e4e9897195..2059727e7b 100644 --- a/qpid/cpp/src/tests/QueueTest.cpp +++ b/qpid/cpp/src/tests/QueueTest.cpp @@ -88,8 +88,6 @@ intrusive_ptr<Message> create_message(std::string exchange, std::string routingK msg->getFrames().append(method); msg->getFrames().append(header); msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey); - boost::shared_ptr<AsyncCompletion>dc(new DummyCompletion()); - msg->setIngressCompletion(dc); return msg; } @@ -637,7 +635,7 @@ QPID_AUTO_TEST_CASE(testLVQRecover){ Queue::shared_ptr queue1(new Queue("my-queue", true, &testStore)); intrusive_ptr<Message> received; - queue1->configure(args); + queue1->create(args); intrusive_ptr<Message> msg1 = create_message("e", "A"); intrusive_ptr<Message> msg2 = create_message("e", "A"); @@ -709,9 +707,9 @@ QPID_AUTO_TEST_CASE(testMultiQueueLastNode){ args.setPersistLastNode(); Queue::shared_ptr queue1(new Queue("queue1", true, &testStore )); - queue1->configure(args); + queue1->create(args); Queue::shared_ptr queue2(new Queue("queue2", true, &testStore )); - queue2->configure(args); + queue2->create(args); intrusive_ptr<Message> msg1 = create_message("e", "A"); @@ -797,7 +795,7 @@ not requeued to the store. Queue::shared_ptr queue1(new Queue("my-queue", true, &testStore)); intrusive_ptr<Message> received; - queue1->configure(args); + queue1->create(args); // check requeue 1 intrusive_ptr<Message> msg1 = create_message("e", "C"); diff --git a/qpid/cpp/src/tests/acl.py b/qpid/cpp/src/tests/acl.py index 2d6a5b489d..5e9a150d8f 100755 --- a/qpid/cpp/src/tests/acl.py +++ b/qpid/cpp/src/tests/acl.py @@ -26,10 +26,11 @@ from qpid.datatypes import uuid4 from qpid.testlib import TestBase010 from qmf.console import Session from qpid.datatypes import Message +import qpid.messaging class ACLFile: - def __init__(self): - self.f = open('data_dir/policy.acl','w'); + def __init__(self, policy='data_dir/policy.acl'): + self.f = open(policy,'w') def write(self,line): self.f.write(line) @@ -50,14 +51,24 @@ class ACLTests(TestBase010): acl = self.qmf.getObjects(_class="acl")[0] return acl.reloadACLFile() + def get_acl_file(self): + return ACLFile(self.config.defines.get("policy-file", "data_dir/policy.acl")) + def setUp(self): - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('acl allow all all\n') aclf.close() TestBase010.setUp(self) self.startQmf() self.reload_acl() - + + def tearDown(self): + aclf = self.get_acl_file() + aclf.write('acl allow all all\n') + aclf.close() + self.reload_acl() + TestBase010.tearDown(self) + #===================================== # ACL general tests #===================================== @@ -66,7 +77,7 @@ class ACLTests(TestBase010): """ Test the deny all mode """ - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('acl allow anonymous all all\n') aclf.write('acl allow bob@QPID create queue\n') aclf.write('acl deny all all') @@ -94,7 +105,7 @@ class ACLTests(TestBase010): """ Test the allow all mode """ - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('acl deny bob@QPID bind exchange\n') aclf.write('acl allow all all') aclf.close() @@ -126,7 +137,7 @@ class ACLTests(TestBase010): """ Test empty groups """ - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('acl group\n') aclf.write('acl group admins bob@QPID joe@QPID\n') aclf.write('acl allow all all') @@ -140,7 +151,7 @@ class ACLTests(TestBase010): """ Test illegal acl formats """ - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('acl group admins bob@QPID joe@QPID\n') aclf.write('acl allow all all') aclf.close() @@ -154,7 +165,7 @@ class ACLTests(TestBase010): Test illegal extension lines """ - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('group admins bob@QPID \n') aclf.write(' \ \n') aclf.write('joe@QPID \n') @@ -172,7 +183,7 @@ class ACLTests(TestBase010): """ Test proper extention lines """ - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('group test1 joe@EXAMPLE.com \\ \n') # should be allowed aclf.write(' jack@EXAMPLE.com \\ \n') # should be allowed aclf.write('jill@TEST.COM \\ \n') # should be allowed @@ -189,7 +200,7 @@ class ACLTests(TestBase010): Test a user defined without a realm Ex. group admin rajith """ - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('group admin bob\n') # shouldn't be allowed aclf.write('acl deny admin bind exchange\n') aclf.write('acl allow all all') @@ -204,7 +215,7 @@ class ACLTests(TestBase010): Test a user defined without a realm Ex. group admin rajith """ - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('group test1 joe@EXAMPLE.com\n') # should be allowed aclf.write('group test2 jack_123-jill@EXAMPLE.com\n') # should be allowed aclf.write('group test4 host/somemachine.example.com@EXAMPLE.COM\n') # should be allowed @@ -215,7 +226,7 @@ class ACLTests(TestBase010): if (result.text.find("ACL format error",0,len(result.text)) != -1): self.fail(result) - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('group test1 joe$H@EXAMPLE.com\n') # shouldn't be allowed aclf.write('acl allow all all') aclf.close() @@ -233,7 +244,7 @@ class ACLTests(TestBase010): Test illegal queue policy """ - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('acl deny bob@QPID create queue name=q2 exclusive=true policytype=ding\n') aclf.write('acl allow all all') aclf.close() @@ -249,7 +260,7 @@ class ACLTests(TestBase010): Test illegal queue policy """ - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('acl deny bob@QPID create queue name=q2 maxqueuesize=-1\n') aclf.write('acl allow all all') aclf.close() @@ -260,7 +271,7 @@ class ACLTests(TestBase010): if (result.text != expected): self.fail(result) - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('acl deny bob@QPID create queue name=q2 maxqueuesize=9223372036854775808\n') aclf.write('acl allow all all') aclf.close() @@ -277,7 +288,7 @@ class ACLTests(TestBase010): Test illegal queue policy """ - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('acl deny bob@QPID create queue name=q2 maxqueuecount=-1\n') aclf.write('acl allow all all') aclf.close() @@ -288,7 +299,7 @@ class ACLTests(TestBase010): if (result.text != expected): self.fail(result) - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('acl deny bob@QPID create queue name=q2 maxqueuecount=9223372036854775808\n') aclf.write('acl allow all all') aclf.close() @@ -308,7 +319,7 @@ class ACLTests(TestBase010): """ Test cases for queue acl in allow mode """ - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('acl deny bob@QPID create queue name=q1 durable=true passive=true\n') aclf.write('acl deny bob@QPID create queue name=q2 exclusive=true policytype=ring\n') aclf.write('acl deny bob@QPID access queue name=q3\n') @@ -411,7 +422,7 @@ class ACLTests(TestBase010): """ Test cases for queue acl in deny mode """ - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('acl allow bob@QPID create queue name=q1 durable=true passive=true\n') aclf.write('acl allow bob@QPID create queue name=q2 exclusive=true policytype=ring\n') aclf.write('acl allow bob@QPID access queue name=q3\n') @@ -534,7 +545,7 @@ class ACLTests(TestBase010): """ Test cases for exchange acl in allow mode """ - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('acl deny bob@QPID create exchange name=testEx durable=true passive=true\n') aclf.write('acl deny bob@QPID create exchange name=ex1 type=direct\n') aclf.write('acl deny bob@QPID access exchange name=myEx queuename=q1 routingkey=rk1.*\n') @@ -665,7 +676,7 @@ class ACLTests(TestBase010): """ Test cases for exchange acl in deny mode """ - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('acl allow bob@QPID create exchange name=myEx durable=true passive=false\n') aclf.write('acl allow bob@QPID bind exchange name=amq.topic queuename=bar routingkey=foo.*\n') aclf.write('acl allow bob@QPID unbind exchange name=amq.topic queuename=bar routingkey=foo.*\n') @@ -772,6 +783,52 @@ class ACLTests(TestBase010): if (403 == e.args[0].error_code): self.fail("ACL should allow exchange delete request for myEx"); + def test_create_and_delete_exchange_via_qmf(self): + """ + Test acl is enforced when creating/deleting via QMF + methods. Note that in order to be able to send the QMF methods + and receive the responses a significant amount of permissions + need to be enabled (TODO: can the set below be narrowed down + at all?) + """ + aclf = self.get_acl_file() + aclf.write('acl allow bob@QPID create exchange\n') + aclf.write('acl allow admin@QPID delete exchange\n') + aclf.write('acl allow all access exchange\n') + aclf.write('acl allow all bind exchange\n') + aclf.write('acl allow all create queue\n') + aclf.write('acl allow all access queue\n') + aclf.write('acl allow all delete queue\n') + aclf.write('acl allow all consume queue\n') + aclf.write('acl allow all access method\n') + aclf.write('acl deny all all') + aclf.close() + + result = self.reload_acl() + if (result.text.find("format error",0,len(result.text)) != -1): + self.fail(result) + + bob = BrokerAdmin(self.config.broker, "bob", "bob") + bob.create_exchange("my-exchange") #should pass + #cleanup by deleting exchange + try: + bob.delete_exchange("my-exchange") #should fail + self.fail("ACL should deny exchange delete request for my-exchange"); + except Exception, e: + self.assertEqual(7,e.args[0]["error_code"]) + assert e.args[0]["error_text"].find("unauthorized-access") == 0 + admin = BrokerAdmin(self.config.broker, "admin", "admin") + admin.delete_exchange("my-exchange") #should pass + + anonymous = BrokerAdmin(self.config.broker) + try: + anonymous.create_exchange("another-exchange") #should fail + self.fail("ACL should deny exchange create request for another-exchange"); + except Exception, e: + self.assertEqual(7,e.args[0]["error_code"]) + assert e.args[0]["error_text"].find("unauthorized-access") == 0 + + #===================================== # ACL consume tests #===================================== @@ -780,7 +837,7 @@ class ACLTests(TestBase010): """ Test cases for consume in allow mode """ - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('acl deny bob@QPID consume queue name=q1\n') aclf.write('acl deny bob@QPID consume queue name=q2\n') aclf.write('acl allow all all') @@ -826,7 +883,7 @@ class ACLTests(TestBase010): """ Test cases for consume in allow mode """ - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('acl allow bob@QPID consume queue name=q1\n') aclf.write('acl allow bob@QPID consume queue name=q2\n') aclf.write('acl allow bob@QPID create queue\n') @@ -872,7 +929,7 @@ class ACLTests(TestBase010): """ Test various publish acl """ - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('acl deny bob@QPID publish exchange name=amq.direct routingkey=rk1\n') aclf.write('acl deny bob@QPID publish exchange name=amq.topic\n') aclf.write('acl deny bob@QPID publish exchange name=myEx routingkey=rk2\n') @@ -921,7 +978,7 @@ class ACLTests(TestBase010): """ Test various publish acl """ - aclf = ACLFile() + aclf = self.get_acl_file() aclf.write('acl allow bob@QPID publish exchange name=amq.direct routingkey=rk1\n') aclf.write('acl allow bob@QPID publish exchange name=amq.topic\n') aclf.write('acl allow bob@QPID publish exchange name=myEx routingkey=rk2\n') @@ -972,3 +1029,49 @@ class ACLTests(TestBase010): except qpid.session.SessionException, e: if (403 == e.args[0].error_code): self.fail("ACL should allow message transfer to exchange amq.direct with routing key rk1"); + +class BrokerAdmin: + def __init__(self, broker, username=None, password=None): + self.connection = qpid.messaging.Connection(broker) + if username: + self.connection.username = username + self.connection.password = password + self.connection.sasl_mechanisms = "PLAIN" + self.connection.open() + self.session = self.connection.session() + self.sender = self.session.sender("qmf.default.direct/broker") + self.reply_to = "responses-#; {create:always}" + self.receiver = self.session.receiver(self.reply_to) + + def invoke(self, method, arguments): + content = { + "_object_id": {"_object_name": "org.apache.qpid.broker:broker:amqp-broker"}, + "_method_name": method, + "_arguments": arguments + } + request = qpid.messaging.Message(reply_to=self.reply_to, content=content) + request.properties["x-amqp-0-10.app-id"] = "qmf2" + request.properties["qmf.opcode"] = "_method_request" + self.sender.send(request) + response = self.receiver.fetch() + self.session.acknowledge() + if response.properties['x-amqp-0-10.app-id'] == 'qmf2': + if response.properties['qmf.opcode'] == '_method_response': + return response.content['_arguments'] + elif response.properties['qmf.opcode'] == '_exception': + raise Exception(response.content['_values']) + else: raise Exception("Invalid response received, unexpected opcode: %s" % response.properties['qmf.opcode']) + else: raise Exception("Invalid response received, not a qmfv2 method: %s" % response.properties['x-amqp-0-10.app-id']) + def create_exchange(self, name, exchange_type=None, options={}): + properties = options + if exchange_type: properties["exchange_type"] = exchange_type + self.invoke("create", {"type": "exchange", "name":name, "properties":properties}) + + def create_queue(self, name, properties={}): + self.invoke("create", {"type": "queue", "name":name, "properties":properties}) + + def delete_exchange(self, name): + self.invoke("delete", {"type": "exchange", "name":name}) + + def delete_queue(self, name): + self.invoke("delete", {"type": "queue", "name":name}) diff --git a/qpid/cpp/src/tests/brokertest.py b/qpid/cpp/src/tests/brokertest.py index 54327873db..67765c0e46 100644 --- a/qpid/cpp/src/tests/brokertest.py +++ b/qpid/cpp/src/tests/brokertest.py @@ -62,24 +62,6 @@ def is_running(pid): class BadProcessStatus(Exception): pass -class ExceptionWrapper: - """Proxy object that adds a message to exceptions raised""" - def __init__(self, obj, msg): - self.obj = obj - self.msg = msg - - def __getattr__(self, name): - func = getattr(self.obj, name) - if type(func) != callable: - return func - return lambda *args, **kwargs: self._wrap(func, args, kwargs) - - def _wrap(self, func, args, kwargs): - try: - return func(*args, **kwargs) - except Exception, e: - raise Exception("%s: %s" %(self.msg, str(e))) - def error_line(filename, n=1): """Get the last n line(s) of filename for error messages""" result = [] @@ -89,7 +71,8 @@ def error_line(filename, n=1): for l in f: if len(result) == n: result.pop(0) result.append(" "+l) - finally: f.close() + finally: + f.close() except: return "" return ":\n" + "".join(result) @@ -97,111 +80,85 @@ def retry(function, timeout=10, delay=.01): """Call function until it returns True or timeout expires. Double the delay for each retry. Return True if function returns true, False if timeout expires.""" + deadline = time.time() + timeout while not function(): - if delay > timeout: delay = timeout + remaining = deadline - time.time() + if remaining <= 0: return False + delay = min(delay, remaining) time.sleep(delay) - timeout -= delay - if timeout <= 0: return False delay *= 2 return True +class AtomicCounter: + def __init__(self): + self.count = 0 + self.lock = Lock() + + def next(self): + self.lock.acquire(); + ret = self.count + self.count += 1 + self.lock.release(); + return ret + +_popen_id = AtomicCounter() # Popen identifier for use in output file names. + +# Constants for file descriptor arguments to Popen +FILE = "FILE" # Write to file named after process +PIPE = subprocess.PIPE + class Popen(subprocess.Popen): """ Can set and verify expectation of process status at end of test. Dumps command line, stdout, stderr to data dir for debugging. """ - class DrainThread(Thread): - """Thread to drain a file object and write the data to a file.""" - def __init__(self, infile, outname): - Thread.__init__(self) - self.infile, self.outname = infile, outname - self.outfile = None - - def run(self): - try: - for line in self.infile: - if self.outfile is None: - self.outfile = open(self.outname, "w") - self.outfile.write(line) - finally: - self.infile.close() - if self.outfile is not None: self.outfile.close() - - class OutStream(ExceptionWrapper): - """Wrapper for output streams, handles exceptions & draining output""" - def __init__(self, infile, outfile, msg): - ExceptionWrapper.__init__(self, infile, msg) - self.infile, self.outfile = infile, outfile - self.thread = None - - def drain(self): - if self.thread is None: - self.thread = Popen.DrainThread(self.infile, self.outfile) - self.thread.start() - - def outfile(self, ext): return "%s.%s" % (self.pname, ext) - - def __init__(self, cmd, expect=EXPECT_EXIT_OK, drain=True): - """Run cmd (should be a list of arguments) + def __init__(self, cmd, expect=EXPECT_EXIT_OK, stdin=None, stdout=FILE, stderr=FILE): + """Run cmd (should be a list of program and arguments) expect - if set verify expectation at end of test. - drain - if true (default) drain stdout/stderr to files. + stdout, stderr - can have the same values as for subprocess.Popen as well as + FILE (the default) which means write to a file named after the process. + stdin - like subprocess.Popen but defauts to PIPE """ self._clean = False self._clean_lock = Lock() assert find_exe(cmd[0]), "executable not found: "+cmd[0] if type(cmd) is type(""): cmd = [cmd] # Make it a list. self.cmd = [ str(x) for x in cmd ] - self.returncode = None self.expect = expect + self.id = _popen_id.next() + self.pname = "%s-%d" % (os.path.split(self.cmd[0])[1], self.id) + if stdout == FILE: stdout = open(self.outfile("out"), "w") + if stderr == FILE: stderr = open(self.outfile("err"), "w") try: - subprocess.Popen.__init__(self, self.cmd, 0, None, subprocess.PIPE, subprocess.PIPE, subprocess.PIPE, close_fds=True) - except ValueError: # Windows can't do close_fds - subprocess.Popen.__init__(self, self.cmd, 0, None, subprocess.PIPE, subprocess.PIPE, subprocess.PIPE) - self.pname = "%s-%d" % (os.path.split(self.cmd[0])[1], self.pid) - msg = "Process %s" % self.pname - self.stdin = ExceptionWrapper(self.stdin, msg) - self.stdout = Popen.OutStream(self.stdout, self.outfile("out"), msg) - self.stderr = Popen.OutStream(self.stderr, self.outfile("err"), msg) + subprocess.Popen.__init__(self, self.cmd, bufsize=0, executable=None, + stdin=stdin, stdout=stdout, stderr=stderr, + close_fds=True) + except ValueError: # Windows can't do close_fds + subprocess.Popen.__init__(self, self.cmd, bufsize=0, executable=None, + stdin=stdin, stdout=stdout, stderr=stderr) + f = open(self.outfile("cmd"), "w") - try: f.write(self.cmd_str()) + try: f.write("%s\n%d"%(self.cmd_str(), self.pid)) finally: f.close() log.debug("Started process %s: %s" % (self.pname, " ".join(self.cmd))) - if drain: self.drain() - - def __str__(self): return "Popen<%s>"%(self.pname) - def drain(self): - """Start threads to drain stdout/err""" - self.stdout.drain() - self.stderr.drain() + def __str__(self): return "Popen<%s>"%(self.pname) - def _cleanup(self): - """Close pipes to sub-process""" - self._clean_lock.acquire() - try: - if self._clean: return - self._clean = True - self.stdin.close() - self.drain() # Drain output pipes. - self.stdout.thread.join() # Drain thread closes pipe. - self.stderr.thread.join() - finally: self._clean_lock.release() + def outfile(self, ext): return "%s.%s" % (self.pname, ext) def unexpected(self,msg): err = error_line(self.outfile("err")) or error_line(self.outfile("out")) raise BadProcessStatus("%s %s%s" % (self.pname, msg, err)) - + def stop(self): # Clean up at end of test. try: if self.expect == EXPECT_UNKNOWN: try: self.kill() # Just make sure its dead except: pass elif self.expect == EXPECT_RUNNING: - try: - self.kill() - except: - self.unexpected("expected running, exit code %d" % self.wait()) + try: self.kill() + except: self.unexpected("expected running, exit code %d" % self.wait()) else: retry(lambda: self.poll() is not None) if self.returncode is None: # Still haven't stopped @@ -213,40 +170,21 @@ class Popen(subprocess.Popen): self.unexpected("expected error") finally: self.wait() # Clean up the process. - + def communicate(self, input=None): - if input: - self.stdin.write(input) - self.stdin.close() - outerr = (self.stdout.read(), self.stderr.read()) - self.wait() - return outerr + ret = subprocess.Popen.communicate(self, input) + self.cleanup() + return ret - def is_running(self): - return self.poll() is None + def is_running(self): return self.poll() is None def assert_running(self): if not self.is_running(): self.unexpected("Exit code %d" % self.returncode) - def poll(self, _deadstate=None): # _deadstate required by base class in python 2.4 - if self.returncode is None: - # Pass _deadstate only if it has been set, there is no _deadstate - # parameter in Python 2.6 - if _deadstate is None: ret = subprocess.Popen.poll(self) - else: ret = subprocess.Popen.poll(self, _deadstate) - - if (ret != -1): - self.returncode = ret - self._cleanup() - return self.returncode - def wait(self): - if self.returncode is None: - self.drain() - try: self.returncode = subprocess.Popen.wait(self) - except OSError,e: raise OSError("Wait failed %s: %s"%(self.pname, e)) - self._cleanup() - return self.returncode + ret = subprocess.Popen.wait(self) + self._cleanup() + return ret def terminate(self): try: subprocess.Popen.terminate(self) @@ -255,7 +193,8 @@ class Popen(subprocess.Popen): os.kill( self.pid , signal.SIGTERM) except AttributeError: # no os.kill, using taskkill.. (Windows only) os.popen('TASKKILL /PID ' +str(self.pid) + ' /F') - + self._cleanup() + def kill(self): try: subprocess.Popen.kill(self) except AttributeError: # No terminate method @@ -263,6 +202,20 @@ class Popen(subprocess.Popen): os.kill( self.pid , signal.SIGKILL) except AttributeError: # no os.kill, using taskkill.. (Windows only) os.popen('TASKKILL /PID ' +str(self.pid) + ' /F') + self._cleanup() + + def _cleanup(self): + """Clean up after a dead process""" + self._clean_lock.acquire() + if not self._clean: + self._clean = True + try: self.stdin.close() + except: pass + try: self.stdout.close() + except: pass + try: self.stderr.close() + except: pass + self._clean_lock.release() def cmd_str(self): return " ".join([str(s) for s in self.cmd]) @@ -289,7 +242,7 @@ class Broker(Popen): while (os.path.exists(self.log)): self.log = "%s-%d.log" % (self.name, i) i += 1 - + def get_log(self): return os.path.abspath(self.log) @@ -319,10 +272,10 @@ class Broker(Popen): cmd += ["--log-to-file", self.log] cmd += ["--log-to-stderr=no"] if log_level != None: - cmd += ["--log-enable=%s" % log_level] + cmd += ["--log-enable=%s" % log_level] self.datadir = self.name cmd += ["--data-dir", self.datadir] - Popen.__init__(self, cmd, expect, drain=False) + Popen.__init__(self, cmd, expect, stdout=PIPE) test.cleanup_stop(self) self._host = "127.0.0.1" log.debug("Started broker %s (%s, %s)" % (self.name, self.pname, self.log)) @@ -362,7 +315,7 @@ class Broker(Popen): s = c.session(str(qpid.datatypes.uuid4())) s.queue_declare(queue=queue) c.close() - + def _prep_sender(self, queue, durable, xprops): s = queue + "; {create:always, node:{durable:" + str(durable) if xprops != None: s += ", x-declare:{" + xprops + "}" @@ -406,13 +359,14 @@ class Broker(Popen): def log_ready(self): """Return true if the log file exists and contains a broker ready message""" - if self._log_ready: return True - self._log_ready = find_in_file("notice Broker running", self.log) + if not self._log_ready: + self._log_ready = find_in_file("notice Broker running", self.log) + return self._log_ready def ready(self, **kwargs): """Wait till broker is ready to serve clients""" # First make sure the broker is listening by checking the log. - if not retry(self.log_ready, timeout=30): + if not retry(self.log_ready, timeout=60): raise Exception( "Timed out waiting for broker %s%s"%(self.name, error_line(self.log,5))) # Create a connection and a session. For a cluster broker this will @@ -421,17 +375,19 @@ class Broker(Popen): c = self.connect(**kwargs) try: c.session() finally: c.close() - except: raise RethrownException( - "Broker %s failed ready test%s"%(self.name,error_line(self.log, 5))) + except Exception,e: raise RethrownException( + "Broker %s not responding: (%s)%s"%(self.name,e,error_line(self.log, 5))) def store_state(self): - uuids = open(os.path.join(self.datadir, "cluster", "store.status")).readlines() + f = open(os.path.join(self.datadir, "cluster", "store.status")) + try: uuids = f.readlines() + finally: f.close() null_uuid="00000000-0000-0000-0000-000000000000\n" if len(uuids) < 2: return "unknown" # we looked while the file was being updated. if uuids[0] == null_uuid: return "empty" if uuids[1] == null_uuid: return "dirty" return "clean" - + class Cluster: """A cluster of brokers in a test.""" @@ -495,7 +451,7 @@ class BrokerTest(TestCase): rootdir = os.getcwd() def configure(self, config): self.config=config - + def setUp(self): outdir = self.config.defines.get("OUTDIR") or "brokertest.tmp" self.dir = os.path.join(self.rootdir, outdir, self.id()) @@ -516,10 +472,10 @@ class BrokerTest(TestCase): """Call thing.stop at end of test""" self.stopem.append(stopable) - def popen(self, cmd, expect=EXPECT_EXIT_OK, drain=True): + def popen(self, cmd, expect=EXPECT_EXIT_OK, stdin=None, stdout=FILE, stderr=FILE): """Start a process that will be killed at end of test, in the test dir.""" os.chdir(self.dir) - p = Popen(cmd, expect, drain) + p = Popen(cmd, expect, stdin=stdin, stdout=stdout, stderr=stderr) self.cleanup_stop(p) return p @@ -570,7 +526,7 @@ class StoppableThread(Thread): self.stopped = True self.join() if self.error: raise self.error - + class NumberedSender(Thread): """ Thread to run a sender client and send numbered messages until stopped. @@ -589,7 +545,8 @@ class NumberedSender(Thread): "--failover-updates", "--content-stdin" ], - expect=EXPECT_RUNNING) + expect=EXPECT_RUNNING, + stdin=PIPE) self.condition = Condition() self.max = max_depth self.received = 0 @@ -629,7 +586,7 @@ class NumberedSender(Thread): self.join() self.write_message(-1) # end-of-messages marker. if self.error: raise self.error - + class NumberedReceiver(Thread): """ Thread to run a receiver client and verify it receives @@ -649,14 +606,14 @@ class NumberedReceiver(Thread): "--forever" ], expect=EXPECT_RUNNING, - drain=False) + stdout=PIPE) self.lock = Lock() self.error = None self.sender = sender def read_message(self): return int(self.receiver.stdout.readline()) - + def run(self): try: self.received = 0 @@ -688,7 +645,7 @@ class ErrorGenerator(StoppableThread): self.broker=broker broker.test.cleanup_stop(self) self.start() - + def run(self): c = self.broker.connect_old() try: diff --git a/qpid/cpp/src/tests/cli_tests.py b/qpid/cpp/src/tests/cli_tests.py index deef03279d..6c75927461 100755 --- a/qpid/cpp/src/tests/cli_tests.py +++ b/qpid/cpp/src/tests/cli_tests.py @@ -365,6 +365,26 @@ class CliTests(TestBase010): self.assertEqual(queue._altExchange_.name, altName) self.assertEqual(found, True) + def test_qpid_config_list_queues_arguments(self): + """ + Test to verify that when the type of a policy limit is + actually a string (though still a valid value), it does not + upset qpid-config + """ + self.startQmf(); + qmf = self.qmf + + names = ["queue_capacity%s" % (i) for i in range(1, 6)] + for name in names: + self.session.queue_declare(queue=name, exclusive=True, + arguments={'qpid.max_count' : str(i), 'qpid.max_size': '100'}) + + output = os.popen(self.qpid_config_command(" queues")).readlines() + queues = [line.split()[0] for line in output[2:len(output)]] #ignore first two lines (header) + + for name in names: + assert name in queues, "%s not in %s" % (name, queues) + def test_qpid_route(self): self.startQmf(); qmf = self.qmf @@ -405,7 +425,7 @@ class CliTests(TestBase010): qmf = self.qmf ret = self.qpid_route_api("dynamic add " - + " --sasl-mechanism PLAIN " + + " --client-sasl-mechanism PLAIN " + "guest/guest@localhost:"+str(self.broker.port) + " " + str(self.remote_host())+":"+str(self.remote_port()) + " " +"amq.direct") @@ -424,7 +444,7 @@ class CliTests(TestBase010): qmf = self.qmf ret = self.qpid_route_api("dynamic add " - + " --sasl-mechanism PLAIN " + + " --client-sasl-mechanism PLAIN " + "localhost:"+str(self.broker.port) + " " + str(self.remote_host())+":"+str(self.remote_port()) + " " +"amq.direct") diff --git a/qpid/cpp/src/tests/cluster_test_logs.py b/qpid/cpp/src/tests/cluster_test_logs.py index 1fa9014d11..9f7d1e2f6c 100755 --- a/qpid/cpp/src/tests/cluster_test_logs.py +++ b/qpid/cpp/src/tests/cluster_test_logs.py @@ -60,9 +60,11 @@ def filter_log(log): 'task overran', 'warning CLOSING .* unsent data', 'Inter-broker link ', - 'Running in a cluster, marking store' + 'Running in a cluster, marking store', + 'debug Sending keepalive signal to watchdog', # Watchdog timer thread + 'last broker standing joined by 1 replicas, updating queue policies.', + 'Connection .* timed out: closing' # heartbeat connection close ]) - skip_re = re.compile(skip) # Regex to match a UUID uuid='\w\w\w\w\w\w\w\w-\w\w\w\w-\w\w\w\w-\w\w\w\w-\w\w\w\w\w\w\w\w\w\w\w\w' # Substitutions to remove expected differences @@ -80,6 +82,13 @@ def filter_log(log): (r' map={.*_object_name:([^,}]*)[,}].*', r' \1'), # V2 map - just keep name (r'\d+-\d+-\d+--\d+', 'X-X-X--X'), # V1 Object IDs ] + # Substitutions to mask known issue: durable test shows inconsistent "changed stats for com.redhat.rhm.store:journal" messages. + skip += '|Changed V[12] statistics com.redhat.rhm.store:journal' + subs += [(r'to=console.obj.1.0.com.redhat.rhm.store.journal props=\d+ stats=\d+', + 'to=console.obj.1.0.com.redhat.rhm.store.journal props=NN stats=NN')] + + skip_re = re.compile(skip) + subs = [(re.compile(pattern), subst) for pattern, subst in subs] for l in open(log): if skip_re.search(l): continue for pattern,subst in subs: l = re.sub(pattern,subst,l) diff --git a/qpid/cpp/src/tests/cluster_tests.py b/qpid/cpp/src/tests/cluster_tests.py index 3e13a3ce8a..42f3ae3d25 100755 --- a/qpid/cpp/src/tests/cluster_tests.py +++ b/qpid/cpp/src/tests/cluster_tests.py @@ -22,7 +22,7 @@ import os, signal, sys, time, imp, re, subprocess, glob, cluster_test_logs from qpid import datatypes, messaging from brokertest import * from qpid.harness import Skipped -from qpid.messaging import Message, Empty +from qpid.messaging import Message, Empty, Disposition, REJECTED from threading import Thread, Lock, Condition from logging import getLogger from itertools import chain @@ -246,25 +246,6 @@ acl allow all all session1 = cluster[1].connect().session() for q in queues: self.assert_browse(session1, "q1", ["foo"]) - def test_dr_no_message(self): - """Regression test for https://bugzilla.redhat.com/show_bug.cgi?id=655141 - Joining broker crashes with 'error deliveryRecord no update message' - """ - - cluster = self.cluster(1) - session0 = cluster[0].connect().session() - s = session0.sender("q1;{create:always}") - s.send(Message("a", ttl=0.05), sync=False) - s.send(Message("b", ttl=0.05), sync=False) - r1 = session0.receiver("q1") - self.assertEqual("a", r1.fetch(timeout=0).content) - r2 = session0.receiver("q1;{mode:browse}") - self.assertEqual("b", r2.fetch(timeout=0).content) - # Leave messages un-acknowledged, let the expire, then start new broker. - time.sleep(.1) - cluster.start() - self.assertRaises(Empty, cluster[1].connect().session().receiver("q1").fetch,0) - def test_route_update(self): """Regression test for https://issues.apache.org/jira/browse/QPID-2982 Links and bridges associated with routes were not replicated on update. @@ -304,6 +285,36 @@ acl allow all all # Verify logs are consistent cluster_test_logs.verify_logs() + def test_redelivered(self): + """Verify that redelivered flag is set correctly on replayed messages""" + cluster = self.cluster(2, expect=EXPECT_EXIT_FAIL) + url = "amqp:tcp:%s,tcp:%s" % (cluster[0].host_port(), cluster[1].host_port()) + queue = "my-queue" + cluster[0].declare_queue(queue) + self.sender = self.popen( + ["qpid-send", + "--broker", url, + "--address", queue, + "--sequence=true", + "--send-eos=1", + "--messages=100000", + "--connection-options={reconnect:true}" + ]) + self.receiver = self.popen( + ["qpid-receive", + "--broker", url, + "--address", queue, + "--ignore-duplicates", + "--check-redelivered", + "--connection-options={reconnect:true}", + "--forever" + ]) + time.sleep(1)#give sender enough time to have some messages to replay + cluster[0].kill() + self.sender.wait() + self.receiver.wait() + cluster[1].kill() + class BlockedSend(Thread): """Send a message, send is expected to block. Verify that it does block (for a given timeout), then allow @@ -411,6 +422,33 @@ acl allow all all return cluster[1] self.queue_flowlimit_test(Brokers()) + def test_alternate_exchange_update(self): + """Verify that alternate-exchange on exchanges and queues is propagated to new members of a cluster. """ + cluster = self.cluster(1) + s0 = cluster[0].connect().session() + # create alt queue bound to amq.fanout exchange, will be destination for alternate exchanges + self.evaluate_address(s0, "alt;{create:always,node:{x-bindings:[{exchange:'amq.fanout',queue:alt}]}}") + # create direct exchange ex with alternate-exchange amq.fanout and no queues bound + self.evaluate_address(s0, "ex;{create:always,node:{type:topic, x-declare:{type:'direct', alternate-exchange:'amq.fanout'}}}") + # create queue q with alternate-exchange amq.fanout + self.evaluate_address(s0, "q;{create:always,node:{type:queue, x-declare:{alternate-exchange:'amq.fanout'}}}") + + def verify(broker): + s = broker.connect().session() + # Verify unmatched message goes to ex's alternate. + s.sender("ex").send("foo") + self.assertEqual("foo", s.receiver("alt").fetch(timeout=0).content) + # Verify rejected message goes to q's alternate. + s.sender("q").send("bar") + msg = s.receiver("q").fetch(timeout=0) + self.assertEqual("bar", msg.content) + s.acknowledge(msg, Disposition(REJECTED)) # Reject the message + self.assertEqual("bar", s.receiver("alt").fetch(timeout=0).content) + + verify(cluster[0]) + cluster.start() + verify(cluster[1]) + class LongTests(BrokerTest): """Tests that can run for a long time if -DDURATION=<minutes> is set""" def duration(self): @@ -469,24 +507,24 @@ class LongTests(BrokerTest): if self.stopped: break self.process = self.broker.test.popen( self.cmd, expect=EXPECT_UNKNOWN) - finally: self.lock.release() - try: exit = self.process.wait() + finally: + self.lock.release() + try: + exit = self.process.wait() except OSError, e: - # Seems to be a race in wait(), it throws - # "no such process" during test shutdown. - # Doesn't indicate a test error, ignore. - return + # Process may already have been killed by self.stop() + break except Exception, e: self.process.unexpected( "client of %s: %s"%(self.broker.name, e)) self.lock.acquire() try: - # Quit and ignore errors if stopped or expecting failure. if self.stopped: break if exit != 0: self.process.unexpected( "client of %s exit code %s"%(self.broker.name, exit)) - finally: self.lock.release() + finally: + self.lock.release() except Exception, e: self.error = RethrownException("Error in ClientLoop.run") @@ -517,8 +555,10 @@ class LongTests(BrokerTest): """Start ordinary clients for a broker.""" cmds=[ ["qpid-tool", "localhost:%s"%(broker.port())], - ["qpid-perftest", "--count", 50000, + ["qpid-perftest", "--count=5000", "--durable=yes", "--base-name", str(qpid.datatypes.uuid4()), "--port", broker.port()], + ["qpid-txtest", "--queue-base-name", "tx-%s"%str(qpid.datatypes.uuid4()), + "--port", broker.port()], ["qpid-queue-stats", "-a", "localhost:%s" %(broker.port())], ["testagent", "localhost", str(broker.port())] ] clients.append([ClientLoop(broker, cmd) for cmd in cmds]) @@ -529,7 +569,8 @@ class LongTests(BrokerTest): mclients.append(ClientLoop(broker, cmd)) endtime = time.time() + self.duration() - runtime = self.duration() / 4 # First run is longer, use quarter of duration. + # For long duration, first run is a quarter of the duration. + runtime = max(5, self.duration() / 4.0) alive = 0 # First live cluster member for i in range(len(cluster)): start_clients(cluster[i]) start_mclients(cluster[alive]) @@ -555,14 +596,13 @@ class LongTests(BrokerTest): start_mclients(cluster[alive]) for c in chain(mclients, *clients): c.stop() - # Verify that logs are consistent cluster_test_logs.verify_logs() def test_management_qmf2(self): self.test_management(args=["--mgmt-qmf2=yes"]) - def test_connect_consistent(self): # FIXME aconway 2011-01-18: + def test_connect_consistent(self): args=["--mgmt-pub-interval=1","--log-enable=trace+:management"] cluster = self.cluster(2, args=args) end = time.time() + self.duration() diff --git a/qpid/cpp/src/tests/python_tests b/qpid/cpp/src/tests/python_tests index e367004a71..0216b5ca7b 100755 --- a/qpid/cpp/src/tests/python_tests +++ b/qpid/cpp/src/tests/python_tests @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # # Licensed to the Apache Software Foundation (ASF) under one diff --git a/qpid/cpp/src/tests/qpid-perftest.cpp b/qpid/cpp/src/tests/qpid-perftest.cpp index 4d7b563c8c..8a5cf05775 100644 --- a/qpid/cpp/src/tests/qpid-perftest.cpp +++ b/qpid/cpp/src/tests/qpid-perftest.cpp @@ -423,8 +423,10 @@ struct Controller : public Client { process(opts.totalSubs, subDone, fqn("sub_done"), boost::ref(subRates)); AbsTime end=now(); - double time=secs(start, end); + if (time <= 0.0) { + throw Exception("ERROR: Test completed in zero seconds. Try again with a larger message count."); + } double txrate=opts.transfers/time; double mbytes=(txrate*opts.size)/(1024*1024); @@ -543,6 +545,9 @@ struct PublishThread : public Client { if (opts.confirm) session.sync(); AbsTime end=now(); double time=secs(start,end); + if (time <= 0.0) { + throw Exception("ERROR: Test completed in zero seconds. Try again with a larger message count."); + } // Send result to controller. Message report(lexical_cast<string>(opts.count/time), fqn("pub_done")); diff --git a/qpid/cpp/src/tests/qpid-receive.cpp b/qpid/cpp/src/tests/qpid-receive.cpp index 3189a13c6e..5a85da4fd2 100644 --- a/qpid/cpp/src/tests/qpid-receive.cpp +++ b/qpid/cpp/src/tests/qpid-receive.cpp @@ -53,6 +53,7 @@ struct Options : public qpid::Options bool forever; uint messages; bool ignoreDuplicates; + bool checkRedelivered; uint capacity; uint ackFrequency; uint tx; @@ -75,6 +76,7 @@ struct Options : public qpid::Options forever(false), messages(0), ignoreDuplicates(false), + checkRedelivered(false), capacity(1000), ackFrequency(100), tx(0), @@ -96,6 +98,7 @@ struct Options : public qpid::Options ("forever,f", qpid::optValue(forever), "ignore timeout and wait forever") ("messages,m", qpid::optValue(messages, "N"), "Number of messages to receive; 0 means receive indefinitely") ("ignore-duplicates", qpid::optValue(ignoreDuplicates), "Detect and ignore duplicates (by checking 'sn' header)") + ("check-redelivered", qpid::optValue(checkRedelivered), "Fails with exception if a duplicate is not marked as redelivered (only relevant when ignore-duplicates is selected)") ("capacity", qpid::optValue(capacity, "N"), "Pre-fetch window (0 implies no pre-fetch)") ("ack-frequency", qpid::optValue(ackFrequency, "N"), "Ack frequency (0 implies none of the messages will get accepted)") ("tx", qpid::optValue(tx, "N"), "batch size for transactions (0 implies transaction are not used)") @@ -216,6 +219,8 @@ int main(int argc, char ** argv) std::cout << msg.getContent() << std::endl;//TODO: handle map or list messages if (opts.messages && count >= opts.messages) done = true; } + } else if (opts.checkRedelivered && !msg.getRedelivered()) { + throw qpid::Exception("duplicate sequence number received, message not marked as redelivered!"); } if (opts.tx && (count % opts.tx == 0)) { if (opts.rollbackFrequency && (++txCount % opts.rollbackFrequency == 0)) { diff --git a/qpid/cpp/src/tests/qrsh.cpp b/qpid/cpp/src/tests/qrsh.cpp deleted file mode 100644 index 0cb52b6b05..0000000000 --- a/qpid/cpp/src/tests/qrsh.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include <qpid/client/Connection.h> -#include <qpid/client/Session.h> -#include <qpid/client/AsyncSession.h> -#include <qpid/client/Message.h> -#include <qpid/client/MessageListener.h> -#include <qpid/client/SubscriptionManager.h> - -#include <stdio.h> -#include <cstdlib> -#include <iostream> - -#include <sstream> - -using namespace qpid::client; -using namespace qpid::framing; - -using namespace std; - -namespace qpid { -namespace tests { - -class ResponseListener : public MessageListener -{ - public : - - int exitCode; - - ResponseListener ( SubscriptionManager & subscriptions ) - : exitCode(-1), - subscriptions ( subscriptions ) - { - } - - virtual void - received ( Message & message ) - { - char first_word[1000]; - sscanf ( message.getData().c_str(), "%s", first_word ); - - if ( ! strcmp ( first_word, "wait_response" ) ) - { - // If we receive a message here, parse out the exit code. - sscanf ( message.getData().c_str(), "%*s%d", & exitCode ); - subscriptions.cancel(message.getDestination()); - } - else - if ( ! strcmp ( first_word, "get_response" ) ) - { - // The remainder of the message is the file we requested. - fprintf ( stdout, - "%s", - message.getData().c_str() + strlen("get_response" ) - ); - subscriptions.cancel(message.getDestination()); - } - } - - - private : - - SubscriptionManager & subscriptions; -}; - -}} // namespace qpid::tests - -using namespace qpid::tests; - -/* - * argv[1] host - * argv[2] port - * argv[3] server name - * argv[4] command name - * argv[5..N] args to the command - */ -int -main ( int argc, char ** argv ) -{ - const char* host = argv[1]; - int port = atoi(argv[2]); - - - Connection connection; - - try - { - connection.open ( host, port ); - Session session = connection.newSession ( ); - - // Make a queue and bind it to fanout. - string myQueue = session.getId().getName(); - - session.queueDeclare ( arg::queue=myQueue, - arg::exclusive=true, - arg::autoDelete=true - ); - - session.exchangeBind ( arg::exchange="amq.fanout", - arg::queue=myQueue, - arg::bindingKey="my-key" - ); - - // Get ready to listen for the wait-response. - // or maybe a get-response. - // ( Although this may not be one of those types - // of command, get ready anyway. - SubscriptionManager subscriptions ( session ); - ResponseListener responseListener ( subscriptions ); - subscriptions.subscribe ( responseListener, myQueue ); - - bool response_command = false; - if(! strcmp("exec_wait", argv[4] )) - response_command = true; - else - if(! strcmp("exited", argv[4] )) - response_command = true; - else - if(! strcmp("get", argv[4] )) - response_command = true; - - // Send the payload message. - // Skip "qrsh host_name port" - Message message; - stringstream ss; - for ( int i = 3; i < argc; ++ i ) - ss << argv[i] << ' '; - - message.setData ( ss.str() ); - - session.messageTransfer(arg::content=message, - arg::destination="amq.fanout"); - - if ( response_command ) - subscriptions.run(); - - session.close(); - connection.close(); - return responseListener.exitCode; - } - catch ( exception const & e) - { - cerr << e.what() << endl; - } - - return 1; -} - - - diff --git a/qpid/cpp/src/tests/qrsh_run.cpp b/qpid/cpp/src/tests/qrsh_run.cpp deleted file mode 100644 index cfdd0cef80..0000000000 --- a/qpid/cpp/src/tests/qrsh_run.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include <iostream> -#include <sstream> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <fcntl.h> -#include <errno.h> - -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/wait.h> - - -using namespace std; - - - -int -main ( int argc, char ** argv ) -{ - int exit_code = -1; - int fd[2]; - int my_pid = getpid(); - int child_pid; - - pipe(fd); - - char const * root_dir = argv[1]; // This arg is prepended by qrsh_server. - char const * child_name = argv[2]; // This arg comes from qrsh. - char const * child_path = argv[3]; // This arg comes from qrsh. - - // This is the problem.. - fprintf ( stderr, "MDEBUG qrsh_run: root_dir: |%s|\n", root_dir ); - fprintf ( stderr, "MDEBUG qrsh_run: child_name: |%s|\n", child_name ); - fprintf ( stderr, "MDEBUG qrsh_run: child_path: |%s|\n", child_path ); - - /* - * A named child is one for whom we will create a directory and - * store information. There are some magic names that are not - * real symbolic names -- but are instead the names of actions. - */ - - bool named_child = true; - - if ( ! strcmp ( child_name, "exec" ) ) - named_child = false; - else - if ( ! strcmp ( child_name, "exec_wait" ) ) - named_child = false; - else - if ( ! strcmp ( child_name, "exited" ) ) - named_child = false; - else - named_child = true; - - stringstream child_dir_name; - - if ( named_child ) - { - child_dir_name << root_dir - << '/' - << child_name; - - /* - * Make the child directory before forking, or there is - * a race in which the child might be trying to make its - * stdout and stderr files while we are tring to make - * the directory. - */ - if ( -1 == mkdir ( child_dir_name.str().c_str(), 0777 ) ) - { - fprintf ( stderr, - "qrsh_run error: Can't mkdir |%s|\n", - child_dir_name.str().c_str() - ); - exit ( 1 ); - } - - } - else - /* - * If this is an 'exited' command that means we are - * waiting for a pre-existing child. - */ - if ( ! strcmp ( child_name, "exited" ) ) - { - int wait_pid = atoi(child_path); - - // Find the child's symbolic name. - stringstream pid_to_name_file_name; - pid_to_name_file_name << root_dir - << '/' - << wait_pid; - FILE * fp = fopen ( pid_to_name_file_name.str().c_str(), "r" ); - if (! fp) - { - fprintf ( stderr, - "qrsh_run %d error: Can't open pid2name file |%s|.\n", - my_pid, - pid_to_name_file_name.str().c_str() - ); - exit(1); - } - char symbolic_name[1000]; - strcpy ( symbolic_name, "qrsh_no_name" ); - fscanf ( fp, "%s", symbolic_name ); - fclose ( fp ); - - // Make the name of the child's exit code file. - stringstream exit_code_file_name; - exit_code_file_name << root_dir - << '/' - << symbolic_name - << "/exit_code"; - - struct stat stat_buf; - int file_does_not_exist = stat ( exit_code_file_name.str().c_str(), & stat_buf ); - - /* - * If the result of stat is zero, the file exists, which means that - * the command has exited. The question we are being asked here is - * "has it exited yet?" - */ - if ( ! file_does_not_exist ) - return 1; - else - if ( errno == ENOENT ) - return 0; - else - return 2 ; - } - - - // We are not waiting on a pre-wxiting child: we have a - // new child to create. - - child_pid = fork(); - - if ( child_pid == 0 ) - { - // This code is executed in the child process. - - // If it's a *named* child, then redirect its stdout and stderr. - if ( named_child ) - { - stringstream stdout_path, - stderr_path; - - // Redirect the child's stdout. ----------------- - stdout_path << root_dir - << '/' - << child_name - << '/' - << "stdout"; - - int redirected_stdout = open ( stdout_path.str().c_str(), - O_WRONLY|O_CREAT|O_TRUNC, - S_IRWXU|S_IRWXG|S_IRWXO - ); - if ( redirected_stdout < 0 ) - { - perror ( "qrsh_run: error opening redirected_stdout: " ); - fprintf ( stderr, "stdout path: |%s|\n", stdout_path.str().c_str() ); - exit ( 1 ); - } - if ( -1 == dup2 ( redirected_stdout, 1 ) ) - { - perror ( "qrsh_run: dup2 (stdout) error: " ); - exit(1); - } - - // Redirect the child's stderr. ----------------- - stderr_path << root_dir - << '/' - << child_name - << '/' - << "stderr"; - - int redirected_stderr = open ( stderr_path.str().c_str(), - O_WRONLY|O_CREAT|O_TRUNC, - S_IRWXU|S_IRWXG|S_IRWXO - ); - if ( redirected_stderr < 0 ) - { - perror ( "qrsh_run: error opening redirected_stderr: " ); - fprintf ( stderr, "stderr path: |%s|\n", stderr_path.str().c_str() ); - exit ( 1 ); - } - if(-1 == dup2 ( redirected_stderr, 2 ) ) - { - perror ( "qrsh_run: dup2 (stderr) error: " ); - exit(1); - } - } - - fprintf ( stderr, "MDEBUG ------------- qrsh_run argv -------------\n" ); - for ( int i = 0; i < argc; ++ i ) - fprintf ( stderr, "MDEBUG argv[%d] : |%s|\n", i, argv[i] ); - - execv ( child_path, argv + 2 ); - perror ( "qrsh_run: execv error: " ); - fprintf ( stderr, "on path |%s|\n", child_path ); - exit ( 1 ); - } - else - { - // This code is executed in the parent process. - - if ( named_child ) - { - // Write the name-to-pid mapping. - stringstream pid_file_name; - pid_file_name << child_dir_name.str() - << "/pid"; - - FILE * fp; - if ( ! (fp = fopen ( pid_file_name.str().c_str(), "w") ) ) - { - fprintf ( stderr, - "qrsh_run %d error: Can't open file |%s|\n", - my_pid, - pid_file_name.str().c_str() - ); - exit(1); - } - fprintf ( fp, "%d\n", child_pid ); - fclose ( fp ); - - - // Write the pid-to-name mapping. - stringstream name_to_pid_file_name; - name_to_pid_file_name << root_dir - << '/' - << child_pid; - if(! (fp = fopen ( name_to_pid_file_name.str().c_str(), "w"))) - { - fprintf ( stderr, - "qrsh_run %d error: Can't open file |%s|\n", - my_pid, - name_to_pid_file_name.str().c_str() - ); - exit(1); - } - fprintf ( fp, "%s\n", child_name ); - fclose(fp); - } - - pid_t awaited_pid; - while ( 0 == (awaited_pid = waitpid ( child_pid, & exit_code, WNOHANG)) ) - { - fprintf ( stderr, - "qrsh_run %d info: parent: waiting for child %d...\n", - my_pid, - child_pid - ); - sleep(1); - } - - if ( -1 == awaited_pid ) - { - fprintf ( stderr, "qrsh_run error awaiting child!\n" ); - exit ( 1 ); - } - - /* - * Write the exit code. - */ - exit_code >>= 8; - - if ( named_child ) - { - if ( child_pid == awaited_pid ) - { - stringstream exit_code_file_name; - exit_code_file_name << child_dir_name.str() - << "/exit_code"; - - FILE * fp; - if ( ! (fp = fopen ( exit_code_file_name.str().c_str(), "w") ) ) - { - fprintf ( stderr, - "qrsh_run error: Can't open file |%s|\n", - exit_code_file_name.str().c_str() - ); - exit(1); - } - fprintf ( fp, "%d\n", exit_code ); - fclose ( fp ); - } - } - } - - fprintf ( stderr, "MDEBUG qrsh_run returning exit code %d\n", exit_code ); - return exit_code; -} - - - - diff --git a/qpid/cpp/src/tests/qrsh_server.cpp b/qpid/cpp/src/tests/qrsh_server.cpp deleted file mode 100644 index 782f1e6c7c..0000000000 --- a/qpid/cpp/src/tests/qrsh_server.cpp +++ /dev/null @@ -1,1068 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include <stdio.h> -#include <set> -#include <string> -#include <sstream> -#include <unistd.h> -#include <cstdlib> -#include <iostream> -#include <map> -#include <dirent.h> - -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/wait.h> - -#include <qpid/client/Connection.h> -#include <qpid/client/Session.h> -#include <qpid/client/AsyncSession.h> -#include <qpid/client/Message.h> -#include <qpid/client/MessageListener.h> -#include <qpid/client/SubscriptionManager.h> - - -using namespace qpid::client; -using namespace qpid::framing; -using namespace std; - - -namespace qpid { -namespace tests { - -int -mrand ( int max_desired_val ) -{ - double zero_to_one = (double) rand() / (double) RAND_MAX; - return (int) (zero_to_one * (double) max_desired_val); -} - - - -char * -file2str ( char const * file_name ) -{ - FILE * fp = fopen ( file_name, "r" ); - if(! fp) - { - fprintf ( stderr, "file2str error: can't open file |%s|.\n", file_name ); - return 0; - } - - fseek ( fp, 0, SEEK_END ); - size_t file_len = (size_t) ftell ( fp ); - rewind ( fp ); - char * content = (char *) malloc ( file_len + 1 ); - - if ( ! content ) - { - fprintf ( stderr, - "file2str error: can't malloc %d bytes.\n", - (int)file_len - ); - return 0; - } - - size_t items_read = fread ( content, file_len, 1, fp ); - - if ( 1 != items_read ) - { - fprintf ( stderr, "file2str error: read failed.\n" ); - free ( content ); - return 0; - } - - fclose ( fp ); - content[file_len] = 0; - - return content; -} - - - - - -class QrshServer : public MessageListener -{ - public: - - QrshServer ( SubscriptionManager & subscriptions, - char const * name, - char const * qrsh_run_path, - char const * host, - int port - ); - - virtual void received ( Message & message); - - - private: - - set<string> all_server_names; - - stringstream data_dir; - - SubscriptionManager & subscriptions; - - // Is this message addressed to me? - bool myMessage ( Message const & message ); - - /* ---------------------------------------------- - * Special Commands - * These are commands that the qrsh_server executes - * directly, rather than through a child process - * instance of qrsh_run. - */ - void runCommand ( Message const & message ); - void execute ( Message const & message ); - void wait ( Message const & message ); - void exited ( Message const & message ); - void get ( Message const & message ); - void rememberIntroduction ( Message const & message ); - void getStraw ( Message const & message ); - void addAlias ( Message const & message ); - - void start ( ); - void sayHello ( ); - void sayName ( ); - // end Special Commands ------------------------ - - - void saveCommand ( Message const & message ); - - void send ( string const & content ); - - void drawStraws ( ); - void getNames ( ); - void runSavedCommand ( ); - - char ** getArgs ( char const * s ); - bool isProcessName ( char const * s ); - int string_countWords ( char const * s ); - char const * skipWord ( char const * s ); - - - void string_replaceAll ( string & str, - string & target, - string & replacement - ); - - - string name, - qrsh_run_path, - host; - - vector<string *> aliases; - - int port; - - map < char *, int > abstract_name_map; - - set < string > myFellowBrokers; - - bool saidHello; - - Message savedCommand; - - vector < int > straws; - int myStraw; - -}; - - - -QrshServer::QrshServer ( SubscriptionManager & subs, - char const * name, - char const * qrsh_run_path, - char const * host, - int port - ) - : subscriptions ( subs ), - name ( name ), - qrsh_run_path ( qrsh_run_path ), - host ( host ), - port ( port ), - saidHello ( false ), - myStraw ( 0 ) -{ - data_dir << "/tmp/qrsh_" - << getpid(); - - if(mkdir ( data_dir.str().c_str(), 0777 ) ) - { - fprintf ( stderr, - "QrshServer::QrshServer error: can't mkdir |%s|\n", - data_dir.str().c_str() - ); - exit ( 1 ); - } -} - - - -void -QrshServer::saveCommand ( Message const & message ) -{ - savedCommand = message; -} - - - -void -QrshServer::runSavedCommand ( ) -{ - runCommand ( savedCommand ); -} - - - -void -QrshServer::start ( ) -{ - stringstream announcement_data; - announcement_data << "hello_my_name_is " - << name; - - send ( announcement_data.str() ); - - saidHello = true; -} - - - - -void -QrshServer::send ( string const & content ) -{ - try - { - Message message; - message.setData ( content ); - - Connection connection; - connection.open ( host, port ); - Session session = connection.newSession ( ); - session.messageTransfer ( arg::content = message, - arg::destination = "amq.fanout" - ); - session.close(); - connection.close(); - } - catch ( exception const & e ) - { - fprintf ( stderr, "QrshServer::send error: |%s|\n", e.what() ); - } -} - - - - -void -QrshServer::sayHello ( ) -{ - if ( saidHello ) - return; - - stringstream ss; - - ss << "hello_my_name_is " - << name; - - send ( ss.str() ); - saidHello = true; -} - - - -void -QrshServer::sayName ( ) -{ - fprintf ( stderr, "My name is: |%s|\n", name.c_str() ); -} - - - - -void -QrshServer::drawStraws ( ) -{ - myStraw = mrand ( 1000000000 ); - stringstream ss; - ss << "straw " - << name - << ' ' - << myStraw; - send ( ss.str() ); -} - - - -void -QrshServer::getStraw ( Message const & message ) -{ - int straw; - - char brokerName[1000]; - sscanf ( message.getData().c_str(), "%*s%s", brokerName ); - - if ( ! strcmp ( brokerName, name.c_str() ) ) - return; - - sscanf ( message.getData().c_str(), "%*s%*s%d", & straw ); - straws.push_back ( straw ); - - bool i_win = true; - int ties = 0; - - if ( straws.size() >= myFellowBrokers.size() ) - { - // All votes are in! Let's see if I win! - for ( unsigned int i = 0; i < straws.size(); ++ i ) - { - if ( straws[i] == myStraw ) - ++ ties; - else - if ( straws[i] > myStraw ) - { - i_win = false; - break; - } - } - - if ( i_win && (ties <= 0) ) - { - myStraw = 0; - straws.clear(); - runSavedCommand ( ); - } - else - if ( i_win && (ties > 0) ) - { - fprintf ( stderr, "MDEBUG oh no! drawStraws error: server %s tied with straw %d!\n", name.c_str(), straw ); - } - } -} - - - - -/* - * "APB" command (all-points-bullitens (commands that are not addressed - * specifically to any server)) are handled directly, here. - * Because if I return simply "true", the normal command processing code - * will misinterpret the command. - */ -bool -QrshServer::myMessage ( Message const & message ) -{ - int const maxlen = 100; - char head[maxlen]; - char first_word [ maxlen + 1 ]; - strncpy ( head, message.getData().c_str(), maxlen ); - sscanf ( head, "%s", first_word ); - - if ( ! strcmp ( name.c_str(), first_word ) ) - { - return true; - } - else - { - // Is the given name one of my aliases? - char possibleAlias[1000]; - if(1 == sscanf ( message.getData().c_str(), "%s", possibleAlias )) - { - for ( unsigned int i = 0; i < aliases.size(); ++ i ) - { - - if ( ! strcmp ( possibleAlias, aliases[i]->c_str() )) - { - return true; - } - } - } - } - - if ( ! strcmp ( first_word, "hello_my_name_is" ) ) - { - rememberIntroduction ( message ); - sayHello ( ); - return false; - } - else - if ( ! strcmp ( first_word, "straw" ) ) - { - getStraw ( message ); - return false; - } - else - if ( ! strcmp ( first_word, "all" ) ) - { - return true; - } - else - if ( ! strcmp ( first_word, "any" ) ) - { - straws.clear(); - usleep ( 200000 ); - saveCommand ( message ); - drawStraws ( ); - return false; - } - else - return false; -} - - - - -void -QrshServer::rememberIntroduction ( Message const & message ) -{ - char brokerName [ 1000 ]; - sscanf ( message.getData().c_str(), "%*s%s", brokerName ); - - if ( strcmp ( brokerName, name.c_str() ) ) - myFellowBrokers.insert ( string ( brokerName ) ); -} - - - - -void -QrshServer::addAlias ( Message const & message ) -{ - char alias[1000]; - sscanf ( message.getData().c_str(), "%*s%*s%s", alias ); - aliases.push_back ( new string(alias) ); -} - - - - -void -QrshServer::getNames ( ) -{ - abstract_name_map.clear(); - - DIR * dir = opendir ( data_dir.str().c_str() ); - - if ( ! dir ) - { - fprintf ( stderr, - "QrshServer::getNames error: could not open dir |%s|.\n", - data_dir.str().c_str() - ); - return; - } - - struct dirent * file; - while ( (file = readdir ( dir ) ) ) - { - if ( '.' != file->d_name[0] ) - { - stringstream pid_file_name; - pid_file_name << data_dir.str() - << '/' - << file->d_name - << "/pid"; - - int pid = 0; - FILE * fp; - if ( (fp = fopen ( pid_file_name.str().c_str(), "r" ) ) ) - { - fscanf ( fp, "%d", & pid ); - fclose ( fp ); - abstract_name_map.insert(pair<char*, int>(strdup(file->d_name), pid)); - } - else - { - /* - * Fail silently. The non-existence of this file - * is not necessarily an error. - */ - } - } - } - closedir ( dir ); -} - - - -void -QrshServer::string_replaceAll ( string & str, - string & target, - string & replacement - ) -{ - int target_size = target.size(); - int found_pos = 0; - - while ( 0 <= (found_pos = str.find ( target ) ) ) - str.replace ( found_pos, target_size, replacement ); -} - - - - -bool -QrshServer::isProcessName ( char const * str ) -{ - getNames(); - map<char *, int>::iterator it; - for ( it = abstract_name_map.begin(); it != abstract_name_map.end(); ++ it ) - { - if ( ! strcmp ( str, it->first ) ) - return true; - } - - return false; -} - - - - - -int -QrshServer::string_countWords ( char const * s1 ) -{ - int count = 0; - char const * s2 = s1 + 1; - - if ( ! isspace(* s1) ) - { - ++ count; - } - - for ( ; * s2; ++ s1, ++ s2 ) - { - // count space-to-word transitions. - if ( isspace(*s1) && (! isspace(*s2)) ) - ++ count; - } - - return count; -} - - - - -void -QrshServer::execute ( Message const & message ) -{ - // First, gather all the symbolic names we know. - getNames(); - - // Now make a copy of the command, that I can alter. - string command ( message.getData() ); - - - // Replace each occurrence of every abstract name with its pid. - char pid_str[100]; - map<char *, int>::iterator it; - for ( it = abstract_name_map.begin(); it != abstract_name_map.end(); ++ it ) - { - sprintf ( pid_str, "%d", it->second ); - string target ( it->first ), - replacement ( pid_str ); - string_replaceAll ( command, target, replacement ); - } - - - char const * truncated_command = skipWord(skipWord(command.c_str())); - - if ( truncated_command ) - system ( truncated_command ); -} - - - - - -void -QrshServer::get ( Message const & request_message ) -{ - char * file_content; - - /* - * Get the contents of the requested file. - */ - char file_or_process_name[1000]; - sscanf ( request_message.getData().c_str(), "%*s%*s%s", file_or_process_name ); - - if ( isProcessName ( file_or_process_name ) ) - { - stringstream desired_file_name; - desired_file_name << data_dir.str() - << '/' - << file_or_process_name - << '/'; - char requested_output_stream[1000]; - if(1 != sscanf ( request_message.getData().c_str(), - "%*s%*s%*s%s", - requested_output_stream - ) - ) - { - fprintf ( stderr, - "QrshServer::get error: Can't read requested data file name from this message: |%s|\n", - request_message.getData().c_str() - ); - return; - } - desired_file_name << requested_output_stream; - file_content = file2str ( desired_file_name.str().c_str() ); - } - else - { - file_content = file2str ( file_or_process_name ); - } - - stringstream reply_data ; - reply_data << "get_response " - << file_content; - /* - * Send a response-message to the server who is waiting. - */ - send ( reply_data.str() ); -} - - - - - - -void -QrshServer::exited ( Message const & message ) -{ - int exit_code = -1; - - // First, gather all the symbolic names we know. - getNames(); - - // Now make a copy of the command, that I can alter. - string edited_command ( message.getData() ); - - // Replace each occurrence of every abstract name with its pid. - char pid_str[100]; - map<char *, int>::iterator it; - for ( it = abstract_name_map.begin(); it != abstract_name_map.end(); ++ it ) - { - sprintf ( pid_str, "%d", it->second ); - string target ( it->first ), - replacement ( pid_str ); - string_replaceAll ( edited_command, target, replacement ); - } - - // Skip the service name. That is not used by the child. - char const * truncated_command = skipWord(edited_command.c_str()); - - if ( truncated_command ) - { - stringstream ss; - ss << qrsh_run_path - << ' ' - << data_dir.str() - << ' ' - << truncated_command; - - int child_pid; - if ( ! (child_pid = fork() ) ) - { - // This is the child. - - char ** argv = getArgs ( ss.str().c_str() ); - execv ( qrsh_run_path.c_str(), argv ); - - perror ( "qrsh_server: execv error: " ); - exit ( 1 ); - } - else - { - // This is the parent. - pid_t awaited_pid; - while ( 0 == (awaited_pid = waitpid ( child_pid, & exit_code, WNOHANG)) ) - { - fprintf ( stderr, "qrsh_server info: parent: waiting for child...\n" ); - sleep(1); - } - - if ( -1 == awaited_pid ) - { - fprintf ( stderr, "qrsh_server error awaiting child!\n" ); - exit ( 1 ); - } - - exit_code >>= 8; - - stringstream data; - data << "wait_response " - << exit_code; - - send ( data.str() ); - } - } -} - - - - -void -QrshServer::wait ( Message const & message ) -{ - bool pre_existing = false; - if ( 3 == string_countWords ( message.getData().c_str() ) ) - { - // The first word is the name of this service. - // The second word is "exec_wait". - // The third word is the symbolic name of the command to wait for. - // The fact that there are exactly three words means that this - // must be a command that has already been named and started -- - // we just need to find its pid and wait on it. - pre_existing = true; - } - - - int exit_code = -1; - - // First, gather all the symbolic names we know. - getNames(); - - // Now make a copy of the command, that I can alter. - string edited_command ( message.getData() ); - - // Replace each occurrence of every abstract name with its pid. - char pid_str[100]; - map<char *, int>::iterator it; - for ( it = abstract_name_map.begin(); it != abstract_name_map.end(); ++ it ) - { - sprintf ( pid_str, "%d", it->second ); - string target ( it->first ), - replacement ( pid_str ); - string_replaceAll ( edited_command, target, replacement ); - } - - // Skip the service name. That is not used by the child. - char const * truncated_command = skipWord(edited_command.c_str()); - - if ( truncated_command ) - { - stringstream ss; - ss << qrsh_run_path - << ' ' - << data_dir.str() - << ' ' - << truncated_command; - - int child_pid; - if ( ! (child_pid = fork() ) ) - { - // This is the child. - - char ** argv = getArgs ( ss.str().c_str() ); - execv ( qrsh_run_path.c_str(), argv ); - - perror ( "qrsh_server: execv error: " ); - exit ( 1 ); - } - else - { - // This is the parent. - pid_t awaited_pid; - while ( 0 == (awaited_pid = waitpid ( child_pid, & exit_code, WNOHANG)) ) - { - fprintf ( stderr, "qrsh_server info: parent: waiting for child...\n" ); - sleep(1); - } - - if ( -1 == awaited_pid ) - { - fprintf ( stderr, "qrsh_server error awaiting child!\n" ); - exit ( 1 ); - } - } - - exit_code >>= 8; - - stringstream data; - data << "wait_response " - << exit_code; - - send ( data.str() ); - } -} - - - - - -char const * -QrshServer::skipWord ( char const * s ) -{ - if(! (s && *s) ) - return 0; - - // skip past initial white space - while ( isspace(*s) ) - { - ++ s; - if(! *s) - return 0; - } - - // skip past first word - while ( ! isspace(*s) ) - { - ++ s; - if(! *s) - return 0; - } - - return s; -} - - - - - -char ** -QrshServer::getArgs ( char const * str ) -{ - char const * s = str; - - char ** argv = 0; - vector<int> start_positions, - lengths; - - int pos = 0; - int arg_len = 0; - - int n_args = 0; - while ( 1 ) - { - // advance over whitespace. - while ( isspace ( *s ) ) - { - ++ s; ++ pos; - if(! *s) - { - goto done; - } - } - - ++ n_args; - start_positions.push_back ( pos ); - arg_len = 0; - - // advance over non-whitespace. - while ( ! isspace ( *s ) ) - { - ++ s; ++ pos; ++ arg_len; - if(! *s) - { - lengths.push_back ( arg_len ); - arg_len = 0; - goto done; - } - } - - lengths.push_back ( arg_len ); - arg_len = 0; - } - - done: - - if ( arg_len > 0 ) - lengths.push_back ( arg_len ); - - // Alloc the array. - argv = (char **) malloc ( sizeof(char *) * ( n_args + 1 ) ); - argv[n_args] = 0; // mull-term the array. - - for ( int i = 0; i < n_args; ++ i ) - { - argv[i] = ( char *) malloc ( lengths[i] + 1 ); - strncpy ( argv[i], - str + start_positions[i], - lengths[i] - ); - argv[i][lengths[i]] = 0; - } - - return argv; -} - - - -void -QrshServer::runCommand ( Message const & message ) -{ - char const * s = message.getData().c_str(); - - /* - * Skip the first word, which is this server's name. - */ - while ( isspace(*s) ) // go to start of first word. - ++ s; - - while ( ! isspace(*s) ) // go to end of first word. - ++ s; - - while ( isspace(*s) ) // go to start of second word. - ++ s; - - char command_name[1000]; - sscanf ( s, "%s", command_name ); - - if ( ! strcmp ( "get", command_name ) ) - { - get ( message ); - } - else - if ( ! strcmp ( "exited", command_name ) ) - { - exited ( message ); - } - else - if ( ! strcmp ( "exec_wait", command_name ) ) - { - wait ( message ); - } - else - if ( ! strcmp ( "exec", command_name ) ) - { - execute ( message ); - } - else - if ( ! strcmp ( "start", command_name ) ) - { - start ( ); - } - else - if ( ! strcmp ( "alias", command_name ) ) - { - addAlias ( message ); - } - else - if ( ! strcmp ( "sayName", command_name ) ) - { - sayName ( ); - } - else - { - /* - * If the command is not any of the "special" commands - * above, then it's a "normal" command. - * That means we run it with a child process instance of - * qrsh_run, which will save all its data in the qrsh dir. - */ - stringstream ss; - ss << qrsh_run_path - << ' ' - << data_dir.str() - << ' ' - << s; - - if ( ! fork() ) - { - char ** argv = getArgs ( ss.str().c_str() ); - execv ( qrsh_run_path.c_str(), argv ); - perror ( "qrsh_server: execv error: " ); - } - } -} - - - -void -QrshServer::received ( Message & message ) -{ - if ( myMessage ( message ) ) - runCommand ( message ); -} - - - -}} // namespace qpid::tests - -using namespace qpid::tests; - -/* - * fixme mick Mon Aug 3 10:29:26 EDT 2009 - * argv[1] server name - * argv[2] qrsh exe path - * argv[3] host - * argv[4] port - */ -int -main ( int /*argc*/, char** argv ) -{ - const char* host = argv[3]; - int port = atoi(argv[4]); - Connection connection; - Message msg; - - srand ( getpid() ); - - try - { - connection.open ( host, port ); - Session session = connection.newSession(); - - - // Declare queues. - string myQueue = session.getId().getName(); - session.queueDeclare ( arg::queue=myQueue, - arg::exclusive=true, - arg::autoDelete=true); - - session.exchangeBind ( arg::exchange="amq.fanout", - arg::queue=myQueue, - arg::bindingKey="my-key"); - - // Create a server and subscribe it to my queue. - SubscriptionManager subscriptions ( session ); - QrshServer server ( subscriptions, - argv[1], // server name - argv[2], // qrsh exe path - host, - port - ); - subscriptions.subscribe ( server, myQueue ); - - // Receive messages until the subscription is cancelled - // by QrshServer::received() - subscriptions.run(); - - connection.close(); - } - catch(const exception& error) - { - cout << error.what() << endl; - return 1; - } - - return 0; -} - - - - diff --git a/qpid/cpp/src/tests/qrsh_utils/2_forever b/qpid/cpp/src/tests/qrsh_utils/2_forever deleted file mode 100755 index 5528b0e4d8..0000000000 --- a/qpid/cpp/src/tests/qrsh_utils/2_forever +++ /dev/null @@ -1,26 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#! /bin/bash - - -./qrsh 127.0.0.1 5813 \ - mrg23 command_2 /home/mick/redhat/qrsh/qrsh_run/forever foo bar baz diff --git a/qpid/cpp/src/tests/qrsh_utils/4_wait_for_it b/qpid/cpp/src/tests/qrsh_utils/4_wait_for_it deleted file mode 100755 index a4dc0da1ce..0000000000 --- a/qpid/cpp/src/tests/qrsh_utils/4_wait_for_it +++ /dev/null @@ -1,26 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#! /bin/bash - -./qrsh 127.0.0.1 5813 \ - mrg23 exec_wait /home/mick/redhat/qrsh/qrsh_run/my_command foo bar baz -echo "my_command returned an exit code of $?" diff --git a/qpid/cpp/src/tests/qrsh_utils/5_exited b/qpid/cpp/src/tests/qrsh_utils/5_exited deleted file mode 100755 index 4fec1dcc79..0000000000 --- a/qpid/cpp/src/tests/qrsh_utils/5_exited +++ /dev/null @@ -1,64 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#! /bin/bash - -path=/home/mick/redhat/qrsh/qrsh_run - -echo "Running command_3 ..." -./qrsh 127.0.0.1 5813 \ - mrg23 command_3 $path/my_command foo bar baz - -echo "Now I do some other stuff..." -sleep 1 -echo "And then some more stuff..." -sleep 1 -echo "and so on..." -sleep 1 - -echo "Now I'm waiting for command_3 ..." -./qrsh 127.0.0.1 5813 \ - mrg23 exited command_3 -echo "has command_3 exited: $? ." -sleep 5 - -./qrsh 127.0.0.1 5813 \ - mrg23 exited command_3 -echo "has command_3 exited: $? ." -sleep 5 - -./qrsh 127.0.0.1 5813 \ - mrg23 exited command_3 -echo "has command_3 exited: $? ." -sleep 5 - -./qrsh 127.0.0.1 5813 \ - mrg23 exited command_3 -echo "has command_3 exited: $? ." -sleep 5 - -./qrsh 127.0.0.1 5813 \ - mrg23 exited command_3 -echo "has command_3 exited: $? ." -sleep 5 - - - diff --git a/qpid/cpp/src/tests/qrsh_utils/6_get b/qpid/cpp/src/tests/qrsh_utils/6_get deleted file mode 100755 index 4b35ca98e6..0000000000 --- a/qpid/cpp/src/tests/qrsh_utils/6_get +++ /dev/null @@ -1,29 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#! /bin/bash - -echo "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 deleted file mode 100755 index 59911089ec..0000000000 --- a/qpid/cpp/src/tests/qrsh_utils/7_get_output +++ /dev/null @@ -1,44 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#! /bin/bash - -echo "Run a command..." -./qrsh 127.0.0.1 5813 \ - mrg23 command_4 /home/mick/redhat/qrsh/qrsh_run/my_command foo bar baz - -echo "Wait for a while..." -sleep 20 - -echo "Get stderr output:" -echo "------------- begin stderr ---------------" -./qrsh 127.0.0.1 5813 \ - mrg23 get command_4 stderr -echo "------------- end stderr ---------------" -echo " " -echo " " -echo " " -echo "Get stdout output:" -echo "------------- begin stdout ---------------" -./qrsh 127.0.0.1 5813 \ - mrg23 get command_4 stdout -echo "------------- end stdout ---------------" - diff --git a/qpid/cpp/src/tests/qrsh_utils/8_any b/qpid/cpp/src/tests/qrsh_utils/8_any deleted file mode 100755 index 2a922ea0e0..0000000000 --- a/qpid/cpp/src/tests/qrsh_utils/8_any +++ /dev/null @@ -1,43 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#! /bin/bash - -echo "asking any server to say his name ..." -./qrsh 127.0.0.1 5813 \ - any sayName -sleep 1 -echo "asking any server to say his name ..." -./qrsh 127.0.0.1 5813 \ - any sayName -sleep 1 -echo "asking any server to say his name ..." -./qrsh 127.0.0.1 5813 \ - any sayName -sleep 1 -echo "asking any server to say his name ..." -./qrsh 127.0.0.1 5813 \ - any sayName -sleep 1 - - - - diff --git a/qpid/cpp/src/tests/qrsh_utils/9_alias b/qpid/cpp/src/tests/qrsh_utils/9_alias deleted file mode 100755 index a4cfdfdf9a..0000000000 --- a/qpid/cpp/src/tests/qrsh_utils/9_alias +++ /dev/null @@ -1,38 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#! /bin/bash - -# Make a group of two of the servers, using "alias", -# and send the group a command. - -qrsh 127.0.0.1 5813 \ - mrg22 alias group_1 -qrsh 127.0.0.1 5813 \ - mrg23 alias group_1 - -echo "Asking group_1 to say their names... " -qrsh 127.0.0.1 5813 \ - group_1 sayName - - - - diff --git a/qpid/cpp/src/tests/qrsh_utils/qrsh_example_command.cpp b/qpid/cpp/src/tests/qrsh_utils/qrsh_example_command.cpp deleted file mode 100644 index 386e2f73f0..0000000000 --- a/qpid/cpp/src/tests/qrsh_utils/qrsh_example_command.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -#include <stdio.h> -#include <unistd.h> - - - -main ( int argc, char ** argv ) -{ - fprintf ( stderr, "Hello, I am the 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 deleted file mode 100644 index 191a9bca11..0000000000 --- a/qpid/cpp/src/tests/qrsh_utils/qrsh_forever.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - - -#include <stdio.h> -#include <unistd.h> - - - -main ( int argc, char ** argv ) -{ - fprintf ( stderr, "Hello, I am the Forever Example Child!\n"); - fprintf ( stderr, "my %d arguments are:\n", argc - 1 ); - - int i; - for ( i = 1; i < argc; ++ i ) - fprintf ( stderr, "arg %d: |%s|\n", i, argv[i] ); - - for ( i = 0; i >= 0; ++ i ) - { - fprintf ( stderr, "child sleeping forever %d ...\n" , i); - sleep ( 1 ); - } - - fprintf ( stderr, "child exiting with code 12.\n" ); - - return 12; -} - - - - diff --git a/qpid/cpp/src/tests/qrsh_utils/qsh_doc.txt b/qpid/cpp/src/tests/qrsh_utils/qsh_doc.txt deleted file mode 100644 index ad5990b38b..0000000000 --- a/qpid/cpp/src/tests/qrsh_utils/qsh_doc.txt +++ /dev/null @@ -1,309 +0,0 @@ -/* - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * - */ - -############################################## - qrsh: a Qpid-based remote shell utility - - Last updated: 3 Aug 09 Mick Goulish -############################################## - - - -============================= -Overview -============================= - - You're writing a multi-box test, and you want to write a - shell script in which you start processes on other boxes - and kill them (or send arbitrary signals to them). - - But ssh doesn't let you signal them, and bash isn't the - greatest language in the world for creating data structures - (like you need to associate the PIDs with box names and - executable names.) - - Qsh is a utility implemented on Qpid that you can use from - within your bash script, or any other scripting language. - With it, you can: - - 1. run any executable on any box in your cluster. - - 2. don't worry about PIDs and box-names. You associate - your own abstract names with the executable instances, - and then use those names in the rest of your script. - I.e. "broker_1" "sender_3" etc. - - 3. Launch the executable and wait until it returns, and - get its exit code. - - 4. Launch your executable and do other stuff, then come - back later and see if it has exited. - - 5. Get whatever it sent to stdout or stderr. - - 6. Get the contents of any other file. - - 7. send a command to all your boxes at once - - 8. send a command to a randomly selected box. - - 9. define groups of boxes, and send a command simultaneously - to all boxes in a given group. - - - - -============================= -Using It -============================= - - 1. You need to run a Qpid broker. - - 2. You start a Qpid client ( which is called a qrsh_server ) - on all the boxes you care about. And you give them all - names like "mrg13", "mrg14" etc. The names can be anything - you want, but I've always used one qrsh_server per box, - and given it the box name. ( However, you can run two on - one box, they won't collide. ) - - 3. After you start all servers, send a "start" command to any - one of them: - - 4. The qrsh_servers use the fanout exchange to talk to each - other. - - 5. In your script, you run an executable called "qrsh". It knows - how to talk to the servers, do what you want, and retrieve - the data you want. - - - example start script: (this does 4 servers on the same box) - ------------------------------------------------------------- - - echo "Starting server mrg22 ..." - ./qrsh_server mrg22 ./qrsh_run 127.0.0.1 5813 & - - echo "Starting server mrg23 ..." - ./qrsh_server mrg23 ./qrsh_run 127.0.0.1 5813 & - - echo "Starting server mrg24 ..." - ./qrsh_server mrg24 ./qrsh_run 127.0.0.1 5813 & - - echo "Starting server mrg25 ..." - ./qrsh_server mrg25 ./qrsh_run 127.0.0.1 5813 & - - echo "Issuing start command..." - sleep 2 - ./qrsh 127.0.0.1 5813 mrg22 start - sleep 1 - - echo "Ready." - - # end of script. - - - - - - -============================= -Qrsh Syntax -============================= - - qrsh host port server_name command_name arg* - - - "host" and "port" specify the Qpid server to connect to. - - "server_name" can be anything you want. I always use the name - of the box that the server is running on. - - "command_name" is the name that you choose to assign to - the process you are running. Each process that you decide - to name must have a unique name within this script. - - Or it could be a reserved command name, that Qsh - interprets in a special way. - - Reserved command names are: - - exec - exec_wait - exited - get - - "exec" means "interpret the rest of the command line as a - command to be executed by the designated server. - - "exec_wait" means same as "exec", but wait for the command - to terminate, and return its exit code. - - "exited" -- you provide 1 arg, which is an abstract - process name. qrsh returns 1 if that process has exited, - else 0. - - "get" -- you provide one arg which is a path. qrsh returns - (by printing to stdout) the contents of that file. - - "arg*" is zero or more arguments. They are interpreted - differently depending on whether you are using one of - the above reserved command names, or making up your own - abstract name for a command. - - - - -============================= -Examples -============================= - - 1. Run a process on a remote box. - - qrsh mrg23 command_1 /usr/sbin/whatever foo bar baz - - Returns immediately. - - - - 2. Kill a process that you started earlier: - - qrsh mrg23 exec kill -9 command_1 - - After the word "exec" put any command line you want. - The server you're sending this to will replace all abstract - names in the command with process IDs. ( In this example, - just the word "command_1" will be replaced. ) Then it will - execute the command. - - - - 3. Execute a command, and wait for it to finish - - qrsh mrg23 exec_wait command_name args - - - - 4. Check on whether a command you issude earlier has exited. - - ./qrsh mrg23 exited command_3 - - Returns 1 if it has exited, else 0. - - - - 5. Get the contents of a file from the remote system: - - ./qrsh mrg23 get /tmp/foo - - Prints the contents to stdout. - - - - 6. Send a command to all servers at once: - - # This example causes them all to print thir names to stderr. - ./qrsh all sayName - - - 7. Define a group of servers and send a command to that group. - - #! /bin/bash - - # Make a group of two of the servers, using "alias", - # and send the group a command. - - qrsh 127.0.0.1 5813 \ - mrg22 alias group_1 - - qrsh 127.0.0.1 5813 \ - mrg23 alias group_1 - - echo "Asking group_1 to say their names... " - qrsh 127.0.0.1 5813 \ - group_1 sayName - - # end of script. - - - - - 8. Execute a command and get its stdout and stderr contents. - - #! /bin/bash - - echo "Run a command..." - ./qrsh 127.0.0.1 5813 \ - mrg23 command_4 my_command foo bar baz - - echo "Wait for a while..." - sleep 10 - - echo "Get stderr output:" - echo "------------- begin stderr ---------------" - ./qrsh 127.0.0.1 5813 \ - mrg23 get command_4 stderr - echo "------------- end stderr ---------------" - echo " " - - echo " " - echo "Get stdout output:" - echo "------------- begin stdout ---------------" - ./qrsh 127.0.0.1 5813 \ - mrg23 get command_4 stdout - echo "------------- end stdout ---------------" - - # end of script. - - - - - 9. Send a command to one of your servers, selected - at random. - - #! /bin/bash - - # I do it multiple times here, so I can see - # that it really is selecting randomly. - - echo "asking any server to say his name ..." - ./qrsh 127.0.0.1 5813 \ - any sayName - sleep 1 - - echo "asking any server to say his name ..." - ./qrsh 127.0.0.1 5813 \ - any sayName - sleep 1 - - echo "asking any server to say his name ..." - ./qrsh 127.0.0.1 5813 \ - any sayName - sleep 1 - - echo "asking any server to say his name ..." - ./qrsh 127.0.0.1 5813 \ - any sayName - - # end of script. - - - - diff --git a/qpid/cpp/src/tests/queue_flow_limit_tests.py b/qpid/cpp/src/tests/queue_flow_limit_tests.py index ac62dcdd1e..bdd2a21b78 100644 --- a/qpid/cpp/src/tests/queue_flow_limit_tests.py +++ b/qpid/cpp/src/tests/queue_flow_limit_tests.py @@ -137,12 +137,12 @@ class QueueFlowLimitTests(TestBase010): """ self.startQmf(); oid = self._create_queue("test-q", stop_count=373, resume_count=229) + self.assertEqual(self.qmf.getObjects(_objectId=oid)[0].flowStoppedCount, 0) sndr1 = self._start_qpid_send("test-q", count=1213, content="XXX", capacity=50); sndr2 = self._start_qpid_send("test-q", count=797, content="Y", capacity=13); sndr3 = self._start_qpid_send("test-q", count=331, content="ZZZZZ", capacity=149); totalMsgs = 1213 + 797 + 331 - # wait until flow control is active count = 0 @@ -180,6 +180,7 @@ class QueueFlowLimitTests(TestBase010): self.assertEqual(count, totalMsgs) self.failIf(self.qmf.getObjects(_objectId=oid)[0].flowStopped) + self.failUnless(self.qmf.getObjects(_objectId=oid)[0].flowStoppedCount) self._delete_queue("test-q") diff --git a/qpid/cpp/src/tests/replication_test b/qpid/cpp/src/tests/replication_test index 691fd20b0c..8c37568875 100755 --- a/qpid/cpp/src/tests/replication_test +++ b/qpid/cpp/src/tests/replication_test @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # # Licensed to the Apache Software Foundation (ASF) under one diff --git a/qpid/cpp/src/tests/run_acl_tests b/qpid/cpp/src/tests/run_acl_tests index aff13408ed..41f41e20e1 100755 --- a/qpid/cpp/src/tests/run_acl_tests +++ b/qpid/cpp/src/tests/run_acl_tests @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # # Licensed to the Apache Software Foundation (ASF) under one diff --git a/qpid/cpp/src/tests/run_cli_tests b/qpid/cpp/src/tests/run_cli_tests index 3f1388b9f5..ec5c71b646 100755 --- a/qpid/cpp/src/tests/run_cli_tests +++ b/qpid/cpp/src/tests/run_cli_tests @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # # Licensed to the Apache Software Foundation (ASF) under one @@ -70,7 +70,8 @@ stop_brokers() { if test -d ${PYTHON_DIR} ; then start_brokers echo "Running CLI tests using brokers on ports $LOCAL_PORT $REMOTE_PORT" - $QPID_PYTHON_TEST -m cli_tests -b localhost:$LOCAL_PORT -Dremote-port=$REMOTE_PORT -Dcli-dir=$CLI_DIR $targs $@ + PYTHON_TESTS=${PYTHON_TESTS:-$*} + $QPID_PYTHON_TEST -m cli_tests -b localhost:$LOCAL_PORT -Dremote-port=$REMOTE_PORT -Dcli-dir=$CLI_DIR $targs $PYTHON_TESTS $@ RETCODE=$? stop_brokers if test x$RETCODE != x0; then diff --git a/qpid/cpp/src/tests/run_federation_tests b/qpid/cpp/src/tests/run_federation_tests index 4be27a2e85..590f74746e 100755 --- a/qpid/cpp/src/tests/run_federation_tests +++ b/qpid/cpp/src/tests/run_federation_tests @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # # Licensed to the Apache Software Foundation (ASF) under one diff --git a/qpid/cpp/src/tests/run_header_test b/qpid/cpp/src/tests/run_header_test index 07658343e7..34008132cc 100755 --- a/qpid/cpp/src/tests/run_header_test +++ b/qpid/cpp/src/tests/run_header_test @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # # Licensed to the Apache Software Foundation (ASF) under one diff --git a/qpid/cpp/src/tests/run_test b/qpid/cpp/src/tests/run_test index 4b227621bc..6ec1fd892b 100755 --- a/qpid/cpp/src/tests/run_test +++ b/qpid/cpp/src/tests/run_test @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file diff --git a/qpid/cpp/src/tests/sasl.mk b/qpid/cpp/src/tests/sasl.mk index 5b8419f604..20eaa7c7a5 100644 --- a/qpid/cpp/src/tests/sasl.mk +++ b/qpid/cpp/src/tests/sasl.mk @@ -30,9 +30,20 @@ check_PROGRAMS+=sasl_version sasl_version_SOURCES=sasl_version.cpp sasl_version_LDADD=$(lib_client) -TESTS += run_cluster_authentication_test sasl_fed sasl_fed_ex +TESTS += run_cluster_authentication_test sasl_fed sasl_fed_ex_dynamic sasl_fed_ex_link sasl_fed_ex_queue sasl_fed_ex_route sasl_fed_ex_route_cluster sasl_fed_ex_link_cluster sasl_fed_ex_queue_cluster sasl_fed_ex_dynamic_cluster LONG_TESTS += run_cluster_authentication_soak -EXTRA_DIST += run_cluster_authentication_test sasl_fed sasl_fed_ex run_cluster_authentication_soak +EXTRA_DIST += run_cluster_authentication_test \ + sasl_fed \ + sasl_fed_ex \ + run_cluster_authentication_soak \ + sasl_fed_ex_dynamic \ + sasl_fed_ex_link \ + sasl_fed_ex_queue \ + sasl_fed_ex_route \ + sasl_fed_ex_dynamic_cluster \ + sasl_fed_ex_link_cluster \ + sasl_fed_ex_queue_cluster \ + sasl_fed_ex_route_cluster endif # HAVE_SASL diff --git a/qpid/cpp/src/tests/sasl_fed_ex b/qpid/cpp/src/tests/sasl_fed_ex index 0740650d6c..71dece56a9 100755 --- a/qpid/cpp/src/tests/sasl_fed_ex +++ b/qpid/cpp/src/tests/sasl_fed_ex @@ -21,20 +21,49 @@ #=============================================================================== -# This test creates a federated link between two brokers using SASL security. +# These tests create federated links between two brokers using SASL security. # The SASL mechanism used is EXTERNAL, which is satisfied by SSL # transport-layer security. #=============================================================================== source ./test_env.sh +script_name=`basename $0` + +if [ $# -lt 1 ] || [ $# -gt 2 ] +then + echo + # These are the four different ways of creating links ( or routes+links ) + # that the qpid-route command provides. + echo "Usage: ${script_name} dynamic|link|queue|route [cluster]" + echo + exit 1 +fi + +# Has the user told us to do clustering ? ----------- +clustering_flag= +if [ $# -eq "2" ] && [ "$2" == "cluster" ]; then + clustering_flag=true +fi + +qpid_route_method=$1 + +# Debugging print. -------------------------- +debug= +function print { + if [ "$debug" ]; then + echo "${script_name}: $1" + fi +} + + # This minimum value corresponds to sasl version 2.1.22 minimum_sasl_version=131350 sasl_version=`$QPID_TEST_EXEC_DIR/sasl_version` -# This test is necessary becasue this sasl version is the first one that permits +# This test is necessary because this sasl version is the first one that permits # redirection of the sasl config file path. if [ "$sasl_version" -lt "$minimum_sasl_version" ]; then echo "sasl_fed: must have sasl version 2.1.22 or greater. ( Integer value: $minimum_sasl_version ) Version is: $sasl_version" @@ -60,6 +89,7 @@ create_certs() { delete_certs() { if [[ -e ${CERT_DIR} ]] ; then + print "removing cert dir ${CERT_DIR}" rm -rf ${CERT_DIR} fi } @@ -75,19 +105,31 @@ delete_certs create_certs || error "Could not create test certificate" -sasl_config_file=$builddir/sasl_config +sasl_config_dir=$builddir/sasl_config -my_random_number=$RANDOM -tmp_root=/tmp/sasl_fed_$my_random_number +tmp_root=${builddir}/sasl_fed_ex_temp +print "results dir is ${tmp_root}" +rm -rf ${tmp_root} mkdir -p $tmp_root SRC_SSL_PORT=6667 DST_SSL_PORT=6666 +SRC_SSL_PORT_2=6668 +DST_SSL_PORT_2=6669 + SRC_TCP_PORT=5801 DST_TCP_PORT=5807 -SSL_LIB=../.libs/ssl.so +SRC_TCP_PORT_2=5802 +DST_TCP_PORT_2=5803 + +CLUSTER_1_NAME=sasl_fed_ex_cluster_1 +CLUSTER_2_NAME=sasl_fed_ex_cluster_2 + + +SSL_LIB=${moduledir}/ssl.so +CLUSTER_LIB=${moduledir}/cluster.so export QPID_SSL_CERT_NAME=${TEST_HOSTNAME} @@ -116,52 +158,109 @@ export QPID_SSL_CERT_NAME=${TEST_HOSTNAME} # 5. DST pulls messages off the temp queue on SRC to itself. # +COMMON_BROKER_OPTIONS=" \ + --ssl-sasl-no-dict \ + --sasl-config=$sasl_config_dir \ + --ssl-require-client-authentication \ + --auth yes \ + --ssl-cert-db $CERT_DIR \ + --ssl-cert-password-file $CERT_PW_FILE \ + --ssl-cert-name $TEST_HOSTNAME \ + --no-data-dir \ + --no-module-dir \ + --load-module ${SSL_LIB} \ + --load-module ${CLUSTER_LIB} \ + --mgmt-enable=yes \ + --log-enable info+ \ + --log-source yes \ + --daemon " + + +function start_brokers { + if [ $1 ]; then + # clustered ---------------------------------------- + print "Starting SRC cluster" + + print " src broker 1" + $QPIDD_EXEC \ + --port=${SRC_TCP_PORT} \ + --ssl-port ${SRC_SSL_PORT} \ + ${COMMON_BROKER_OPTIONS} \ + --cluster-name ${CLUSTER_1_NAME} \ + --log-to-file $tmp_root/qpidd_src.log 2> /dev/null + + broker_ports[0]=${SRC_TCP_PORT} + + print " src broker 2" + $QPIDD_EXEC \ + --port=${SRC_TCP_PORT_2} \ + --ssl-port ${SRC_SSL_PORT_2} \ + ${COMMON_BROKER_OPTIONS} \ + --cluster-name ${CLUSTER_1_NAME} \ + --log-to-file $tmp_root/qpidd_src_2.log 2> /dev/null + + broker_ports[1]=${SRC_TCP_PORT_2} + + + print "Starting DST cluster" + + print " dst broker 1" + $QPIDD_EXEC \ + --port=${DST_TCP_PORT} \ + --ssl-port ${DST_SSL_PORT} \ + ${COMMON_BROKER_OPTIONS} \ + --cluster-name ${CLUSTER_2_NAME} \ + --log-to-file $tmp_root/qpidd_dst.log 2> /dev/null + + broker_ports[2]=${DST_TCP_PORT} + + print " dst broker 2" + $QPIDD_EXEC \ + --port=${DST_TCP_PORT_2} \ + --ssl-port ${DST_SSL_PORT_2} \ + ${COMMON_BROKER_OPTIONS} \ + --cluster-name ${CLUSTER_2_NAME} \ + --log-to-file $tmp_root/qpidd_dst_2.log 2> /dev/null + + broker_ports[3]=${DST_TCP_PORT_2} + + else + # vanilla brokers -------------------------------- + print "Starting SRC broker" + $QPIDD_EXEC \ + --port=${SRC_TCP_PORT} \ + --ssl-port ${SRC_SSL_PORT} \ + ${COMMON_BROKER_OPTIONS} \ + --log-to-file $tmp_root/qpidd_src.log 2> /dev/null + + broker_ports[0]=${SRC_TCP_PORT} + + print "Starting DST broker" + $QPIDD_EXEC \ + --port=${DST_TCP_PORT} \ + --ssl-port ${DST_SSL_PORT} \ + ${COMMON_BROKER_OPTIONS} \ + --log-to-file $tmp_root/qpidd_dst.log 2> /dev/null + + broker_ports[1]=${DST_TCP_PORT} + fi +} -#echo "-----------------------" -#echo "Starting SRC broker" -#echo "-----------------------" -$QPIDD_EXEC \ - --port=${SRC_TCP_PORT} \ - --ssl-port ${SRC_SSL_PORT} \ - --ssl-sasl-no-dict \ - --sasl-config=$sasl_config_file \ - --ssl-require-client-authentication \ - --auth yes \ - --ssl-cert-db $CERT_DIR \ - --ssl-cert-password-file $CERT_PW_FILE \ - --ssl-cert-name $TEST_HOSTNAME \ - --no-data-dir \ - --no-module-dir \ - --load-module ${SSL_LIB} \ - --mgmt-enable=yes \ - --log-enable info+ \ - --log-source yes \ - --daemon \ - --log-to-file $tmp_root/qpidd_src.log 2> /dev/null - - -#echo "-----------------------" -#echo "Starting DST broker" -#echo "-----------------------" -$QPIDD_EXEC \ - --port=${DST_TCP_PORT} \ - --ssl-port ${DST_SSL_PORT} \ - --ssl-cert-db $CERT_DIR \ - --ssl-cert-password-file $CERT_PW_FILE \ - --ssl-cert-name $TEST_HOSTNAME \ - --ssl-sasl-no-dict \ - --ssl-require-client-authentication \ - --sasl-config=$sasl_config_file \ - --no-data-dir \ - --no-module-dir \ - --load-module ${SSL_LIB} \ - --mgmt-enable=yes \ - --log-enable info+ \ - --log-source yes \ - --daemon \ - $COMMON_BROKER_OPTIONS \ - --log-to-file $tmp_root/qpidd_dst.log 2> /dev/null +function halt_brokers { + n_brokers=${#broker_ports[@]} + print "Halting ${n_brokers} brokers." + for i in $(seq 0 $((${n_brokers} - 1))) + do + halt_port=${broker_ports[$i]} + print "Halting broker $i on port ${halt_port}" + $QPIDD_EXEC --port ${halt_port} --quit + done + +} + + +start_brokers $clustering_flag # I am not randomizing these names, because this test creates its own brokers. @@ -170,76 +269,77 @@ ROUTING_KEY=sasl_fed_queue EXCHANGE_NAME=sasl_fedex -#echo "-----------------------" -#echo "add exchanges" -#echo "-----------------------" +print "add exchanges" $QPID_CONFIG_EXEC -a localhost:${SRC_TCP_PORT} add exchange direct $EXCHANGE_NAME $QPID_CONFIG_EXEC -a localhost:${DST_TCP_PORT} add exchange direct $EXCHANGE_NAME -#echo "-----------------------" -#echo "add queues" -#echo "-----------------------" +print "add queues" $QPID_CONFIG_EXEC -a localhost:${SRC_TCP_PORT} add queue $QUEUE_NAME $QPID_CONFIG_EXEC -a localhost:${DST_TCP_PORT} add queue $QUEUE_NAME -#echo "-----------------------" -#echo "create bindings" -#echo "-----------------------" +print "create bindings" $QPID_CONFIG_EXEC -a localhost:${SRC_TCP_PORT} bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY $QPID_CONFIG_EXEC -a localhost:${DST_TCP_PORT} bind $EXCHANGE_NAME $QUEUE_NAME $ROUTING_KEY -#echo "-----------------------" -#echo "qpid-route route add" -#echo "-----------------------" +# # NOTE: The SRC broker *must* be referred to as $TEST_HOSTNAME, and not as "localhost". # It must be referred to by the exact string given as the Common Name (CN) in the cert, # which was created in the function create_certs, above. -$QPID_ROUTE_EXEC route add localhost:${DST_TCP_PORT} $TEST_HOSTNAME:${SRC_SSL_PORT} -t ssl $EXCHANGE_NAME $ROUTING_KEY "" "" EXTERNAL -#echo "-----------------------" -#echo "view the route :" -#echo "-----------------------" -#$PYTHON_COMMANDS/qpid-route route list localhost:${DST_TCP_PORT} -# I don't know how to avoid this sleep yet. It has to come after route-creation. -sleep 5 - -n_messages=100 - -./datagen --count ${n_messages} | ./sender --broker localhost --port ${SRC_TCP_PORT} --exchange ${EXCHANGE_NAME} --routing-key ${ROUTING_KEY} --mechanism ANONYMOUS +#---------------------------------------------------------------- +# Use qpid-route to create the link, or the link+route, depending +# on which of its several methods was requested. +#---------------------------------------------------------------- +if [ ${qpid_route_method} == "dynamic" ]; then + print "dynamic add" + $QPID_ROUTE_EXEC -t ssl dynamic add localhost:${DST_TCP_PORT} $TEST_HOSTNAME:${SRC_SSL_PORT} $EXCHANGE_NAME "" "" EXTERNAL +elif [ ${qpid_route_method} == "link" ]; then + print "link add" + $QPID_ROUTE_EXEC -t ssl link add localhost:${DST_TCP_PORT} $TEST_HOSTNAME:${SRC_SSL_PORT} EXTERNAL +elif [ ${qpid_route_method} == "queue" ]; then + print "queue add" + $QPID_ROUTE_EXEC -t ssl queue add localhost:${DST_TCP_PORT} $TEST_HOSTNAME:${SRC_SSL_PORT} $EXCHANGE_NAME $ROUTING_KEY EXTERNAL +elif [ ${qpid_route_method} == "route" ]; then + print "route add" + $QPID_ROUTE_EXEC -t ssl route add localhost:${DST_TCP_PORT} $TEST_HOSTNAME:${SRC_SSL_PORT} $EXCHANGE_NAME $ROUTING_KEY "" "" EXTERNAL +else + echo "unknown method: |${qpid_route_method}|" + echo " choices are: dynamic|link|queue|route " + print "Asking brokers to quit." + $QPIDD_EXEC --port ${SRC_TCP_PORT} --quit + $QPIDD_EXEC --port ${DST_TCP_PORT} --quit + exit 2 +fi -#echo "-----------------------" -#echo "Examine DST Broker" -#echo "-----------------------" -dst_message_count=`qpid-stat -q localhost:${DST_TCP_PORT} | grep sasl_fed_queue | awk '{print $2}'` +# I don't know how to avoid this sleep yet. It has to come after route-creation +# to avoid false negatives. +sleep 5 +# This should work the same whether or not we are running a clustered test. +print "check the link" +link_status=$($QPID_ROUTE_EXEC link list localhost:${DST_TCP_PORT} | tail -1 | awk '{print $5}') +print "link_status == ${link_status}" -#echo "-----------------------" -#echo "Asking brokers to quit." -#echo "-----------------------" -$QPIDD_EXEC --port ${SRC_TCP_PORT} --quit -$QPIDD_EXEC --port ${DST_TCP_PORT} --quit +halt_brokers +sleep 1 -#echo "-----------------------" -#echo "Removing temporary directory $tmp_root" -#echo "-----------------------" +print "Removing temporary directory $tmp_root" rm -rf $tmp_root -if [ "$dst_message_count" -eq "$n_messages" ]; then - #echo "good: |$dst_message_count| == |$n_messages|" + +if [ ${link_status} == "Operational" ]; then + print "result: good" exit 0 -else - #echo "not ideal: |$dst_message_count| != |$n_messages|" - exit 1 fi - - +print "result: fail" +exit 3 diff --git a/qpid/cpp/src/tests/sasl_fed_ex_dynamic b/qpid/cpp/src/tests/sasl_fed_ex_dynamic new file mode 100755 index 0000000000..c20b8d69a0 --- /dev/null +++ b/qpid/cpp/src/tests/sasl_fed_ex_dynamic @@ -0,0 +1,27 @@ +#! /bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +source ./test_env.sh + +${srcdir}/sasl_fed_ex dynamic + + diff --git a/qpid/cpp/src/tests/sasl_fed_ex_dynamic_cluster b/qpid/cpp/src/tests/sasl_fed_ex_dynamic_cluster new file mode 100755 index 0000000000..273a543ec5 --- /dev/null +++ b/qpid/cpp/src/tests/sasl_fed_ex_dynamic_cluster @@ -0,0 +1,27 @@ +#! /bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +source ./test_env.sh + +${srcdir}/sasl_fed_ex dynamic cluster + + diff --git a/qpid/cpp/src/tests/sasl_fed_ex_link b/qpid/cpp/src/tests/sasl_fed_ex_link new file mode 100755 index 0000000000..7b232d4874 --- /dev/null +++ b/qpid/cpp/src/tests/sasl_fed_ex_link @@ -0,0 +1,27 @@ +#! /bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +source ./test_env.sh + +${srcdir}/sasl_fed_ex link + + diff --git a/qpid/cpp/src/tests/sasl_fed_ex_link_cluster b/qpid/cpp/src/tests/sasl_fed_ex_link_cluster new file mode 100755 index 0000000000..0e037b1969 --- /dev/null +++ b/qpid/cpp/src/tests/sasl_fed_ex_link_cluster @@ -0,0 +1,27 @@ +#! /bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +source ./test_env.sh + +${srcdir}/sasl_fed_ex link cluster + + diff --git a/qpid/cpp/src/tests/sasl_fed_ex_queue b/qpid/cpp/src/tests/sasl_fed_ex_queue new file mode 100755 index 0000000000..be0c10cf63 --- /dev/null +++ b/qpid/cpp/src/tests/sasl_fed_ex_queue @@ -0,0 +1,27 @@ +#! /bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +source ./test_env.sh + +${srcdir}/sasl_fed_ex queue + + diff --git a/qpid/cpp/src/tests/sasl_fed_ex_queue_cluster b/qpid/cpp/src/tests/sasl_fed_ex_queue_cluster new file mode 100755 index 0000000000..ec8750b861 --- /dev/null +++ b/qpid/cpp/src/tests/sasl_fed_ex_queue_cluster @@ -0,0 +1,27 @@ +#! /bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +source ./test_env.sh + +${srcdir}/sasl_fed_ex queue cluster + + diff --git a/qpid/cpp/src/tests/sasl_fed_ex_route b/qpid/cpp/src/tests/sasl_fed_ex_route new file mode 100755 index 0000000000..dd5c4f3cac --- /dev/null +++ b/qpid/cpp/src/tests/sasl_fed_ex_route @@ -0,0 +1,27 @@ +#! /bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +source ./test_env.sh + +${srcdir}/sasl_fed_ex route + + diff --git a/qpid/cpp/src/tests/sasl_fed_ex_route_cluster b/qpid/cpp/src/tests/sasl_fed_ex_route_cluster new file mode 100755 index 0000000000..fd59680b6e --- /dev/null +++ b/qpid/cpp/src/tests/sasl_fed_ex_route_cluster @@ -0,0 +1,27 @@ +#! /bin/bash + +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + + +source ./test_env.sh + +${srcdir}/sasl_fed_ex route cluster + + diff --git a/qpid/cpp/src/tests/ssl_test b/qpid/cpp/src/tests/ssl_test index 04584f169d..cbf75eb237 100755 --- a/qpid/cpp/src/tests/ssl_test +++ b/qpid/cpp/src/tests/ssl_test @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # # Licensed to the Apache Software Foundation (ASF) under one diff --git a/qpid/cpp/xml/cluster.xml b/qpid/cpp/xml/cluster.xml index ee5ef60e5e..e0cd647894 100644 --- a/qpid/cpp/xml/cluster.xml +++ b/qpid/cpp/xml/cluster.xml @@ -252,7 +252,6 @@ <!-- Replicate encoded exchanges/queues. --> <control name="exchange" code="0x31"><field name="encoded" type="str32"/></control> - <control name="queue" code="0x32"><field name="encoded" type="str32"/></control> <!-- Set expiry-id for subsequent messages. --> <control name="expiry-id" code="0x33"><field name="expiry-id" type="uint64"/></control> diff --git a/qpid/doc/book/src/AMQP-Messaging-Broker-CPP-Book.xml b/qpid/doc/book/src/AMQP-Messaging-Broker-CPP-Book.xml index 309492262d..6c27d7c668 100644 --- a/qpid/doc/book/src/AMQP-Messaging-Broker-CPP-Book.xml +++ b/qpid/doc/book/src/AMQP-Messaging-Broker-CPP-Book.xml @@ -56,6 +56,7 @@ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="LVQ.xml"/> <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="queue-state-replication.xml"/> <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Starting-a-cluster.xml"/> + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="producer-flow-control.xml"/> <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="AMQP-Compatibility.xml"/> <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Qpid-Interoperability-Documentation.xml"/> diff --git a/qpid/doc/book/src/AMQP-Messaging-Broker-CPP.xml b/qpid/doc/book/src/AMQP-Messaging-Broker-CPP.xml index b9c513e511..15f5660455 100644 --- a/qpid/doc/book/src/AMQP-Messaging-Broker-CPP.xml +++ b/qpid/doc/book/src/AMQP-Messaging-Broker-CPP.xml @@ -50,7 +50,8 @@ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="LVQ.xml"/> <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="queue-state-replication.xml"/> <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="Starting-a-cluster.xml"/> - <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="ACL.xml"/> + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="ACL.xml"/> + <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="producer-flow-control.xml"/> </chapter> diff --git a/qpid/doc/book/src/Cheat-Sheet-for-configuring-Queue-Options.xml b/qpid/doc/book/src/Cheat-Sheet-for-configuring-Queue-Options.xml index 60f1a4103d..d50948e0cc 100644 --- a/qpid/doc/book/src/Cheat-Sheet-for-configuring-Queue-Options.xml +++ b/qpid/doc/book/src/Cheat-Sheet-for-configuring-Queue-Options.xml @@ -65,6 +65,16 @@ </itemizedlist> </para></listitem> </itemizedlist> + + <para> + The 0.10 C++ Broker supports the following additional Queue configuration options: + </para> + <itemizedlist> + <listitem><para> + <xref linkend="producer-flow-control"/> + </para></listitem> + </itemizedlist> + <section role="h3" id="CheatSheetforconfiguringQueueOptions-ApplyingQueueSizingConstraints"><title> Applying Queue Sizing Constraints </title> diff --git a/qpid/doc/book/src/Programming-In-Apache-Qpid.xml b/qpid/doc/book/src/Programming-In-Apache-Qpid.xml index 3950b375da..6fae2f0bc7 100644 --- a/qpid/doc/book/src/Programming-In-Apache-Qpid.xml +++ b/qpid/doc/book/src/Programming-In-Apache-Qpid.xml @@ -1722,7 +1722,7 @@ try { <row> <entry> - username + <literal>username</literal> </entry> <entry> string @@ -1733,7 +1733,7 @@ try { </row> <row> <entry> - password + <literal>password</literal> </entry> <entry> string @@ -1744,35 +1744,22 @@ try { </row> <row> <entry> - sasl-mechanism + <literal>sasl_mechanisms</literal> </entry> <entry> string </entry> <entry> - The specific SASL mechanism to use with the c++ - client when authenticating to the broker. Only a - single value can be specified at present. [C++ only]. - </entry> - </row> - <row> - <entry> - sasl_mechanisms - </entry> - <entry> - string - </entry> - <entry> - The specific SASL mechanism to use with the python + The specific SASL mechanisms to use with the python client when authenticating to the broker. The value - is a space separated list in order of preference. [Python only]. + is a space separated list. </entry> </row> <row> <entry> - reconnect + <literal>reconnect</literal> </entry> <entry> boolean @@ -1783,7 +1770,7 @@ try { </row> <row> <entry> - <literal>reconnect_timeout [Python]</literal> <literal>reconnect-timeout [C++]</literal> + <literal>reconnect_timeout</literal> </entry> <entry> integer @@ -1794,7 +1781,7 @@ try { </row> <row> <entry> - <literal>reconnect_limit [Python]</literal> <literal>reconnect-limit [C++]</literal> + <literal>reconnect_limit</literal> </entry> <entry> integer @@ -1805,7 +1792,7 @@ try { </row> <row> <entry> - <literal>reconnect_interval_min [Python]</literal> <literal>reconnect-interval-min [C++]</literal> + <literal>reconnect_interval_min</literal> </entry> <entry> integer representing time in seconds @@ -1816,7 +1803,7 @@ try { </row> <row> <entry> - <literal>reconnect_interval_max [Python]</literal> <literal>reconnect-interval-max [C++]</literal> + <literal>reconnect_interval_max</literal> </entry> <entry> integer representing time in seconds @@ -1827,7 +1814,7 @@ try { </row> <row> <entry> - <literal>reconnect_interval [Python]</literal> <literal>reconnect-interval [C++]</literal> + <literal>reconnect_interval</literal> </entry> <entry> integer representing time in seconds @@ -1839,7 +1826,7 @@ try { <row> <entry> - heartbeat + <literal>heartbeat</literal> </entry> <entry> integer representing time in seconds @@ -1852,7 +1839,7 @@ try { </row> <row> <entry> - protocol + <literal>protocol</literal> </entry> <entry> string @@ -1863,7 +1850,7 @@ try { </row> <row> <entry> - tcp-nodelay + <literal>tcp-nodelay</literal> </entry> <entry> boolean diff --git a/qpid/doc/book/src/producer-flow-control.xml b/qpid/doc/book/src/producer-flow-control.xml new file mode 100644 index 0000000000..01ca4f8ac9 --- /dev/null +++ b/qpid/doc/book/src/producer-flow-control.xml @@ -0,0 +1,347 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + +--> + +<section id="producer-flow-control"> + <title> + Producer Flow Control + </title> + + <section role="h2" id="producerflowcontrol-Overview"> + <title> + Overview + </title> + <para> + As of release 0.10, the C++ broker supports the use of flow control to + throttle back message producers that are at risk of overflowing a + destination queue. + </para> + + <para> + Each queue in the C++ broker has two threshold values associated with it: + </para> + + <para> + Flow Stop Threshold: this is the level of queue resource + utilization above which flow control will be enabled. Once this + threshold is crossed, the queue is considered in danger of overflow. + </para> + + <para> + Flow Resume Threshold - this is the level of queue resource utilization + below which flow control will be disabled. Once this threshold is + crossed, the queue is no longer considered in danger of overflow. + </para> + + <para> + In the above description, queue resource utilization may be + defined as the total count of messages currently enqueued, or the total + sum of all message content in bytes. + </para> + + <para> + The value for a queue's Flow Stop Threshold must be greater than or + equal to the value of the queue's Flow Resume Threshold. + </para> + + <section role="h3" id="producerflowcontrol-QueueThresholdsExample"> + <title> + Example + </title> + + <para> + Let's consider a queue with a maximum limit set on the total number of + messages that may be enqueued to that queue. Assume this maximum + message limit is 1000 messages. Assume also that the user configures a + Flow Stop Threshold of 900 messages, and a Flow Resume Threshold of 500 + messages. Then the following holds: + </para> + + <para> + When the total number of enqueued messages is greater than or equal to + 900, the queue's flow control state is OFF. + </para> + + <para> + When the total number of enqueued messages is greater than 900, the + queue's flow control state transitions to ON. + </para> + + <para> + When the queue's flow control state is "ON", it remains "ON" until the + total number of enqueued messages is less than 500. At that point, the queue's + flow control state transitions to "OFF". + </para> + + <para> + A similar example using total enqueued content bytes as the threshold + units are permitted. + </para> + </section> + + <para> + Thresholds may be set using both total message counts and total byte + counts. In this case, the following rules apply: + </para> + + <para> + 1) Flow control is "ON" when either stop threshold value is crossed. + </para> + <para> + 2) Flow control remains "ON" until both resume thresholds are satisfied. + </para> + + <section role="h3" id="producerflowcontro-MultiThresholdExample"> + <title> + Example + </title> + + <para> + Let's consider a queue with a maximum size limit of 10K bytes, and 5000 + messages. A user may assign a Flow Stop Threshold based on a total + message count of 4000 messages. They may also assigne a Flow Stop + Threshold of 8K bytes. The queue's flow control state transitions to + "ON" if either threshold is crossed: (total-msgs greater-than 4000 OR total-bytes + greater-than 8K). + </para> + + <para> + Assume the user has assigned Flow Resume threshold's of 3000 messages and + 6K bytes. Then the queue's flow control will remain active until both + thresholds are satified: (total-msg less-than 3000 AND total-bytes less-than 6K). + </para> + </section> + + <para> + The Broker enforces flow control by delaying the completion of the + Message.Transfer command that causes a message to be delivered to a queue + with active flow control. The completion of the Message.Transfer command + is held off until flow control state transitions to "OFF" for all queues + that are a destination for that command. + </para> + + <para> + A message producing client is permitted to have a finite number of + commands pending completion. When the total number of these outstanding + commands reaches the limit, the client must not issue further commands + until one or more of the outstanding commands have completed. This + window of outstanding commands is considered the sender's "capacity". + This allows any given producer to have a "capacity's" worth of messages + blocked due to flow control before the sender must stop sending further + messages. + </para> + + <para> + This capacity window must be considered when determining a suitable + flow stop threshold for a given queue, as a producer may send its + capacity worth of messages _after_ a queue has reached the flow stop + threshold. Therefore, a flow stop threshould should be set such that + the queue can accomodate more messages without overflowing. + </para> + + <para> + For example, assume two clients, C1 and C2, are producing messages to + one particular destination queue. Assume client C1 has a configured + capacity of 50 messages, and client C2's capacity is 15 messages. In + this example, assume C1 and C2 are the only clients queuing messages to + a given queue. If this queue has a Flow Stop Threshold of 100 + messages, then, worst-case, the queue may receive up to 165 messages + before clients C1 and C2 are blocked from sending further messages. + This is due to the fact that the queue will enable flow control on + receipt of its 101'st message - preventing the completion of the + Message.Transfer command that carried the 101'st message. However, C1 + and C2 are allowed to have a total of 65 (50 for C1 and 15 for C2) + messages pending completion of Message.Transfer before they will stop + producing messages. Thus, up to 65 messages may be enqueued beyond the + flow stop threshold before the producers will be blocked. + </para> + </section> + + <section role="h2" id="producerflowcontrol-UserInterface"> + <title> + User Interface + </title> + + <para> + By default, the C++ broker assigns a queue's flow stop and flow resume + thresholds when the queue is created. The C++ broker also allows the + user to manually specify the flow control thresholds on a per queue + basis. + </para> + + <para> + However, queues that have been configured with a Limit Policy of type + RING or RING-STRICT do NOT have queue flow thresholds enabled by + default. The nature of a RING queue defines its behavior when its + capacity is reach: replace the oldest message. + </para> + + <para> + The flow control state of a queue can be determined by the "flowState" + boolean in the queue's QMF management object. The queue's management + object also contains a counter that increments each time flow control + becomes active for the queue. + </para> + + <para> + The broker applies a threshold ratio to compute a queue's default flow + control configuration. These thresholds are expressed as a percentage + of a queue's maximum capacity. There is one value for determining the + stop threshold, and another for determining the resume threshold. The + user may configure these percentages using the following broker + configuration options: + </para> + + <programlisting> + --default-flow-stop-threshold ("Queue capacity level at which flow control is activated.") + --default-flow-resume-threshold ("Queue capacity level at which flow control is de-activated.") + </programlisting> + + <para> + For example: + </para> + + <programlisting> + qpidd --default-flow-stop-threshold=90 --default-flow-resume-threshold=75 + </programlisting> + + <para> + Sets the default flow stop threshold to 90% of a queue's maximum + capacity and the flow resume threshold to 75% of the maximum capacity. + If a queue is created with a default-queue-limit of 10000 bytes, then + the default flow stop threshold would be 90% of 10000 = 9000 bytes and + the flow resume threshold would be 75% of 10000 = 7500. The same + computation is performed should a queue be created with a maximum size + expressed as a message count instead of a byte count. + </para> + + <para> + If not overridden by the user, the value of the + default-flow-stop-threshold is 80% and the value of the + default-flow-resume-threshold is 70%. + </para> + + <para> + The user may disable default queue flow control broker-wide by + specifying the value 0 for both of these configuration options. Note + that flow control may still be applied manually on a per-queue basis in + this case. + </para> + + <para> + The user may manually set the flow thresholds when creating a queue. + The following options may be provided when adding a queue using the + <command>qpid-config</command> command line tool: + </para> + + <programlisting> + --flow-stop-size=<replaceable>N</replaceable> Sets the queue's flow stop threshold to <replaceable>N</replaceable> total bytes. + --flow-resume-size=<replaceable>N</replaceable> Sets the queue's flow resume threshold to <replaceable>N</replaceable> total bytes. + --flow-stop-count=<replaceable>N</replaceable> Sets the queue's flow stop threshold to <replaceable>N</replaceable> total messages. + --flow-resume-count=<replaceable>N</replaceable> Sets the queue's flow resume threshold to <replaceable>N</replaceable> total messages. + </programlisting> + + <para> + Flow thresholds may also be specified in the + <command>queue.declare</command> method, via the + <command>arguments</command> parameter map. The following keys can be + provided in the arguments map for setting flow thresholds: + </para> + + <table> + <title>Queue Declare Method Flow Control Arguments</title> + <tgroup cols="2"> + <thead> + <row> + <entry>Key</entry> + <entry>Value</entry> + </row> + </thead> + <tbody> + <row> + <entry>qpid.flow_stop_size</entry> + <entry>integer - queue's flow stop threshold value in bytes</entry> + </row> + <row> + <entry>qpid.flow_resume_size</entry> + <entry>integer - queue's flow resume threshold value in bytes</entry> + </row> + <row> + <entry>qpid.flow_stop_count</entry> + <entry>integer - queue's flow stop threshold value as a message count</entry> + </row> + <row> + <entry>qpid.flow_resume_count</entry> + <entry>integer - queue's flow resume threshold value as a message count</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + The user may disable flow control on a per queue basis by setting + the flow-stop-size and flow-stop-count to zero for the queue. + </para> + + <para> + The current state of flow control for a given queue can be + determined by the "flowStopped" statistic. This statistic is + available in the queue's QMF management object. The value of + flowStopped is True when the queue's capacity has exceeded the + flow stop threshold. The value of flowStopped is False when the + queue is no longer blocking due to flow control. + </para> + + <para> + A queue will also track the number of times flow control has been + activated. The "flowStoppedCount" statistic is incremented each time + the queue's capacity exceeds a flow stop threshold. This statistic can + be used to monitor the activity of flow control for any given queue + over time. + </para> + + <table> + <title>Flow Control Statistics available in Queue's QMF Class</title> + <tgroup cols="3"> + <thead> + <row> + <entry>Statistic Name</entry> + <entry>Type</entry> + <entry>Description</entry> + </row> + </thead> + <tbody> + <row> + <entry>flowStopped</entry> + <entry>Boolean</entry> + <entry>If true, producers are blocked by flow control.</entry> + </row> + <row> + <entry>flowStoppedCount</entry> + <entry>count32</entry> + <entry>Number of times flow control was activated for this queue</entry> + </row> + </tbody> + </tgroup> + </table> + </section> + <!--h2--> + </section> diff --git a/qpid/cpp/src/tests/qrsh_utils/3_kill_it b/qpid/dotnet/Consumer/Program.cs index afc7a03c9d..d9a6224a42 100755..100644 --- a/qpid/cpp/src/tests/qrsh_utils/3_kill_it +++ b/qpid/dotnet/Consumer/Program.cs @@ -9,7 +9,7 @@ * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -17,11 +17,14 @@ * 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 - + */ +namespace Consumer +{ + class Program + { + static void Main(string[] args) + { + Apache.Qpid.Client.Tests.interop.Consumer.Main(args); + } + } +} diff --git a/qpid/dotnet/Consumer/default.build b/qpid/dotnet/Consumer/default.build new file mode 100644 index 0000000000..5b1e3166c5 --- /dev/null +++ b/qpid/dotnet/Consumer/default.build @@ -0,0 +1,47 @@ +<?xml version="1.0"?> +<!-- + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + +--> + +<project name="Consumer" default="build"> + <!-- + Properties that come from master build file + - build.dir: root directory for build + - build.debug: true if building debug release + - build.defines: variables to define during build + --> + + <target name="build"> + <csc target="exe" + define="${build.defines}" + debug="${build.debug}" + unsafe="true" + output="${build.dir}/${project::get-name()}.exe"> + + <sources> + <include name="**/*.cs" /> + </sources> + <references> + <include name="${build.dir}\Apache.Qpid.Client.Tests.dll"/> + </references> + </csc> + </target> +</project> + diff --git a/qpid/cpp/src/tests/qrsh_utils/10_all b/qpid/dotnet/Producer/Program.cs index 7b486ea672..83dbf597ed 100755..100644 --- a/qpid/cpp/src/tests/qrsh_utils/10_all +++ b/qpid/dotnet/Producer/Program.cs @@ -9,7 +9,7 @@ * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -17,14 +17,14 @@ * 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 - - - - + */ +namespace Producer +{ + class Program + { + static void Main(string[] args) + { + Apache.Qpid.Client.Tests.interop.Producer.Main(args); + } + } +} diff --git a/qpid/dotnet/Producer/default.build b/qpid/dotnet/Producer/default.build new file mode 100644 index 0000000000..72d2a3fa8d --- /dev/null +++ b/qpid/dotnet/Producer/default.build @@ -0,0 +1,47 @@ +<?xml version="1.0"?> +<!-- + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + +--> + +<project name="Producer" default="build"> + <!-- + Properties that come from master build file + - build.dir: root directory for build + - build.debug: true if building debug release + - build.defines: variables to define during build + --> + + <target name="build"> + <csc target="exe" + define="${build.defines}" + debug="${build.debug}" + unsafe="true" + output="${build.dir}/${project::get-name()}.exe"> + + <sources> + <include name="**/*.cs" /> + </sources> + <references> + <include name="${build.dir}\Apache.Qpid.Client.Tests.dll"/> + </references> + </csc> + </target> +</project> + diff --git a/qpid/cpp/src/tests/qrsh_utils/1_remote_run b/qpid/dotnet/Program.cs index 5b9b307bba..d9a6224a42 100755..100644 --- a/qpid/cpp/src/tests/qrsh_utils/1_remote_run +++ b/qpid/dotnet/Program.cs @@ -9,7 +9,7 @@ * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -17,10 +17,14 @@ * 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 + */ +namespace Consumer +{ + class Program + { + static void Main(string[] args) + { + Apache.Qpid.Client.Tests.interop.Consumer.Main(args); + } + } +} diff --git a/qpid/dotnet/Qpid.Client.Tests/interop/Consumer.cs b/qpid/dotnet/Qpid.Client.Tests/interop/Consumer.cs new file mode 100644 index 0000000000..d60514ae4c --- /dev/null +++ b/qpid/dotnet/Qpid.Client.Tests/interop/Consumer.cs @@ -0,0 +1,56 @@ +/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+using System;
+using Apache.Qpid.Client;
+using Apache.Qpid.Messaging;
+
+namespace Apache.Qpid.Client.Tests.interop
+{
+ public class Consumer
+ {
+ public static void Main(string[] args)
+ {
+ try
+ {
+ const string connectionUrl = @"amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672'";
+ const string queueName = @"test-queue";
+
+ var connectionInfo = QpidConnectionInfo.FromUrl(connectionUrl);
+ var connection = new AMQConnection(connectionInfo);
+ var channel = connection.CreateChannel(false, AcknowledgeMode.AutoAcknowledge, 1);
+
+ channel.DeclareQueue(queueName, false, true, true);
+ channel.Bind(queueName, ExchangeNameDefaults.DIRECT, queueName);
+ IMessageConsumer consumer = channel.CreateConsumerBuilder(queueName) .Create();
+ connection.Start();
+
+ ITextMessage message = (ITextMessage) consumer.Receive();
+ Console.WriteLine("Got: " + message.Text);
+ connection.Dispose();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ }
+ }
+ }
+}
diff --git a/qpid/dotnet/Qpid.Client.Tests/interop/Producer.cs b/qpid/dotnet/Qpid.Client.Tests/interop/Producer.cs new file mode 100644 index 0000000000..d775080fc3 --- /dev/null +++ b/qpid/dotnet/Qpid.Client.Tests/interop/Producer.cs @@ -0,0 +1,55 @@ +/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+using System;
+using Apache.Qpid.Client;
+using Apache.Qpid.Messaging;
+
+namespace Apache.Qpid.Client.Tests.interop
+{
+ public class Producer
+ {
+ public static void Main(string[] args)
+ {
+ try
+ {
+ const string connectionUrl = @"amqp://guest:guest@clientid/test?brokerlist='tcp://localhost:5672'";
+ const string queueName = @"test-queue";
+
+ var connectionInfo = QpidConnectionInfo.FromUrl(connectionUrl);
+ var connection = new AMQConnection(connectionInfo);
+ var channel = connection.CreateChannel(false, AcknowledgeMode.AutoAcknowledge);
+ var publisher = channel.CreatePublisherBuilder()
+ .WithExchangeName(ExchangeNameDefaults.DIRECT)
+ .WithRoutingKey(queueName)
+ .Create();
+ IMessage message = channel.CreateTextMessage("0123456789");
+ publisher.Send(message);
+ Console.WriteLine("Sent message");
+ connection.Dispose();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ }
+ }
+ }
+}
diff --git a/qpid/dotnet/Qpid.Client.Tests/interop/TopicListener.cs b/qpid/dotnet/Qpid.Client.Tests/interop/TopicListener.cs index b355abb28d..e5daa64a89 100644 --- a/qpid/dotnet/Qpid.Client.Tests/interop/TopicListener.cs +++ b/qpid/dotnet/Qpid.Client.Tests/interop/TopicListener.cs @@ -19,6 +19,7 @@ * */ using System; +using System.Threading; using log4net; using Apache.Qpid.Messaging; using Apache.Qpid.Client.Qms; @@ -47,6 +48,12 @@ namespace Apache.Qpid.Client.Tests.interop /// <summary> Holds the producer to send report messages on. </summary> private IMessagePublisher publisher; + /// <summary> A monitor used to wait for shutdown. </summary> + private AutoResetEvent shutdownReceivedEvt = new AutoResetEvent(false); + + /// <summary> Holds the default test timeout for communications . </summary> + const int TIMEOUT = 60000; + /// <summary> Holds a flag to indicate that a timer has begun on the first message. Reset when report is sent. </summary> */ private bool init; @@ -85,6 +92,15 @@ namespace Apache.Qpid.Client.Tests.interop connection.Start(); Console.WriteLine("Waiting for messages..."); + + if (shutdownReceivedEvt.WaitOne(TIMEOUT, true)) + { + Console.WriteLine("Shutting down - shut down message was received"); + } + else + { + Console.WriteLine("Shutting down - timeout elapsed"); + } } public static void Main(String[] argv) @@ -185,6 +201,8 @@ namespace Apache.Qpid.Client.Tests.interop connection.Stop(); channel.Dispose(); connection.Dispose(); + + shutdownReceivedEvt.Set(); } /// <summary> Sends the report message to the response location. </summary> diff --git a/qpid/dotnet/README.txt b/qpid/dotnet/README.txt index 0199ad6410..70830a625c 100644 --- a/qpid/dotnet/README.txt +++ b/qpid/dotnet/README.txt @@ -32,6 +32,7 @@ If using nant, set up PATH to include Nant.exe, e.g.: If using msbuild, it is recommended to use a "Visual Studio Command Prompt" + Building ======== @@ -49,6 +50,27 @@ If you are using nant, the script build-nant.bat contains standard arguments tha To build for Mono on Linux (to bin/mono-2.0) the build-mono shell script is provided. +Running the Examples +==================== + +The tree contains two paired examples that demonstrate some of the features of the library: +Producer/Consumer and TopicPublisher/TopicListener. + +To run on Mono on Linux (cd to bin/mono-2.0) and execute + +mono Consumer.exe (in one window , and +mono Producer.exe in another window) + +or: + +mono TopicListener.exe (in one window , and +mono TopicPublisher.exe in another window) + +Both examples assume you have a broker running on localhost:5672. You must run Consumer/TopicListener +before Producer/TopicPublisher in order that queue is created before the first message is sent. + +The source code for the examples is in directory Qpid.Client.Tests/interop. + Releasing ========= diff --git a/qpid/dotnet/build-mono b/qpid/dotnet/build-mono index 2bb6147b53..71d94ad268 100755 --- a/qpid/dotnet/build-mono +++ b/qpid/dotnet/build-mono @@ -18,4 +18,4 @@ # under the License. # -nant -t:mono-2.0 +nant -t:mono-2.0 "$@" diff --git a/qpid/dotnet/build-nant-release b/qpid/dotnet/build-nant-release index 611a1efe08..a6d73dc40a 100755 --- a/qpid/dotnet/build-nant-release +++ b/qpid/dotnet/build-nant-release @@ -1,24 +1,3 @@ -# -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT 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 # # diff --git a/qpid/dotnet/default.build b/qpid/dotnet/default.build index 4cd38e90ff..b038307623 100644 --- a/qpid/dotnet/default.build +++ b/qpid/dotnet/default.build @@ -62,9 +62,12 @@ <!-- Other test or utility assemblies. --> <fileset id='other.builds'> + <include name="TestClient/default.build" /> + <!-- Examples --> <include name="TopicListener/default.build" /> <include name="TopicPublisher/default.build" /> - <include name="TestClient/default.build" /> + <include name="Consumer/default.build" /> + <include name="Producer/default.build" /> </fileset> <!-- Prepare environment for a debug build. --> diff --git a/qpid/extras/qmf/setup.py b/qpid/extras/qmf/setup.py index 2e3ec7a38e..e7bf4b9717 100755 --- a/qpid/extras/qmf/setup.py +++ b/qpid/extras/qmf/setup.py @@ -20,10 +20,10 @@ from distutils.core import setup setup(name="qpid-qmf", - version="0.9", + version="0.11", author="Apache Qpid", author_email="dev@qpid.apache.org", - packages=["qmf", "qmf2", "qmf2.tests"], + packages=["qmf"], package_dir={"": "src/py"}, url="http://qpid.apache.org/", license="Apache Software License", diff --git a/qpid/extras/qmf/src/py/qmf/console.py b/qpid/extras/qmf/src/py/qmf/console.py index 6f4c11ae15..ecb0e1d9d0 100644 --- a/qpid/extras/qmf/src/py/qmf/console.py +++ b/qpid/extras/qmf/src/py/qmf/console.py @@ -1211,11 +1211,22 @@ class Session: try: agentName = ah["qmf.agent"] values = content["_values"] - timestamp = values["_timestamp"] - interval = values["_heartbeat_interval"] + + if '_timestamp' in values: + timestamp = values["_timestamp"] + else: + timestamp = values['timestamp'] + + if '_heartbeat_interval' in values: + interval = values['_heartbeat_interval'] + else: + interval = values['heartbeat_interval'] + epoch = 0 if '_epoch' in values: epoch = values['_epoch'] + elif 'epoch' in values: + epoch = values['epoch'] except Exception,e: return @@ -2416,7 +2427,7 @@ class Broker(Thread): if uid.__class__ == tuple and len(uid) == 2: self.saslUser = uid[1] else: - self.saslUser = None + self.saslUser = self.authUser # prevent topic queues from filling up (and causing the agents to # disconnect) by discarding the oldest queued messages when full. diff --git a/qpid/extras/qmf/src/py/qmf2/__init__.py b/qpid/extras/qmf/src/py/qmf2-prototype/__init__.py index 31d5a2ef58..31d5a2ef58 100644 --- a/qpid/extras/qmf/src/py/qmf2/__init__.py +++ b/qpid/extras/qmf/src/py/qmf2-prototype/__init__.py diff --git a/qpid/extras/qmf/src/py/qmf2/agent.py b/qpid/extras/qmf/src/py/qmf2-prototype/agent.py index 4ec00bd288..4ec00bd288 100644 --- a/qpid/extras/qmf/src/py/qmf2/agent.py +++ b/qpid/extras/qmf/src/py/qmf2-prototype/agent.py diff --git a/qpid/extras/qmf/src/py/qmf2/common.py b/qpid/extras/qmf/src/py/qmf2-prototype/common.py index 2e5367f54f..2e5367f54f 100644 --- a/qpid/extras/qmf/src/py/qmf2/common.py +++ b/qpid/extras/qmf/src/py/qmf2-prototype/common.py diff --git a/qpid/extras/qmf/src/py/qmf2/console.py b/qpid/extras/qmf/src/py/qmf2-prototype/console.py index 9227835b3f..9227835b3f 100644 --- a/qpid/extras/qmf/src/py/qmf2/console.py +++ b/qpid/extras/qmf/src/py/qmf2-prototype/console.py diff --git a/qpid/extras/qmf/src/py/qmf2/tests/__init__.py b/qpid/extras/qmf/src/py/qmf2-prototype/tests/__init__.py index eff9357e1f..eff9357e1f 100644 --- a/qpid/extras/qmf/src/py/qmf2/tests/__init__.py +++ b/qpid/extras/qmf/src/py/qmf2-prototype/tests/__init__.py diff --git a/qpid/extras/qmf/src/py/qmf2/tests/agent_discovery.py b/qpid/extras/qmf/src/py/qmf2-prototype/tests/agent_discovery.py index 2c20794aaa..2c20794aaa 100644 --- a/qpid/extras/qmf/src/py/qmf2/tests/agent_discovery.py +++ b/qpid/extras/qmf/src/py/qmf2-prototype/tests/agent_discovery.py diff --git a/qpid/extras/qmf/src/py/qmf2/tests/agent_test.py b/qpid/extras/qmf/src/py/qmf2-prototype/tests/agent_test.py index 14d8ada197..14d8ada197 100644 --- a/qpid/extras/qmf/src/py/qmf2/tests/agent_test.py +++ b/qpid/extras/qmf/src/py/qmf2-prototype/tests/agent_test.py diff --git a/qpid/extras/qmf/src/py/qmf2/tests/async_method.py b/qpid/extras/qmf/src/py/qmf2-prototype/tests/async_method.py index 2339fc71a9..2339fc71a9 100644 --- a/qpid/extras/qmf/src/py/qmf2/tests/async_method.py +++ b/qpid/extras/qmf/src/py/qmf2-prototype/tests/async_method.py diff --git a/qpid/extras/qmf/src/py/qmf2/tests/async_query.py b/qpid/extras/qmf/src/py/qmf2-prototype/tests/async_query.py index b1c01611f7..b1c01611f7 100644 --- a/qpid/extras/qmf/src/py/qmf2/tests/async_query.py +++ b/qpid/extras/qmf/src/py/qmf2-prototype/tests/async_query.py diff --git a/qpid/extras/qmf/src/py/qmf2/tests/basic_method.py b/qpid/extras/qmf/src/py/qmf2-prototype/tests/basic_method.py index 8d038bc4c8..8d038bc4c8 100644 --- a/qpid/extras/qmf/src/py/qmf2/tests/basic_method.py +++ b/qpid/extras/qmf/src/py/qmf2-prototype/tests/basic_method.py diff --git a/qpid/extras/qmf/src/py/qmf2/tests/basic_query.py b/qpid/extras/qmf/src/py/qmf2-prototype/tests/basic_query.py index 9f5dda6d54..9f5dda6d54 100644 --- a/qpid/extras/qmf/src/py/qmf2/tests/basic_query.py +++ b/qpid/extras/qmf/src/py/qmf2-prototype/tests/basic_query.py diff --git a/qpid/extras/qmf/src/py/qmf2/tests/console_test.py b/qpid/extras/qmf/src/py/qmf2-prototype/tests/console_test.py index ac0e064f20..ac0e064f20 100644 --- a/qpid/extras/qmf/src/py/qmf2/tests/console_test.py +++ b/qpid/extras/qmf/src/py/qmf2-prototype/tests/console_test.py diff --git a/qpid/extras/qmf/src/py/qmf2/tests/events.py b/qpid/extras/qmf/src/py/qmf2-prototype/tests/events.py index 624c9b3823..624c9b3823 100644 --- a/qpid/extras/qmf/src/py/qmf2/tests/events.py +++ b/qpid/extras/qmf/src/py/qmf2-prototype/tests/events.py diff --git a/qpid/extras/qmf/src/py/qmf2/tests/multi_response.py b/qpid/extras/qmf/src/py/qmf2-prototype/tests/multi_response.py index 991fa0114e..991fa0114e 100644 --- a/qpid/extras/qmf/src/py/qmf2/tests/multi_response.py +++ b/qpid/extras/qmf/src/py/qmf2-prototype/tests/multi_response.py diff --git a/qpid/extras/qmf/src/py/qmf2/tests/obj_gets.py b/qpid/extras/qmf/src/py/qmf2-prototype/tests/obj_gets.py index 695b096973..695b096973 100644 --- a/qpid/extras/qmf/src/py/qmf2/tests/obj_gets.py +++ b/qpid/extras/qmf/src/py/qmf2-prototype/tests/obj_gets.py diff --git a/qpid/extras/qmf/src/py/qmf2/tests/subscriptions.py b/qpid/extras/qmf/src/py/qmf2-prototype/tests/subscriptions.py index 5c39af4b32..5c39af4b32 100644 --- a/qpid/extras/qmf/src/py/qmf2/tests/subscriptions.py +++ b/qpid/extras/qmf/src/py/qmf2-prototype/tests/subscriptions.py diff --git a/qpid/java/broker-plugins/experimental/shutdown/src/main/java/shutdown.bnd b/qpid/java/broker-plugins/experimental/shutdown/src/main/java/shutdown.bnd index 6e005f5bdb..f49578ba8c 100755 --- a/qpid/java/broker-plugins/experimental/shutdown/src/main/java/shutdown.bnd +++ b/qpid/java/broker-plugins/experimental/shutdown/src/main/java/shutdown.bnd @@ -17,7 +17,7 @@ # under the License. # -ver: 0.9.0 +ver: 0.11.0 Bundle-SymbolicName: qpid-shutdown-plugin Bundle-Version: ${ver} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java b/qpid/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java index a6f319cb1f..5192d5be6f 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/qmf/QMFService.java @@ -1096,6 +1096,11 @@ public class QMFService implements ConfigStore.ConfigEventListener, Closeable return Boolean.FALSE; } + public Long getFlowStoppedCount() + { + return 0L; + } + public BrokerSchema.QueueClass.PurgeMethodResponseCommand purge(final BrokerSchema.QueueClass.PurgeMethodResponseCommandFactory factory, final Long request) { 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 a612f280d6..d1ea5dba69 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 @@ -327,4 +327,74 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr { return getObjectNameForSingleInstanceMBean(); } + + public void resetStatistics() throws Exception + { + getVirtualHost().resetStatistics(); + } + + public double getPeakMessageDeliveryRate() + { + return getVirtualHost().getMessageDeliveryStatistics().getPeak(); + } + + public double getPeakDataDeliveryRate() + { + return getVirtualHost().getDataDeliveryStatistics().getPeak(); + } + + public double getMessageDeliveryRate() + { + return getVirtualHost().getMessageDeliveryStatistics().getRate(); + } + + public double getDataDeliveryRate() + { + return getVirtualHost().getDataDeliveryStatistics().getRate(); + } + + public long getTotalMessagesDelivered() + { + return getVirtualHost().getMessageDeliveryStatistics().getTotal(); + } + + public long getTotalDataDelivered() + { + return getVirtualHost().getDataDeliveryStatistics().getTotal(); + } + + public double getPeakMessageReceiptRate() + { + return getVirtualHost().getMessageReceiptStatistics().getPeak(); + } + + public double getPeakDataReceiptRate() + { + return getVirtualHost().getDataReceiptStatistics().getPeak(); + } + + public double getMessageReceiptRate() + { + return getVirtualHost().getMessageReceiptStatistics().getRate(); + } + + public double getDataReceiptRate() + { + return getVirtualHost().getDataReceiptStatistics().getRate(); + } + + public long getTotalMessagesReceived() + { + return getVirtualHost().getMessageReceiptStatistics().getTotal(); + } + + public long getTotalDataReceived() + { + return getVirtualHost().getDataReceiptStatistics().getTotal(); + } + + public boolean isStatisticsEnabled() + { + return getVirtualHost().isStatisticsEnabled(); + } } 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 4f86c82578..dd3046cd01 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 @@ -22,6 +22,7 @@ package org.apache.qpid.server; import org.apache.log4j.Logger; +import org.apache.qpid.AMQConnectionException; import org.apache.qpid.AMQException; import org.apache.qpid.AMQSecurityException; import org.apache.qpid.framing.AMQMethodBody; @@ -141,6 +142,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel private final AtomicLong _txnCommits = new AtomicLong(0); private final AtomicLong _txnRejects = new AtomicLong(0); private final AtomicLong _txnCount = new AtomicLong(0); + private final AtomicLong _txnUpdateTime = new AtomicLong(0); private final AMQProtocolSession _session; private AtomicBoolean _closing = new AtomicBoolean(false); @@ -200,6 +202,11 @@ public class AMQChannel implements SessionConfig, AMQSessionModel return !(_transaction instanceof AutoCommitTransaction); } + public boolean inTransaction() + { + return isTransactional() && _txnUpdateTime.get() > 0 && _transaction.getTransactionStartTime() > 0; + } + private void incrementOutstandingTxnsIfNecessary() { if(isTransactional()) @@ -295,7 +302,6 @@ public class AMQChannel implements SessionConfig, AMQSessionModel }); deliverCurrentMessageIfComplete(); - } } @@ -333,11 +339,15 @@ public class AMQChannel implements SessionConfig, AMQSessionModel { _transaction.enqueue(destinationQueues, _currentMessage, new MessageDeliveryAction(_currentMessage, destinationQueues, isTransactional())); incrementOutstandingTxnsIfNecessary(); + updateTransactionalActivity(); } } } finally { + long bodySize = _currentMessage.getSize(); + long timestamp = ((BasicContentHeaderProperties) _currentMessage.getContentHeader().properties).getTimestamp(); + _session.registerMessageReceived(bodySize, timestamp); _currentMessage = null; } } @@ -794,6 +804,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel { Collection<QueueEntry> ackedMessages = getAckedMessages(deliveryTag, multiple); _transaction.dequeue(ackedMessages, new MessageAcknowledgeAction(ackedMessages)); + updateTransactionalActivity(); } private Collection<QueueEntry> getAckedMessages(long deliveryTag, boolean multiple) @@ -968,6 +979,17 @@ public class AMQChannel implements SessionConfig, AMQSessionModel } + /** + * Update last transaction activity timestamp + */ + private void updateTransactionalActivity() + { + if (isTransactional()) + { + _txnUpdateTime.set(System.currentTimeMillis()); + } + } + public String toString() { return "["+_session.toString()+":"+_channelId+"]"; @@ -1018,6 +1040,7 @@ public class AMQChannel implements SessionConfig, AMQSessionModel { getProtocolSession().getProtocolOutputConverter().writeDeliver(entry, getChannelId(), deliveryTag, sub.getConsumerTag()); + _session.registerMessageDelivered(entry.getMessage().getSize()); } }; @@ -1407,4 +1430,36 @@ public class AMQChannel implements SessionConfig, AMQSessionModel { _session.mgmtCloseChannel(_channelId); } + + public void checkTransactionStatus(long openWarn, long openClose, long idleWarn, long idleClose) throws AMQException + { + if (inTransaction()) + { + long currentTime = System.currentTimeMillis(); + long openTime = currentTime - _transaction.getTransactionStartTime(); + long idleTime = currentTime - _txnUpdateTime.get(); + + // Log a warning on idle or open transactions + if (idleWarn > 0L && idleTime > idleWarn) + { + CurrentActor.get().message(_logSubject, ChannelMessages.IDLE_TXN(idleTime)); + _logger.warn("IDLE TRANSACTION ALERT " + _logSubject.toString() + " " + idleTime + " ms"); + } + else if (openWarn > 0L && openTime > openWarn) + { + CurrentActor.get().message(_logSubject, ChannelMessages.OPEN_TXN(openTime)); + _logger.warn("OPEN TRANSACTION ALERT " + _logSubject.toString() + " " + openTime + " ms"); + } + + // Close connection for idle or open transactions that have timed out + if (idleClose > 0L && idleTime > idleClose) + { + getConnectionModel().closeSession(this, AMQConstant.RESOURCE_ERROR, "Idle transaction timed out"); + } + else if (openClose > 0L && openTime > openClose) + { + getConnectionModel().closeSession(this, AMQConstant.RESOURCE_ERROR, "Open transaction timed out"); + } + } + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java index 71cf17ed60..9d3c4dd2e8 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java @@ -320,8 +320,7 @@ public class Main ConfigurationManagementMBean configMBean = new ConfigurationManagementMBean(); configMBean.register(); - ServerInformationMBean sysInfoMBean = - new ServerInformationMBean(QpidProperties.getBuildVersion(), QpidProperties.getReleaseVersion()); + ServerInformationMBean sysInfoMBean = new ServerInformationMBean(config); sysInfoMBean.register(); 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 7197ec8cdc..43be0611a5 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 @@ -767,6 +767,36 @@ public class ServerConfiguration extends ConfigurationPlugin implements SignalHa DEFAULT_HOUSEKEEPING_PERIOD)); } + public long getStatisticsSamplePeriod() + { + return getConfig().getLong("statistics.sample.period", 5000L); + } + + public boolean isStatisticsGenerationBrokerEnabled() + { + return getConfig().getBoolean("statistics.generation.broker", false); + } + + public boolean isStatisticsGenerationVirtualhostsEnabled() + { + return getConfig().getBoolean("statistics.generation.virtualhosts", false); + } + + public boolean isStatisticsGenerationConnectionsEnabled() + { + return getConfig().getBoolean("statistics.generation.connections", false); + } + + public long getStatisticsReportingPeriod() + { + return getConfig().getLong("statistics.reporting.period", 0L); + } + + public boolean isStatisticsReportResetEnabled() + { + return getConfig().getBoolean("statistics.reporting.reset", false); + } + public NetworkDriverConfiguration getNetworkConfiguration() { return new NetworkDriverConfiguration() diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java index d9d7083543..48f2d776bb 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java @@ -313,4 +313,24 @@ public class VirtualHostConfiguration extends ConfigurationPlugin { return getIntValue("housekeeping.poolSize", Runtime.getRuntime().availableProcessors()); } + + public long getTransactionTimeoutOpenWarn() + { + return getLongValue("transactionTimeout.openWarn", 0L); + } + + public long getTransactionTimeoutOpenClose() + { + return getLongValue("transactionTimeout.openClose", 0L); + } + + public long getTransactionTimeoutIdleWarn() + { + return getLongValue("transactionTimeout.idleWarn", 0L); + } + + public long getTransactionTimeoutIdleClose() + { + return getLongValue("transactionTimeout.idleClose", 0L); + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java index bac751e0c8..c06305ee4e 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/ConnectionRegistry.java @@ -20,19 +20,19 @@ */ package org.apache.qpid.server.connection; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.apache.log4j.Logger; -import org.apache.qpid.AMQConnectionException; import org.apache.qpid.AMQException; import org.apache.qpid.common.Closeable; import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.server.protocol.AMQProtocolSession; +import org.apache.qpid.server.protocol.AMQConnectionModel; public class ConnectionRegistry implements IConnectionRegistry, Closeable { - private List<AMQProtocolSession> _registry = new CopyOnWriteArrayList<AMQProtocolSession>(); + private List<AMQConnectionModel> _registry = new CopyOnWriteArrayList<AMQConnectionModel>(); private Logger _logger = Logger.getLogger(ConnectionRegistry.class); @@ -40,44 +40,42 @@ public class ConnectionRegistry implements IConnectionRegistry, Closeable { // None required } - - public void expireClosedChannels() - { - for (AMQProtocolSession connection : _registry) - { - connection.closeIfLingeringClosedChannels(); - } - } /** Close all of the currently open connections. */ public void close() { while (!_registry.isEmpty()) { - AMQProtocolSession connection = _registry.get(0); - - try - { - connection.closeConnection(0, new AMQConnectionException(AMQConstant.INTERNAL_ERROR, "Broker is shutting down", - 0, 0, - connection.getProtocolOutputConverter().getProtocolMajorVersion(), - connection.getProtocolOutputConverter().getProtocolMinorVersion(), - (Throwable) null), true); - } - catch (AMQException e) - { - _logger.warn("Error closing connection:" + e.getMessage()); - } + AMQConnectionModel connection = _registry.get(0); + closeConnection(connection, AMQConstant.INTERNAL_ERROR, "Broker is shutting down"); + } + } + + public void closeConnection(AMQConnectionModel connection, AMQConstant cause, String message) + { + try + { + connection.close(cause, message); + } + catch (AMQException e) + { + _logger.warn("Error closing connection:" + e.getMessage()); } } - public void registerConnection(AMQProtocolSession connnection) + public void registerConnection(AMQConnectionModel connnection) { _registry.add(connnection); } - public void deregisterConnection(AMQProtocolSession connnection) + public void deregisterConnection(AMQConnectionModel connnection) { _registry.remove(connnection); } + + @Override + public List<AMQConnectionModel> getConnections() + { + return new ArrayList<AMQConnectionModel>(_registry); + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java index 002269bbaa..b4f5bffa57 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/connection/IConnectionRegistry.java @@ -20,18 +20,23 @@ */ package org.apache.qpid.server.connection; -import org.apache.qpid.server.protocol.AMQProtocolSession; +import java.util.List; + import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.protocol.AMQConnectionModel; public interface IConnectionRegistry { - public void initialise(); public void close() throws AMQException; + + public void closeConnection(AMQConnectionModel connection, AMQConstant cause, String message); + + public List<AMQConnectionModel> getConnections(); - public void registerConnection(AMQProtocolSession connnection); - - public void deregisterConnection(AMQProtocolSession connnection); + public void registerConnection(AMQConnectionModel connnection); + public void deregisterConnection(AMQConnectionModel connnection); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchangeMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchangeMBean.java index 7aeff2561e..0f1b709475 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchangeMBean.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchangeMBean.java @@ -90,12 +90,12 @@ public abstract class AbstractExchangeMBean<T extends AbstractExchange> extends public String getObjectInstanceName() { - return _exchange.getNameShortString().toString(); + return ObjectName.quote(_exchange.getName()); } public String getName() { - return _exchange.getNameShortString().toString(); + return _exchange.getName(); } public String getExchangeType() diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/information/management/ServerInformationMBean.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/information/management/ServerInformationMBean.java index db2cc970b2..5e6a143d52 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/information/management/ServerInformationMBean.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/information/management/ServerInformationMBean.java @@ -22,9 +22,11 @@ package org.apache.qpid.server.information.management; import java.io.IOException; +import org.apache.qpid.common.QpidProperties; import org.apache.qpid.management.common.mbeans.ServerInformation; import org.apache.qpid.management.common.mbeans.annotations.MBeanDescription; import org.apache.qpid.server.management.AMQManagedObject; +import org.apache.qpid.server.registry.ApplicationRegistry; import javax.management.JMException; @@ -34,12 +36,15 @@ public class ServerInformationMBean extends AMQManagedObject implements ServerIn { private String buildVersion; private String productVersion; + private ApplicationRegistry registry; - public ServerInformationMBean(String buildVersion, String productVersion) throws JMException + public ServerInformationMBean(ApplicationRegistry applicationRegistry) throws JMException { super(ServerInformation.class, ServerInformation.TYPE); - this.buildVersion = buildVersion; - this.productVersion = productVersion; + + registry = applicationRegistry; + buildVersion = QpidProperties.getBuildVersion(); + productVersion = QpidProperties.getReleaseVersion(); } public String getObjectInstanceName() @@ -67,5 +72,75 @@ public class ServerInformationMBean extends AMQManagedObject implements ServerIn return productVersion; } + + public void resetStatistics() throws Exception + { + registry.resetStatistics(); + } + + public double getPeakMessageDeliveryRate() + { + return registry.getMessageDeliveryStatistics().getPeak(); + } + + public double getPeakDataDeliveryRate() + { + return registry.getDataDeliveryStatistics().getPeak(); + } + + public double getMessageDeliveryRate() + { + return registry.getMessageDeliveryStatistics().getRate(); + } + + public double getDataDeliveryRate() + { + return registry.getDataDeliveryStatistics().getRate(); + } + + public long getTotalMessagesDelivered() + { + return registry.getMessageDeliveryStatistics().getTotal(); + } + + public long getTotalDataDelivered() + { + return registry.getDataDeliveryStatistics().getTotal(); + } + + public double getPeakMessageReceiptRate() + { + return registry.getMessageReceiptStatistics().getPeak(); + } + + public double getPeakDataReceiptRate() + { + return registry.getDataReceiptStatistics().getPeak(); + } + + public double getMessageReceiptRate() + { + return registry.getMessageReceiptStatistics().getRate(); + } + + public double getDataReceiptRate() + { + return registry.getDataReceiptStatistics().getRate(); + } + + public long getTotalMessagesReceived() + { + return registry.getMessageReceiptStatistics().getTotal(); + } + + public long getTotalDataReceived() + { + return registry.getDataReceiptStatistics().getTotal(); + } + + public boolean isStatisticsEnabled() + { + return registry.isStatisticsEnabled(); + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Broker_logmessages.properties b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Broker_logmessages.properties index 6b83a7e7a5..5d1e85fe41 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Broker_logmessages.properties +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Broker_logmessages.properties @@ -32,4 +32,7 @@ STOPPED = BRK-1005 : Stopped # 0 - path CONFIG = BRK-1006 : Using configuration : {0} # 0 - path -LOG_CONFIG = BRK-1007 : Using logging configuration : {0}
\ No newline at end of file +LOG_CONFIG = BRK-1007 : Using logging configuration : {0} + +STATS_DATA = BRK-1008 : {0,choice,0#delivered|1#received} : {1,number,#.###} kB/s peak : {2,number,#} bytes total +STATS_MSGS = BRK-1009 : {0,choice,0#delivered|1#received} : {1,number,#.###} msg/s peak : {2,number,#} msgs total
\ No newline at end of file diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Channel_logmessages.properties b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Channel_logmessages.properties index 53bcd712f2..ed8c0d0ce9 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Channel_logmessages.properties +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Channel_logmessages.properties @@ -28,3 +28,7 @@ PREFETCH_SIZE = CHN-1004 : Prefetch Size (bytes) {0,number} : Count {1,number} # 0 - queue causing flow control FLOW_ENFORCED = CHN-1005 : Flow Control Enforced (Queue {0}) FLOW_REMOVED = CHN-1006 : Flow Control Removed +# Channel Transactions +# 0 - time in milliseconds +OPEN_TXN = CHN-1007 : Open Transaction : {0,number} ms +IDLE_TXN = CHN-1008 : Idle Transaction : {0,number} ms diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/VirtualHost_logmessages.properties b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/VirtualHost_logmessages.properties index 66bbefacb0..3e640c7929 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/VirtualHost_logmessages.properties +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/VirtualHost_logmessages.properties @@ -20,4 +20,7 @@ # # 0 - name CREATED = VHT-1001 : Created : {0} -CLOSED = VHT-1002 : Closed
\ No newline at end of file +CLOSED = VHT-1002 : Closed + +STATS_DATA = VHT-1003 : {0} : {1,choice,0#delivered|1#received} : {2,number,#.###} kB/s peak : {3,number,#} bytes total +STATS_MSGS = VHT-1004 : {0} : {1,choice,0#delivered|1#received} : {2,number,#.###} msg/s peak : {3,number,#} msgs total`
\ No newline at end of file diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java index 0a739af695..7924964fdf 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/management/DefaultManagedObject.java @@ -153,32 +153,4 @@ public abstract class DefaultManagedObject extends StandardMBean implements Mana return ""; } - protected static StringBuffer jmxEncode(StringBuffer jmxName, int attrPos) - { - for (int i = attrPos; i < jmxName.length(); i++) - { - if (jmxName.charAt(i) == ',') - { - jmxName.setCharAt(i, ';'); - } - else if (jmxName.charAt(i) == ':') - { - jmxName.setCharAt(i, '-'); - } - else if (jmxName.charAt(i) == '?' || - jmxName.charAt(i) == '*' || - jmxName.charAt(i) == '\\') - { - jmxName.insert(i, '\\'); - i++; - } - else if (jmxName.charAt(i) == '\n') - { - jmxName.insert(i, '\\'); - i++; - jmxName.setCharAt(i, 'n'); - } - } - return jmxName; - } } 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 964b5ed5a0..380f51e308 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 @@ -213,6 +213,20 @@ public class MBeanInvocationHandlerImpl implements InvocationHandler, Notificati ObjectName object = (ObjectName) args[0]; String vhost = object.getKeyProperty("VirtualHost"); + if(vhost != null) + { + try + { + //if the name is quoted in the ObjectName, unquote it + vhost = ObjectName.unquote(vhost); + } + catch(IllegalArgumentException e) + { + //ignore, this just means the name is not quoted + //and can be left unchanged + } + } + return vhost; } return null; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java index a6bab017a1..4e8d64a136 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/plugins/PluginManager.java @@ -63,7 +63,7 @@ public class PluginManager implements Closeable private static final Logger _logger = Logger.getLogger(PluginManager.class); private static final int FELIX_STOP_TIMEOUT = 30000; - private static final String QPID_VER_SUFFIX = "version=0.9,"; + private static final String QPID_VER_SUFFIX = "version=0.11,"; private Framework _felix; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQConnectionModel.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQConnectionModel.java index bcda385f64..061ebf50cd 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQConnectionModel.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQConnectionModel.java @@ -20,14 +20,35 @@ */ package org.apache.qpid.server.protocol; -import org.apache.qpid.protocol.AMQConstant; +import java.util.List; +import java.util.UUID; + import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.server.logging.LogSubject; +import org.apache.qpid.server.stats.StatisticsGatherer; -public interface AMQConnectionModel +public interface AMQConnectionModel extends StatisticsGatherer { + /** + * get a unique id for this connection. + * + * @return a {@link UUID} representing the connection + */ + public UUID getId(); + + /** + * Close the underlying Connection + * + * @param cause + * @param message + * @throws org.apache.qpid.AMQException + */ + public void close(AMQConstant cause, String message) throws AMQException; /** * Close the given requested Session + * * @param session * @param cause * @param message @@ -36,4 +57,16 @@ public interface AMQConnectionModel public void closeSession(AMQSessionModel session, AMQConstant cause, String message) throws AMQException; public long getConnectionId(); + + /** + * Get a list of all sessions using this connection. + * + * @return a list of {@link AMQSessionModel}s + */ + public List<AMQSessionModel> getSessionModels(); + + /** + * Return a {@link LogSubject} for the connection. + */ + public LogSubject getLogSubject(); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java index a1ffe272fd..449f698c48 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolEngine.java @@ -30,7 +30,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; -import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; @@ -92,6 +91,7 @@ import org.apache.qpid.server.output.ProtocolOutputConverterRegistry; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.state.AMQState; import org.apache.qpid.server.state.AMQStateManager; +import org.apache.qpid.server.stats.StatisticsCounter; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; import org.apache.qpid.transport.NetworkDriver; @@ -172,6 +172,10 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol private final UUID _id; private final ConfigStore _configStore; private long _createTime = System.currentTimeMillis(); + + private ApplicationRegistry _registry; + private boolean _statisticsEnabled = false; + private StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived; public ManagedObject getManagedObject() { @@ -195,9 +199,10 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol _configStore = virtualHostRegistry.getConfigStore(); _id = _configStore.createId(); - _actor.message(ConnectionMessages.OPEN(null, null, false, false)); + _registry = virtualHostRegistry.getApplicationRegistry(); + initialiseStatistics(); } private AMQProtocolSessionMBean createMBean() throws JMException @@ -1078,19 +1083,6 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol return (_clientVersion == null) ? null : _clientVersion.toString(); } - public void closeIfLingeringClosedChannels() - { - for (Entry<Integer, Long>id : _closingChannelsList.entrySet()) - { - if (id.getValue() + 30000 > System.currentTimeMillis()) - { - // We have a channel that we closed 30 seconds ago. Client's dead, kill the connection - _logger.error("Closing connection as channel was closed more than 30 seconds ago and no ChannelCloseOk has been processed"); - closeProtocolSession(); - } - } - } - public Boolean isIncoming() { return true; @@ -1263,7 +1255,6 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol public void closeSession(AMQSessionModel session, AMQConstant cause, String message) throws AMQException { - closeChannel((Integer)session.getID()); MethodRegistry methodRegistry = getMethodRegistry(); @@ -1274,5 +1265,97 @@ public class AMQProtocolEngine implements ProtocolEngine, Managable, AMQProtocol 0,0); writeFrame(responseBody.generateFrame((Integer)session.getID())); - } + } + + public void close(AMQConstant cause, String message) throws AMQException + { + closeConnection(0, new AMQConnectionException(cause, message, 0, 0, + getProtocolOutputConverter().getProtocolMajorVersion(), + getProtocolOutputConverter().getProtocolMinorVersion(), + (Throwable) null), true); + } + + public List<AMQSessionModel> getSessionModels() + { + List<AMQSessionModel> sessions = new ArrayList<AMQSessionModel>(); + for (AMQChannel channel : getChannels()) + { + sessions.add((AMQSessionModel) channel); + } + return sessions; + } + + public LogSubject getLogSubject() + { + return _logSubject; + } + + public void registerMessageDelivered(long messageSize) + { + if (isStatisticsEnabled()) + { + _messagesDelivered.registerEvent(1L); + _dataDelivered.registerEvent(messageSize); + } + _virtualHost.registerMessageDelivered(messageSize); + } + + public void registerMessageReceived(long messageSize, long timestamp) + { + if (isStatisticsEnabled()) + { + _messagesReceived.registerEvent(1L, timestamp); + _dataReceived.registerEvent(messageSize, timestamp); + } + _virtualHost.registerMessageReceived(messageSize, timestamp); + } + + public StatisticsCounter getMessageReceiptStatistics() + { + return _messagesReceived; + } + + public StatisticsCounter getDataReceiptStatistics() + { + return _dataReceived; + } + + public StatisticsCounter getMessageDeliveryStatistics() + { + return _messagesDelivered; + } + + public StatisticsCounter getDataDeliveryStatistics() + { + return _dataDelivered; + } + + public void resetStatistics() + { + _messagesDelivered.reset(); + _dataDelivered.reset(); + _messagesReceived.reset(); + _dataReceived.reset(); + } + + public void initialiseStatistics() + { + setStatisticsEnabled(!StatisticsCounter.DISABLE_STATISTICS && + _registry.getConfiguration().isStatisticsGenerationConnectionsEnabled()); + + _messagesDelivered = new StatisticsCounter("messages-delivered-" + getSessionID()); + _dataDelivered = new StatisticsCounter("data-delivered-" + getSessionID()); + _messagesReceived = new StatisticsCounter("messages-received-" + getSessionID()); + _dataReceived = new StatisticsCounter("data-received-" + getSessionID()); + } + + public boolean isStatisticsEnabled() + { + return _statisticsEnabled; + } + + public void setStatisticsEnabled(boolean enabled) + { + _statisticsEnabled = enabled; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java index f48a214933..c64ed4ad5a 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSession.java @@ -231,7 +231,5 @@ public interface AMQProtocolSession extends AMQVersionAwareProtocolSession, Prin List<AMQChannel> getChannels(); - void closeIfLingeringClosedChannels(); - void mgmtCloseChannel(int channelId); } 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 f4f2cab2c2..fcac78fafa 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 @@ -37,25 +37,15 @@ */ package org.apache.qpid.server.protocol; -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.ConnectionCloseBody; -import org.apache.qpid.framing.MethodRegistry; -import org.apache.qpid.management.common.mbeans.ManagedConnection; -import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor; -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.management.AMQManagedObject; -import org.apache.qpid.server.management.ManagedObject; +import java.util.Date; +import java.util.List; import javax.management.JMException; import javax.management.MBeanException; import javax.management.MBeanNotificationInfo; import javax.management.NotCompliantMBeanException; import javax.management.Notification; +import javax.management.ObjectName; import javax.management.monitor.MonitorNotification; import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeDataSupport; @@ -66,8 +56,20 @@ import javax.management.openmbean.SimpleType; import javax.management.openmbean.TabularData; import javax.management.openmbean.TabularDataSupport; import javax.management.openmbean.TabularType; -import java.util.Date; -import java.util.List; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ConnectionCloseBody; +import org.apache.qpid.framing.MethodRegistry; +import org.apache.qpid.management.common.mbeans.ManagedConnection; +import org.apache.qpid.management.common.mbeans.annotations.MBeanConstructor; +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.management.AMQManagedObject; +import org.apache.qpid.server.management.ManagedObject; /** * This MBean class implements the management interface. In order to make more attributes, operations and notifications @@ -94,8 +96,7 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed super(ManagedConnection.class, ManagedConnection.TYPE); _protocolSession = amqProtocolSession; String remote = getRemoteAddress(); - remote = "anonymous".equals(remote) ? (remote + hashCode()) : remote; - _name = jmxEncode(new StringBuffer(remote), 0).toString(); + _name = "anonymous".equals(remote) ? (remote + hashCode()) : remote; init(); } @@ -175,7 +176,7 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed public String getObjectInstanceName() { - return _name; + return ObjectName.quote(_name); } /** @@ -339,4 +340,78 @@ public class AMQProtocolSessionMBean extends AMQManagedObject implements Managed _broadcaster.sendNotification(n); } -} // End of MBean class + public void resetStatistics() throws Exception + { + _protocolSession.resetStatistics(); + } + + public double getPeakMessageDeliveryRate() + { + return _protocolSession.getMessageDeliveryStatistics().getPeak(); + } + + public double getPeakDataDeliveryRate() + { + return _protocolSession.getDataDeliveryStatistics().getPeak(); + } + + public double getMessageDeliveryRate() + { + return _protocolSession.getMessageDeliveryStatistics().getRate(); + } + + public double getDataDeliveryRate() + { + return _protocolSession.getDataDeliveryStatistics().getRate(); + } + + public long getTotalMessagesDelivered() + { + return _protocolSession.getMessageDeliveryStatistics().getTotal(); + } + + public long getTotalDataDelivered() + { + return _protocolSession.getDataDeliveryStatistics().getTotal(); + } + + public double getPeakMessageReceiptRate() + { + return _protocolSession.getMessageReceiptStatistics().getPeak(); + } + + public double getPeakDataReceiptRate() + { + return _protocolSession.getDataReceiptStatistics().getPeak(); + } + + public double getMessageReceiptRate() + { + return _protocolSession.getMessageReceiptStatistics().getRate(); + } + + public double getDataReceiptRate() + { + return _protocolSession.getDataReceiptStatistics().getRate(); + } + + public long getTotalMessagesReceived() + { + return _protocolSession.getMessageReceiptStatistics().getTotal(); + } + + public long getTotalDataReceived() + { + return _protocolSession.getDataReceiptStatistics().getTotal(); + } + + public boolean isStatisticsEnabled() + { + return _protocolSession.isStatisticsEnabled(); + } + + public void setStatisticsEnabled(boolean enabled) + { + _protocolSession.setStatisticsEnabled(enabled); + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java index a9b2354d75..bc63403a86 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQSessionModel.java @@ -20,15 +20,35 @@ */ package org.apache.qpid.server.protocol; +import org.apache.qpid.AMQException; import org.apache.qpid.server.logging.LogSubject; public interface AMQSessionModel { - Object getID(); + public Object getID(); - AMQConnectionModel getConnectionModel(); + public AMQConnectionModel getConnectionModel(); - String getClientID(); + public String getClientID(); + + public void close() throws AMQException; - LogSubject getLogSubject(); + public LogSubject getLogSubject(); + + /** + * This method is called from the housekeeping thread to check the status of + * transactions on this session and react appropriately. + * + * If a transaction is open for too long or idle for too long then a warning + * is logged or the connection is closed, depending on the configuration. An open + * transaction is one that has recent activity. The transaction age is counted + * from the time the transaction was started. An idle transaction is one that + * has had no activity, such as publishing or acknowledgeing messages. + * + * @param openWarn time in milliseconds before alerting on open transaction + * @param openClose time in milliseconds before closing connection with open transaction + * @param idleWarn time in milliseconds before alerting on idle transaction + * @param idleClose time in milliseconds before closing connection with idle transaction + */ + public void checkTransactionStatus(long openWarn, long openClose, long idleWarn, long idleClose) throws AMQException; } 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 b5294b6d2f..77c4e8fc23 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 @@ -43,6 +43,7 @@ import javax.management.JMException; import javax.management.MBeanException; import javax.management.MBeanNotificationInfo; import javax.management.Notification; +import javax.management.ObjectName; import javax.management.OperationsException; import javax.management.monitor.MonitorNotification; import javax.management.openmbean.ArrayType; @@ -97,7 +98,7 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que { super(ManagedQueue.class, ManagedQueue.TYPE); _queue = queue; - _queueName = jmxEncode(new StringBuffer(queue.getNameShortString()), 0).toString(); + _queueName = queue.getName(); } public ManagedObject getParentObject() @@ -147,7 +148,7 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que public String getObjectInstanceName() { - return _queueName; + return ObjectName.quote(_queueName); } public String getName() diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java index 78a642f22f..72b2a68450 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java @@ -23,6 +23,8 @@ package org.apache.qpid.server.registry; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; import java.util.UUID; import org.apache.commons.configuration.ConfigurationException; @@ -41,11 +43,12 @@ import org.apache.qpid.server.configuration.VirtualHostConfiguration; import org.apache.qpid.server.logging.CompositeStartupMessageLogger; import org.apache.qpid.server.logging.Log4jMessageLogger; import org.apache.qpid.server.logging.RootMessageLogger; -import org.apache.qpid.server.logging.AbstractRootMessageLogger; import org.apache.qpid.server.logging.SystemOutMessageLogger; +import org.apache.qpid.server.logging.actors.AbstractActor; import org.apache.qpid.server.logging.actors.BrokerActor; import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.messages.BrokerMessages; +import org.apache.qpid.server.logging.messages.VirtualHostMessages; import org.apache.qpid.server.management.ManagedObjectRegistry; import org.apache.qpid.server.management.NoopManagedObjectRegistry; import org.apache.qpid.server.plugins.PluginManager; @@ -54,6 +57,7 @@ import org.apache.qpid.server.security.auth.database.ConfigurationFilePrincipalD import org.apache.qpid.server.security.auth.database.PrincipalDatabaseManager; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; +import org.apache.qpid.server.stats.StatisticsCounter; import org.apache.qpid.server.transport.QpidAcceptor; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostImpl; @@ -104,6 +108,10 @@ public abstract class ApplicationRegistry implements IApplicationRegistry private ConfigStore _configStore; protected String _registryName; + + private Timer _reportingTimer; + private boolean _statisticsEnabled = false; + private StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived; static { @@ -294,6 +302,8 @@ public abstract class ApplicationRegistry implements IApplicationRegistry try { initialiseVirtualHosts(); + initialiseStatistics(); + initialiseStatisticsReporting(); } finally { @@ -320,6 +330,72 @@ public abstract class ApplicationRegistry implements IApplicationRegistry { _managedObjectRegistry = new NoopManagedObjectRegistry(); } + + public void initialiseStatisticsReporting() + { + long report = _configuration.getStatisticsReportingPeriod() * 1000; // convert to ms + final boolean broker = _configuration.isStatisticsGenerationBrokerEnabled(); + final boolean virtualhost = _configuration.isStatisticsGenerationVirtualhostsEnabled(); + final boolean reset = _configuration.isStatisticsReportResetEnabled(); + + /* add a timer task to report statistics if generation is enabled for broker or virtualhosts */ + if (report > 0L && (broker || virtualhost)) + { + _reportingTimer = new Timer("Statistics-Reporting", true); + + class StatisticsReportingTask extends TimerTask + { + private final int DELIVERED = 0; + private final int RECEIVED = 1; + + public void run() + { + CurrentActor.set(new AbstractActor(ApplicationRegistry.getInstance().getRootMessageLogger()) { + public String getLogMessage() + { + return "[" + Thread.currentThread().getName() + "] "; + } + }); + + if (broker) + { + CurrentActor.get().message(BrokerMessages.STATS_DATA(DELIVERED, _dataDelivered.getPeak() / 1024.0, _dataDelivered.getTotal())); + CurrentActor.get().message(BrokerMessages.STATS_MSGS(DELIVERED, _messagesDelivered.getPeak(), _messagesDelivered.getTotal())); + CurrentActor.get().message(BrokerMessages.STATS_DATA(RECEIVED, _dataReceived.getPeak() / 1024.0, _dataReceived.getTotal())); + CurrentActor.get().message(BrokerMessages.STATS_MSGS(RECEIVED, _messagesReceived.getPeak(), _messagesReceived.getTotal())); + } + + if (virtualhost) + { + for (VirtualHost vhost : getVirtualHostRegistry().getVirtualHosts()) + { + String name = vhost.getName(); + StatisticsCounter dataDelivered = vhost.getDataDeliveryStatistics(); + StatisticsCounter messagesDelivered = vhost.getMessageDeliveryStatistics(); + StatisticsCounter dataReceived = vhost.getDataReceiptStatistics(); + StatisticsCounter messagesReceived = vhost.getMessageReceiptStatistics(); + + CurrentActor.get().message(VirtualHostMessages.STATS_DATA(name, DELIVERED, dataDelivered.getPeak() / 1024.0, dataDelivered.getTotal())); + CurrentActor.get().message(VirtualHostMessages.STATS_MSGS(name, DELIVERED, messagesDelivered.getPeak(), messagesDelivered.getTotal())); + CurrentActor.get().message(VirtualHostMessages.STATS_DATA(name, RECEIVED, dataReceived.getPeak() / 1024.0, dataReceived.getTotal())); + CurrentActor.get().message(VirtualHostMessages.STATS_MSGS(name, RECEIVED, messagesReceived.getPeak(), messagesReceived.getTotal())); + } + } + + if (reset) + { + resetStatistics(); + } + + CurrentActor.remove(); + } + } + + _reportingTimer.scheduleAtFixedRate(new StatisticsReportingTask(), + report / 2, + report); + } + } public static IApplicationRegistry getInstance() { @@ -369,6 +445,12 @@ public abstract class ApplicationRegistry implements IApplicationRegistry { _logger.info("Shutting down ApplicationRegistry:" + this); } + + //Stop Statistics Reporting + if (_reportingTimer != null) + { + _reportingTimer.cancel(); + } //Stop incoming connections unbind(); @@ -498,4 +580,76 @@ public abstract class ApplicationRegistry implements IApplicationRegistry getBroker().addVirtualHost(virtualHost); return virtualHost; } + + public void registerMessageDelivered(long messageSize) + { + if (isStatisticsEnabled()) + { + _messagesDelivered.registerEvent(1L); + _dataDelivered.registerEvent(messageSize); + } + } + + public void registerMessageReceived(long messageSize, long timestamp) + { + if (isStatisticsEnabled()) + { + _messagesReceived.registerEvent(1L, timestamp); + _dataReceived.registerEvent(messageSize, timestamp); + } + } + + public StatisticsCounter getMessageReceiptStatistics() + { + return _messagesReceived; + } + + public StatisticsCounter getDataReceiptStatistics() + { + return _dataReceived; + } + + public StatisticsCounter getMessageDeliveryStatistics() + { + return _messagesDelivered; + } + + public StatisticsCounter getDataDeliveryStatistics() + { + return _dataDelivered; + } + + public void resetStatistics() + { + _messagesDelivered.reset(); + _dataDelivered.reset(); + _messagesReceived.reset(); + _dataReceived.reset(); + + for (VirtualHost vhost : _virtualHostRegistry.getVirtualHosts()) + { + vhost.resetStatistics(); + } + } + + public void initialiseStatistics() + { + setStatisticsEnabled(!StatisticsCounter.DISABLE_STATISTICS && + getConfiguration().isStatisticsGenerationBrokerEnabled()); + + _messagesDelivered = new StatisticsCounter("messages-delivered"); + _dataDelivered = new StatisticsCounter("bytes-delivered"); + _messagesReceived = new StatisticsCounter("messages-received"); + _dataReceived = new StatisticsCounter("bytes-received"); + } + + public boolean isStatisticsEnabled() + { + return _statisticsEnabled; + } + + public void setStatisticsEnabled(boolean enabled) + { + _statisticsEnabled = enabled; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java index 228c3b9112..0ef55097ce 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java @@ -35,11 +35,12 @@ import org.apache.qpid.server.plugins.PluginManager; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.auth.database.PrincipalDatabaseManager; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.stats.StatisticsGatherer; import org.apache.qpid.server.transport.QpidAcceptor; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.server.virtualhost.VirtualHostRegistry; -public interface IApplicationRegistry +public interface IApplicationRegistry extends StatisticsGatherer { /** * Initialise the application registry. All initialisation must be done in this method so that any components @@ -97,4 +98,6 @@ public interface IApplicationRegistry ConfigStore getConfigStore(); void setConfigStore(ConfigStore store); + + void initialiseStatisticsReporting(); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsCounter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsCounter.java new file mode 100644 index 0000000000..b732121180 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsCounter.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.stats; + +import java.util.Date; +import java.util.concurrent.atomic.AtomicLong; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class collects statistics and counts the total, rate per second and + * peak rate per second values for the events that are registered with it. + */ +public class StatisticsCounter +{ + private static final Logger _log = LoggerFactory.getLogger(StatisticsCounter.class); + + public static final long DEFAULT_SAMPLE_PERIOD = Long.getLong("qpid.statistics.samplePeriod", 2000L); // 2s + public static final boolean DISABLE_STATISTICS = Boolean.getBoolean("qpid.statistics.disable"); + + private static final String COUNTER = "counter"; + private static final AtomicLong _counterIds = new AtomicLong(0L); + + private long _peak = 0L; + private long _total = 0L; + private long _temp = 0L; + private long _last = 0L; + private long _rate = 0L; + + private long _start; + + private final long _period; + private final String _name; + + public StatisticsCounter() + { + this(COUNTER); + } + + public StatisticsCounter(String name) + { + this(name, DEFAULT_SAMPLE_PERIOD); + } + + public StatisticsCounter(String name, long period) + { + _period = period; + _name = name + "-" + + _counterIds.incrementAndGet(); + reset(); + } + + public void registerEvent() + { + registerEvent(1L); + } + + public void registerEvent(long value) + { + registerEvent(value, System.currentTimeMillis()); + } + + public void registerEvent(long value, long timestamp) + { + if (DISABLE_STATISTICS) + { + return; + } + + long thisSample = (timestamp / _period); + synchronized (this) + { + if (thisSample > _last) + { + _last = thisSample; + _rate = _temp; + _temp = 0L; + if (_rate > _peak) + { + _peak = _rate; + } + } + + _total += value; + _temp += value; + } + } + + /** + * Update the current rate and peak - may reset rate to zero if a new + * sample period has started. + */ + private void update() + { + registerEvent(0L, System.currentTimeMillis()); + } + + /** + * Reset + */ + public void reset() + { + _log.info("Resetting statistics for counter: " + _name); + _peak = 0L; + _rate = 0L; + _total = 0L; + _start = System.currentTimeMillis(); + _last = _start / _period; + } + + public double getPeak() + { + update(); + return (double) _peak / ((double) _period / 1000.0d); + } + + public double getRate() + { + update(); + return (double) _rate / ((double) _period / 1000.0d); + } + + public long getTotal() + { + return _total; + } + + public long getStart() + { + return _start; + } + + public Date getStartTime() + { + return new Date(_start); + } + + public String getName() + { + return _name; + } + + public long getPeriod() + { + return _period; + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsGatherer.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsGatherer.java new file mode 100644 index 0000000000..36fec4025a --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/stats/StatisticsGatherer.java @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.qpid.server.stats; + +/** + * This interface is to be implemented by any broker business object that + * wishes to gather statistics about messages delivered through it. + * + * These statistics are exposed using a separate JMX Mbean interface, which + * calls these methods to retrieve the underlying {@link StatisticsCounter}s + * and return their attributes. This interface gives a standard way for + * parts of the broker to set up and configure statistics generation. + * <p> + * When creating these objects, there should be a parent/child relationship + * between them, such that the lowest level gatherer can record staticics if + * enabled, and pass on the notification to the parent object to allow higher + * level aggregation. When resetting statistics, this works in the opposite + * direction, with higher level gatherers also resetting all of their children. + */ +public interface StatisticsGatherer +{ + /** + * Initialise the statistics gathering for this object. + * + * This method is responsible for creating any {@link StatisticsCounter} + * objects and for determining whether statistics generation should be + * enabled, by checking broker and system configuration. + * + * @see StatisticsCounter#DISABLE_STATISTICS + */ + void initialiseStatistics(); + + /** + * This method is responsible for registering the receipt of a message + * with the counters, and also for passing this notification to any parent + * {@link StatisticsGatherer}s. If statistics generation is not enabled, + * then this method should simple delegate to the parent gatherer. + * + * @param messageSize the size in bytes of the delivered message + * @param timestamp the time the message was delivered + */ + void registerMessageReceived(long messageSize, long timestamp); + + /** + * This method is responsible for registering the delivery of a message + * with the counters. Message delivery is recorded by the counter using + * the current system time, as opposed to the message timestamp. + * + * @param messageSize the size in bytes of the delivered message + * @see #registerMessageReceived(long, long) + */ + void registerMessageDelivered(long messageSize); + + /** + * Gives access to the {@link StatisticsCounter} that is used to count + * delivered message statistics. + * + * @return the {@link StatisticsCounter} that counts delivered messages + */ + StatisticsCounter getMessageDeliveryStatistics(); + + /** + * Gives access to the {@link StatisticsCounter} that is used to count + * received message statistics. + * + * @return the {@link StatisticsCounter} that counts received messages + */ + StatisticsCounter getMessageReceiptStatistics(); + + /** + * Gives access to the {@link StatisticsCounter} that is used to count + * delivered message size statistics. + * + * @return the {@link StatisticsCounter} that counts delivered bytes + */ + StatisticsCounter getDataDeliveryStatistics(); + + /** + * Gives access to the {@link StatisticsCounter} that is used to count + * received message size statistics. + * + * @return the {@link StatisticsCounter} that counts received bytes + */ + StatisticsCounter getDataReceiptStatistics(); + + /** + * Reset the counters for this, and any child {@link StatisticsGatherer}s. + */ + void resetStatistics(); + + /** + * Check if this object has statistics generation enabled. + * + * @return true if statistics generation is enabled + */ + boolean isStatisticsEnabled(); + + /** + * Enable or disable statistics generation for this object. + */ + void setStatisticsEnabled(boolean enabled); +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java index b36ac84cdd..a20436f029 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription_0_10.java @@ -97,7 +97,6 @@ public class Subscription_0_10 implements Subscription, FlowCreditManager.FlowCr private FlowCreditManager_0_10 _creditManager; - private StateListener _stateListener = new StateListener() { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java index d2addfde0c..75bd50e3a2 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnection.java @@ -24,6 +24,9 @@ import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.*; import java.text.MessageFormat; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; import org.apache.qpid.AMQException; import org.apache.qpid.protocol.AMQConstant; @@ -35,12 +38,16 @@ import org.apache.qpid.server.logging.actors.GenericActor; import org.apache.qpid.server.logging.messages.ConnectionMessages; import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.server.protocol.AMQSessionModel; +import org.apache.qpid.server.registry.ApplicationRegistry; +import org.apache.qpid.server.stats.StatisticsCounter; import org.apache.qpid.server.virtualhost.VirtualHost; import org.apache.qpid.transport.Connection; +import org.apache.qpid.transport.ConnectionCloseCode; import org.apache.qpid.transport.ExecutionErrorCode; import org.apache.qpid.transport.ExecutionException; import org.apache.qpid.transport.Method; import org.apache.qpid.transport.ProtocolEvent; +import org.apache.qpid.transport.Session; public class ServerConnection extends Connection implements AMQConnectionModel, LogSubject { @@ -49,11 +56,20 @@ public class ServerConnection extends Connection implements AMQConnectionModel, private AtomicBoolean _logClosed = new AtomicBoolean(false); private LogActor _actor = GenericActor.getInstance(this); + private ApplicationRegistry _registry; + private boolean _statisticsEnabled = false; + private StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived; + public ServerConnection() { } + public UUID getId() + { + return _config.getId(); + } + @Override protected void invoke(Method method) { @@ -110,6 +126,9 @@ public class ServerConnection extends Connection implements AMQConnectionModel, public void setVirtualHost(VirtualHost virtualHost) { _virtualHost = virtualHost; + _virtualHost.getConnectionRegistry().registerConnection(this); + + initialiseStatistics(); } public void setConnectionConfig(final ConnectionConfig config) @@ -145,6 +164,11 @@ public class ServerConnection extends Connection implements AMQConnectionModel, ((ServerSession)session).close(); } + + public LogSubject getLogSubject() + { + return (LogSubject) this; + } @Override public void received(ProtocolEvent event) @@ -215,4 +239,100 @@ public class ServerConnection extends Connection implements AMQConnectionModel, { return _actor; } + + @Override + public void close(AMQConstant cause, String message) throws AMQException + { + ConnectionCloseCode replyCode = ConnectionCloseCode.NORMAL; + try + { + replyCode = ConnectionCloseCode.get(cause.getCode()); + } + catch (IllegalArgumentException iae) + { + // Ignore + } + close(replyCode, message); + getVirtualHost().getConnectionRegistry().deregisterConnection(this); + } + + @Override + public List<AMQSessionModel> getSessionModels() + { + List<AMQSessionModel> sessions = new ArrayList<AMQSessionModel>(); + for (Session ssn : getChannels()) + { + sessions.add((AMQSessionModel) ssn); + } + return sessions; + } + + public void registerMessageDelivered(long messageSize) + { + if (isStatisticsEnabled()) + { + _messagesDelivered.registerEvent(1L); + _dataDelivered.registerEvent(messageSize); + } + _virtualHost.registerMessageDelivered(messageSize); + } + + public void registerMessageReceived(long messageSize, long timestamp) + { + if (isStatisticsEnabled()) + { + _messagesReceived.registerEvent(1L, timestamp); + _dataReceived.registerEvent(messageSize, timestamp); + } + _virtualHost.registerMessageReceived(messageSize, timestamp); + } + + public StatisticsCounter getMessageReceiptStatistics() + { + return _messagesReceived; + } + + public StatisticsCounter getDataReceiptStatistics() + { + return _dataReceived; + } + + public StatisticsCounter getMessageDeliveryStatistics() + { + return _messagesDelivered; + } + + public StatisticsCounter getDataDeliveryStatistics() + { + return _dataDelivered; + } + + public void resetStatistics() + { + _messagesDelivered.reset(); + _dataDelivered.reset(); + _messagesReceived.reset(); + _dataReceived.reset(); + } + + public void initialiseStatistics() + { + setStatisticsEnabled(!StatisticsCounter.DISABLE_STATISTICS && + _virtualHost.getApplicationRegistry().getConfiguration().isStatisticsGenerationConnectionsEnabled()); + + _messagesDelivered = new StatisticsCounter("messages-delivered-" + getConnectionId()); + _dataDelivered = new StatisticsCounter("data-delivered-" + getConnectionId()); + _messagesReceived = new StatisticsCounter("messages-received-" + getConnectionId()); + _dataReceived = new StatisticsCounter("data-received-" + getConnectionId()); + } + + public boolean isStatisticsEnabled() + { + return _statisticsEnabled; + } + + public void setStatisticsEnabled(boolean enabled) + { + _statisticsEnabled = enabled; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java index 2b9e92f685..174dcbfa69 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerConnectionDelegate.java @@ -20,26 +20,28 @@ */ package org.apache.qpid.server.transport; -import org.apache.qpid.transport.*; -import org.apache.qpid.server.logging.actors.CurrentActor; -import org.apache.qpid.server.logging.actors.GenericActor; -import org.apache.qpid.common.ClientProperties; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; + +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + import org.apache.qpid.protocol.ProtocolEngine; -import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.registry.ApplicationRegistry; import org.apache.qpid.server.registry.IApplicationRegistry; +import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.virtualhost.VirtualHost; - -import javax.security.sasl.SaslServer; -import javax.security.sasl.SaslException; -import java.util.*; +import org.apache.qpid.transport.*; public class ServerConnectionDelegate extends ServerDelegate { private String _localFQDN; private final IApplicationRegistry _appRegistry; - public ServerConnectionDelegate(IApplicationRegistry appRegistry, String localFQDN) { this(new HashMap<String,Object>(Collections.singletonMap("qpid.federation_tag",appRegistry.getBroker().getFederationTag())), Collections.singletonList((Object)"en_US"), appRegistry, localFQDN); @@ -138,6 +140,7 @@ public class ServerConnectionDelegate extends ServerDelegate sconn.invoke(new ConnectionClose(ConnectionCloseCode.INVALID_PATH, "Unknown virtualhost '"+vhostName+"'")); sconn.setState(Connection.State.CLOSING); } + } @Override diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java index 540ad3fffd..60c94b43c0 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSession.java @@ -20,12 +20,26 @@ */ package org.apache.qpid.server.transport; -import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.CHANNEL_FORMAT; -import static org.apache.qpid.util.Serial.gt; +import static org.apache.qpid.server.logging.subjects.LogSubjectFormat.*; +import static org.apache.qpid.util.Serial.*; -import com.sun.security.auth.UserPrincipal; +import java.lang.ref.WeakReference; +import java.security.Principal; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.SortedMap; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicLong; import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.ProtocolEngine; import org.apache.qpid.server.configuration.ConfigStore; import org.apache.qpid.server.configuration.ConfiguredObject; @@ -38,6 +52,8 @@ import org.apache.qpid.server.logging.actors.CurrentActor; import org.apache.qpid.server.logging.actors.GenericActor; import org.apache.qpid.server.logging.messages.ChannelMessages; import org.apache.qpid.server.message.ServerMessage; +import org.apache.qpid.server.protocol.AMQConnectionModel; +import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.queue.QueueEntry; @@ -48,8 +64,6 @@ import org.apache.qpid.server.txn.AutoCommitTransaction; import org.apache.qpid.server.txn.LocalTransaction; import org.apache.qpid.server.txn.ServerTransaction; import org.apache.qpid.server.virtualhost.VirtualHost; -import org.apache.qpid.server.protocol.AMQSessionModel; -import org.apache.qpid.server.protocol.AMQConnectionModel; import org.apache.qpid.transport.Binary; import org.apache.qpid.transport.Connection; import org.apache.qpid.transport.MessageTransfer; @@ -58,24 +72,15 @@ import org.apache.qpid.transport.Range; import org.apache.qpid.transport.RangeSet; import org.apache.qpid.transport.Session; import org.apache.qpid.transport.SessionDelegate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.lang.ref.WeakReference; -import java.security.Principal; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.SortedMap; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentSkipListMap; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicLong; +import com.sun.security.auth.UserPrincipal; public class ServerSession extends Session implements PrincipalHolder, SessionConfig, AMQSessionModel, LogSubject { + private static final Logger _logger = LoggerFactory.getLogger(ServerSession.class); + private static final String NULL_DESTINTATION = UUID.randomUUID().toString(); private final UUID _id; @@ -111,6 +116,7 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo private final AtomicLong _txnCommits = new AtomicLong(0); private final AtomicLong _txnRejects = new AtomicLong(0); private final AtomicLong _txnCount = new AtomicLong(0); + private final AtomicLong _txnUpdateTime = new AtomicLong(0); private Principal _principal; @@ -141,7 +147,7 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo _connectionConfig = connConfig; _transaction = new AutoCommitTransaction(this.getMessageStore()); _principal = new UserPrincipal(connection.getAuthorizationID()); - _reference = new WeakReference(this); + _reference = new WeakReference<Session>(this); _id = getConfigStore().createId(); getConfigStore().addConfiguredObject(this); } @@ -160,8 +166,8 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo public void enqueue(final ServerMessage message, final ArrayList<? extends BaseQueue> queues) { - - _transaction.enqueue(queues,message, new ServerTransaction.Action() + getConnectionModel().registerMessageReceived(message.getSize(), message.getArrivalTime()); + _transaction.enqueue(queues,message, new ServerTransaction.Action() { BaseQueue[] _queues = queues.toArray(new BaseQueue[queues.size()]); @@ -189,6 +195,7 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo }); incrementOutstandingTxnsIfNecessary(); + updateTransactionalActivity(); } @@ -196,6 +203,7 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo Runnable postIdSettingAction) { invoke(xfr, postIdSettingAction); + getConnectionModel().registerMessageDelivered(xfr.getBodySize()); } public void onMessageDispositionChange(MessageTransfer xfr, MessageDispositionChangeListener acceptListener) @@ -377,6 +385,7 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo entry.release(); } }); + updateTransactionalActivity(); } public Collection<Subscription_0_10> getSubscriptions() @@ -425,6 +434,11 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo // theory return !(_transaction instanceof AutoCommitTransaction); } + + public boolean inTransaction() + { + return isTransactional() && _txnUpdateTime.get() > 0 && _transaction.getTransactionStartTime() > 0; + } public void selectTx() { @@ -471,6 +485,17 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo } } + /** + * Update last transaction activity timestamp + */ + public void updateTransactionalActivity() + { + if (isTransactional()) + { + _txnUpdateTime.set(System.currentTimeMillis()); + } + } + public Long getTxnStarts() { return _txnStarts.get(); @@ -606,6 +631,38 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo return (LogSubject) this; } + public void checkTransactionStatus(long openWarn, long openClose, long idleWarn, long idleClose) throws AMQException + { + if (inTransaction()) + { + long currentTime = System.currentTimeMillis(); + long openTime = currentTime - _transaction.getTransactionStartTime(); + long idleTime = currentTime - _txnUpdateTime.get(); + + // Log a warning on idle or open transactions + if (idleWarn > 0L && idleTime > idleWarn) + { + CurrentActor.get().message(getLogSubject(), ChannelMessages.IDLE_TXN(openTime)); + _logger.warn("IDLE TRANSACTION ALERT " + getLogSubject().toString() + " " + idleTime + " ms"); + } + else if (openWarn > 0L && openTime > openWarn) + { + CurrentActor.get().message(getLogSubject(), ChannelMessages.OPEN_TXN(openTime)); + _logger.warn("OPEN TRANSACTION ALERT " + getLogSubject().toString() + " " + openTime + " ms"); + } + + // Close connection for idle or open transactions that have timed out + if (idleClose > 0L && idleTime > idleClose) + { + getConnectionModel().closeSession(this, AMQConstant.RESOURCE_ERROR, "Idle transaction timed out"); + } + else if (openClose > 0L && openTime > openClose) + { + getConnectionModel().closeSession(this, AMQConstant.RESOURCE_ERROR, "Open transaction timed out"); + } + } + } + @Override public String toLogString() { @@ -617,7 +674,5 @@ public class ServerSession extends Session implements PrincipalHolder, SessionCo getVirtualHost().getName(), getChannel()) + "] "; - } - } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java index 42a3975e24..be659c87ae 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/transport/ServerSessionDelegate.java @@ -209,26 +209,27 @@ public class ServerSessionDelegate extends SessionDelegate } else { - if(queue.isExclusive()) { + ServerSession s = (ServerSession) session; + queue.setExclusiveOwningSession(s); if(queue.getPrincipalHolder() == null) { - queue.setPrincipalHolder((ServerSession)session); + queue.setPrincipalHolder(s); + queue.setExclusiveOwningSession(s); ((ServerSession) session).addSessionCloseTask(new ServerSession.Task() { - public void doTask(ServerSession session) { if(queue.getPrincipalHolder() == session) { queue.setPrincipalHolder(null); + queue.setExclusiveOwningSession(null); } } }); } - } FlowCreditManager_0_10 creditManager = new WindowCreditManager(0L,0L); @@ -369,7 +370,6 @@ public class ServerSessionDelegate extends SessionDelegate } ssn.processed(xfr); - } @Override @@ -969,10 +969,10 @@ public class ServerSessionDelegate extends SessionDelegate } - if(method.hasAutoDelete() - && method.getAutoDelete() - && method.hasExclusive() - && method.getExclusive()) + if (method.hasAutoDelete() + && method.getAutoDelete() + && method.hasExclusive() + && method.getExclusive()) { final AMQQueue q = queue; final ServerSession.Task deleteQueueTask = new ServerSession.Task() @@ -999,12 +999,12 @@ public class ServerSessionDelegate extends SessionDelegate } }); } - else if(method.getExclusive()) + if (method.hasExclusive() + && method.getExclusive()) { final AMQQueue q = queue; final ServerSession.Task removeExclusive = new ServerSession.Task() { - public void doTask(ServerSession session) { q.setPrincipalHolder(null); @@ -1012,10 +1012,10 @@ public class ServerSessionDelegate extends SessionDelegate } }; final ServerSession s = (ServerSession) session; + q.setExclusiveOwningSession(s); s.addSessionCloseTask(removeExclusive); queue.addQueueDeleteTask(new AMQQueue.Task() { - public void doTask(AMQQueue queue) throws AMQException { s.removeSessionCloseTask(removeExclusive); @@ -1029,7 +1029,7 @@ public class ServerSessionDelegate extends SessionDelegate } } } - else if (method.getExclusive() && (queue.getPrincipalHolder() != null && !queue.getPrincipalHolder().equals(session))) + else if (method.getExclusive() && (queue.getExclusiveOwningSession() != null && !queue.getExclusiveOwningSession().equals(session))) { String description = "Cannot declare queue('" + queueName + "')," + " as exclusive queue with same name " diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java index db781ead96..36e9d78440 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/AutoCommitTransaction.java @@ -50,6 +50,11 @@ public class AutoCommitTransaction implements ServerTransaction _transactionLog = transactionLog; } + public long getTransactionStartTime() + { + return 0L; + } + /** * Since AutoCommitTransaction have no concept of a long lived transaction, any Actions registered * by the caller are executed immediately. diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java index a04c743be1..f9dac782a6 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransaction.java @@ -20,18 +20,23 @@ package org.apache.qpid.server.txn; * */ - import java.util.ArrayList; import java.util.Collection; import java.util.List; -import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.server.message.EnqueableMessage; import org.apache.qpid.server.message.ServerMessage; import org.apache.qpid.server.queue.BaseQueue; import org.apache.qpid.server.queue.QueueEntry; import org.apache.qpid.server.store.TransactionLog; +import org.apache.qpid.server.queue.AMQQueue; +import org.apache.qpid.server.queue.BaseQueue; +import org.apache.qpid.server.queue.QueueEntry; +import org.apache.qpid.server.store.TransactionLog; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * A concrete implementation of ServerTransaction where enqueue/dequeue @@ -41,17 +46,28 @@ import org.apache.qpid.server.store.TransactionLog; */ public class LocalTransaction implements ServerTransaction { - protected static final Logger _logger = Logger.getLogger(LocalTransaction.class); + protected static final Logger _logger = LoggerFactory.getLogger(LocalTransaction.class); private final List<Action> _postTransactionActions = new ArrayList<Action>(); private volatile TransactionLog.Transaction _transaction; private TransactionLog _transactionLog; + private long _txnStartTime = 0L; public LocalTransaction(TransactionLog transactionLog) { _transactionLog = transactionLog; } + + public boolean inTransaction() + { + return _transaction != null; + } + + public long getTransactionStartTime() + { + return _txnStartTime; + } public void addPostTransactionAction(Action postTransactionAction) { @@ -89,7 +105,6 @@ public class LocalTransaction implements ServerTransaction try { - for(QueueEntry entry : queueEntries) { ServerMessage message = entry.getMessage(); @@ -113,7 +128,6 @@ public class LocalTransaction implements ServerTransaction _logger.error("Error during message dequeues", e); tidyUpOnError(e); } - } private void tidyUpOnError(Exception e) @@ -140,8 +154,7 @@ public class LocalTransaction implements ServerTransaction } finally { - _transaction = null; - _postTransactionActions.clear(); + resetDetails(); } } @@ -193,8 +206,25 @@ public class LocalTransaction implements ServerTransaction { _postTransactionActions.add(postTransactionAction); + if (_txnStartTime == 0L) + { + _txnStartTime = System.currentTimeMillis(); + } + if(message.isPersistent()) { + if(_transaction == null) + { + for(BaseQueue queue : queues) + { + if(queue.isDurable()) + { + beginTranIfNecessary(); + break; + } + } + } + try { for(BaseQueue queue : queues) @@ -248,17 +278,14 @@ public class LocalTransaction implements ServerTransaction } finally { - _transaction = null; - _postTransactionActions.clear(); + resetDetails(); } - } public void rollback() { try { - if(_transaction != null) { _transaction.abortTran(); @@ -280,9 +307,15 @@ public class LocalTransaction implements ServerTransaction } finally { - _transaction = null; - _postTransactionActions.clear(); + resetDetails(); } } } + + private void resetDetails() + { + _transaction = null; + _postTransactionActions.clear(); + _txnStartTime = 0L; + } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java index b61b8a5c64..b3c6e1ac3a 100755 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/txn/ServerTransaction.java @@ -52,6 +52,13 @@ public interface ServerTransaction public void onRollback(); } + /** + * Return the time the current transaction started. + * + * @return the time this transaction started or 0 if not in a transaction + */ + long getTransactionStartTime(); + /** * Register an Action for execution after transaction commit or rollback. Actions * will be executed in the order in which they are registered. 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 4ed0507228..04f19b79bb 100755 --- 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 @@ -20,30 +20,28 @@ */ package org.apache.qpid.server.virtualhost; +import java.util.UUID; + import org.apache.qpid.common.Closeable; +import org.apache.qpid.server.binding.BindingFactory; +import org.apache.qpid.server.configuration.ConfigStore; +import org.apache.qpid.server.configuration.VirtualHostConfig; +import org.apache.qpid.server.configuration.VirtualHostConfiguration; import org.apache.qpid.server.connection.IConnectionRegistry; +import org.apache.qpid.server.exchange.ExchangeFactory; +import org.apache.qpid.server.exchange.ExchangeRegistry; import org.apache.qpid.server.federation.BrokerLink; -import org.apache.qpid.server.configuration.VirtualHostConfiguration; -import org.apache.qpid.server.configuration.VirtualHostConfig; -import org.apache.qpid.server.configuration.ConfigStore; +import org.apache.qpid.server.management.ManagedObject; import org.apache.qpid.server.queue.QueueRegistry; -import org.apache.qpid.server.exchange.ExchangeRegistry; -import org.apache.qpid.server.exchange.ExchangeFactory; -import org.apache.qpid.server.store.MessageStore; -import org.apache.qpid.server.store.TransactionLog; -import org.apache.qpid.server.store.DurableConfigurationStore; +import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; -import org.apache.qpid.server.management.ManagedObject; -import org.apache.qpid.server.registry.IApplicationRegistry; -import org.apache.qpid.server.binding.BindingFactory; - -import java.util.List; -import java.util.UUID; -import java.util.TimerTask; -import java.util.concurrent.FutureTask; +import org.apache.qpid.server.stats.StatisticsGatherer; +import org.apache.qpid.server.store.DurableConfigurationStore; +import org.apache.qpid.server.store.MessageStore; +import org.apache.qpid.server.store.TransactionLog; -public interface VirtualHost extends DurableConfigurationStore.Source, VirtualHostConfig, Closeable +public interface VirtualHost extends DurableConfigurationStore.Source, VirtualHostConfig, Closeable, StatisticsGatherer { IConnectionRegistry getConnectionRegistry(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java index 6ec1c512e5..5374a56f06 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHostImpl.java @@ -24,19 +24,18 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.TimerTask; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import javax.management.NotCompliantMBeanException; +import javax.management.ObjectName; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; import org.apache.qpid.AMQException; -import org.apache.qpid.AMQInternalException; import org.apache.qpid.AMQStoreException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; @@ -63,6 +62,8 @@ import org.apache.qpid.server.logging.messages.VirtualHostMessages; import org.apache.qpid.server.logging.subjects.MessageStoreLogSubject; import org.apache.qpid.server.management.AMQManagedObject; import org.apache.qpid.server.management.ManagedObject; +import org.apache.qpid.server.protocol.AMQConnectionModel; +import org.apache.qpid.server.protocol.AMQSessionModel; import org.apache.qpid.server.queue.AMQQueue; import org.apache.qpid.server.queue.AMQQueueFactory; import org.apache.qpid.server.queue.DefaultQueueRegistry; @@ -72,6 +73,7 @@ import org.apache.qpid.server.registry.IApplicationRegistry; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.auth.manager.AuthenticationManager; import org.apache.qpid.server.security.auth.manager.PrincipalDatabaseAuthenticationManager; +import org.apache.qpid.server.stats.StatisticsCounter; import org.apache.qpid.server.store.ConfigurationRecoveryHandler; import org.apache.qpid.server.store.DurableConfigurationStore; import org.apache.qpid.server.store.MessageStore; @@ -111,6 +113,8 @@ public class VirtualHostImpl implements VirtualHost private BrokerConfig _broker; private UUID _id; + private boolean _statisticsEnabled = false; + private StatisticsCounter _messagesDelivered, _dataDelivered, _messagesReceived, _dataReceived; private final long _createTime = System.currentTimeMillis(); private final ConcurrentHashMap<BrokerLink,BrokerLink> _links = new ConcurrentHashMap<BrokerLink, BrokerLink>(); @@ -161,12 +165,12 @@ public class VirtualHostImpl implements VirtualHost public String getObjectInstanceName() { - return _name.toString(); + return ObjectName.quote(_name); } public String getName() { - return _name.toString(); + return _name; } public VirtualHostImpl getVirtualHost() @@ -249,6 +253,8 @@ public class VirtualHostImpl implements VirtualHost _brokerMBean = new AMQBrokerManagerMBean(_virtualHostMBean); _brokerMBean.register(); initialiseHouseKeeping(hostConfig.getHousekeepingExpiredMessageCheckPeriod()); + + initialiseStatistics(); } private void initialiseHouseKeeping(long period) @@ -281,19 +287,30 @@ public class VirtualHostImpl implements VirtualHost // house keeping task from running. } } + for (AMQConnectionModel connection : getConnectionRegistry().getConnections()) + { + _logger.debug("Checking for long running open transactions on connection " + connection); + for (AMQSessionModel session : connection.getSessionModels()) + { + _logger.debug("Checking for long running open transactions on session " + session); + try + { + session.checkTransactionStatus(_configuration.getTransactionTimeoutOpenWarn(), + _configuration.getTransactionTimeoutOpenClose(), + _configuration.getTransactionTimeoutIdleWarn(), + _configuration.getTransactionTimeoutIdleClose()); + } + catch (Exception e) + { + _logger.error("Exception in housekeeping for connection: " + connection.toString(), e); + } + } + } } } scheduleHouseKeepingTask(period, new ExpiredMessagesTask(this)); - class ForceChannelClosuresTask extends TimerTask - { - public void run() - { - _connectionRegistry.expireClosedChannels(); - } - } - Map<String, VirtualHostPluginFactory> plugins = ApplicationRegistry.getInstance().getPluginManager().getVirtualHostPlugins(); @@ -627,6 +644,80 @@ public class VirtualHostImpl implements VirtualHost { return _bindingFactory; } + + public void registerMessageDelivered(long messageSize) + { + if (isStatisticsEnabled()) + { + _messagesDelivered.registerEvent(1L); + _dataDelivered.registerEvent(messageSize); + } + _appRegistry.registerMessageDelivered(messageSize); + } + + public void registerMessageReceived(long messageSize, long timestamp) + { + if (isStatisticsEnabled()) + { + _messagesReceived.registerEvent(1L, timestamp); + _dataReceived.registerEvent(messageSize, timestamp); + } + _appRegistry.registerMessageReceived(messageSize, timestamp); + } + + public StatisticsCounter getMessageReceiptStatistics() + { + return _messagesReceived; + } + + public StatisticsCounter getDataReceiptStatistics() + { + return _dataReceived; + } + + public StatisticsCounter getMessageDeliveryStatistics() + { + return _messagesDelivered; + } + + public StatisticsCounter getDataDeliveryStatistics() + { + return _dataDelivered; + } + + public void resetStatistics() + { + _messagesDelivered.reset(); + _dataDelivered.reset(); + _messagesReceived.reset(); + _dataReceived.reset(); + + for (AMQConnectionModel connection : _connectionRegistry.getConnections()) + { + connection.resetStatistics(); + } + } + + public void initialiseStatistics() + { + setStatisticsEnabled(!StatisticsCounter.DISABLE_STATISTICS && + _appRegistry.getConfiguration().isStatisticsGenerationVirtualhostsEnabled()); + + _messagesDelivered = new StatisticsCounter("messages-delivered-" + getName()); + _dataDelivered = new StatisticsCounter("bytes-delivered-" + getName()); + _messagesReceived = new StatisticsCounter("messages-received-" + getName()); + _dataReceived = new StatisticsCounter("bytes-received-" + getName()); + } + + public boolean isStatisticsEnabled() + { + return _statisticsEnabled; + } + + public void setStatisticsEnabled(boolean enabled) + { + _statisticsEnabled = enabled; + } public void createBrokerConnection(final String transport, final String host, diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/stats/StatisticsCounterTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/stats/StatisticsCounterTest.java new file mode 100644 index 0000000000..fbaa1342c9 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/stats/StatisticsCounterTest.java @@ -0,0 +1,144 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.stats; + +import junit.framework.TestCase; + +/** + * Unit tests for the {@link StatisticsCounter} class. + */ +public class StatisticsCounterTest extends TestCase +{ + /** + * Check that statistics counters are created correctly. + */ + public void testCreate() + { + long before = System.currentTimeMillis(); + StatisticsCounter counter = new StatisticsCounter("name", 1234L); + long after = System.currentTimeMillis(); + + assertTrue(before <= counter.getStart()); + assertTrue(after >= counter.getStart()); + assertTrue(counter.getName().startsWith("name-")); + assertEquals(1234L, counter.getPeriod()); + } + + /** + * Check that totals add up correctly. + */ + public void testTotal() + { + StatisticsCounter counter = new StatisticsCounter("test", 1000L); + long start = counter.getStart(); + for (int i = 0; i < 100; i++) + { + counter.registerEvent(i, start + i); + } + assertEquals(99 * 50, counter.getTotal()); // cf. Gauss + } + + /** + * Test totals add up correctly even when messages are delivered + * out-of-order. + */ + public void testTotalOutOfOrder() + { + StatisticsCounter counter = new StatisticsCounter("test", 1000L); + long start = counter.getStart(); + assertEquals(0, counter.getTotal()); + counter.registerEvent(10, start + 2500); + assertEquals(10, counter.getTotal()); + counter.registerEvent(20, start + 1500); + assertEquals(30, counter.getTotal()); + counter.registerEvent(10, start + 500); + assertEquals(40, counter.getTotal()); + } + + /** + * Test that the peak rate is reported correctly. + */ + public void testPeak() throws Exception + { + StatisticsCounter counter = new StatisticsCounter("test", 1000L); + long start = counter.getStart(); + assertEquals(0.0, counter.getPeak()); + Thread.sleep(500); + counter.registerEvent(1000, start + 500); + Thread.sleep(1000); + assertEquals(1000.0, counter.getPeak()); + counter.registerEvent(2000, start + 1500); + Thread.sleep(1000); + assertEquals(2000.0, counter.getPeak()); + counter.registerEvent(1000, start + 2500); + Thread.sleep(1000); + assertEquals(2000.0, counter.getPeak()); + } + + /** + * Test that peak rate is reported correctly for out-of-order messages, + * and the total is also unaffected. + */ + public void testPeakOutOfOrder() throws Exception + { + StatisticsCounter counter = new StatisticsCounter("test", 1000L); + long start = counter.getStart(); + assertEquals(0.0, counter.getPeak()); + counter.registerEvent(1000, start + 2500); + Thread.sleep(1500); + assertEquals(0.0, counter.getPeak()); + counter.registerEvent(2000, start + 1500); + Thread.sleep(1000L); + assertEquals(0.0, counter.getPeak()); + counter.registerEvent(1000, start + 500); + Thread.sleep(1500); + assertEquals(4000.0, counter.getPeak()); + Thread.sleep(2000); + assertEquals(4000.0, counter.getPeak()); + counter.registerEvent(1000, start + 500); + assertEquals(4000.0, counter.getPeak()); + Thread.sleep(2000); + counter.registerEvent(1000); + assertEquals(4000.0, counter.getPeak()); + assertEquals(6000, counter.getTotal()); + } + + /** + * Test the current rate is generated correctly. + */ + public void testRate() throws Exception + { + StatisticsCounter counter = new StatisticsCounter("test", 1000L); + assertEquals(0.0, counter.getRate()); + Thread.sleep(500); + counter.registerEvent(1000); + Thread.sleep(1000); + assertEquals(1000.0, counter.getRate()); + counter.registerEvent(2000); + Thread.sleep(1000); + assertEquals(2000.0, counter.getRate()); + counter.registerEvent(1000); + Thread.sleep(1000); + assertEquals(1000.0, counter.getRate()); + Thread.sleep(1000); + assertEquals(0.0, counter.getRate()); + } +} diff --git a/qpid/java/broker/src/velocity/java/org/apache/qpid/server/logging/GenerateLogMessages.java b/qpid/java/broker/src/velocity/java/org/apache/qpid/server/logging/GenerateLogMessages.java index 902b86f80b..a39799a6b6 100644 --- a/qpid/java/broker/src/velocity/java/org/apache/qpid/server/logging/GenerateLogMessages.java +++ b/qpid/java/broker/src/velocity/java/org/apache/qpid/server/logging/GenerateLogMessages.java @@ -481,7 +481,7 @@ public class GenerateLogMessages // Only check the text inside the braces '{}' int typeIndexEnd = parametersString[index].indexOf("}", typeIndex); String typeString = parametersString[index].substring(typeIndex, typeIndexEnd); - if (typeString.contains("number")) + if (typeString.contains("number") || typeString.contains("choice")) { type = "Number"; } diff --git a/qpid/java/build.xml b/qpid/java/build.xml index 9031166d76..635e72bdd5 100644 --- a/qpid/java/build.xml +++ b/qpid/java/build.xml @@ -93,6 +93,10 @@ <fail if="failed" message="TEST SUITE FAILED"/> </target> + + <target name="report-module" description="generate junitreport for modules"> + <iterate target="report-module"/> + </target> <target name="jar" description="create module jars"> <iterate target="jar"/> diff --git a/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/ConnectionSetup.java b/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/ConnectionSetup.java index c4edd9034f..88ee9ed2c5 100644 --- a/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/ConnectionSetup.java +++ b/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/ConnectionSetup.java @@ -50,7 +50,7 @@ public class ConnectionSetup final static String QUEUE_NAME = "example.MyQueue"; public static final String TOPIC_JNDI_NAME = "topic"; - final static String TOPIC_NAME = "example.hierarical.topic"; + final static String TOPIC_NAME = "usa.news"; private Context _ctx; diff --git a/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Publisher.java b/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Publisher.java index dd936e429f..ac3829d49e 100644 --- a/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Publisher.java +++ b/qpid/java/client/example/src/main/java/org/apache/qpid/example/pubsub/Publisher.java @@ -71,7 +71,7 @@ public class Publisher extends Client public static void main(String[] args) { - String destination = args.length > 2 ? args[1] : null; + String destination = args.length > 2 ? args[1] : "usa.news"; int msgCount = args.length > 2 ? Integer.parseInt(args[2]) : 100; diff --git a/qpid/java/client/src/main/java/client.bnd b/qpid/java/client/src/main/java/client.bnd index 0ddd163d4f..d2bc4f8d50 100755 --- a/qpid/java/client/src/main/java/client.bnd +++ b/qpid/java/client/src/main/java/client.bnd @@ -17,7 +17,7 @@ # under the License. # -ver: 0.9.0 +ver: 0.11.0 Bundle-SymbolicName: qpid-client Bundle-Version: ${ver} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java index ee52cd50af..b31dd2bc91 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQBrokerDetails.java @@ -105,6 +105,21 @@ public class AMQBrokerDetails implements BrokerDetails if (host == null) { host = ""; + + String auth = connection.getAuthority(); + if (auth != null) + { + // contains both host & port myhost:5672 + if (auth.contains(":")) + { + host = auth.substring(0,auth.indexOf(":")); + } + else + { + host = auth; + } + } + } setHost(host); diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java index b0bd8f8e97..891819c227 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnectionDelegate_0_10.java @@ -69,6 +69,12 @@ public class AMQConnectionDelegate_0_10 implements AMQConnectionDelegate, Connec org.apache.qpid.transport.Connection _qpidConnection; private ConnectionException exception = null; + static + { + // Register any configured SASL client factories. + org.apache.qpid.client.security.DynamicSaslRegistrar.registerSaslProviders(); + } + //--- constructor public AMQConnectionDelegate_0_10(AMQConnection conn) { diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java index 1f940b62f0..5c2949960c 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java @@ -1043,7 +1043,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic throws JMSException { checkNotClosed(); - AMQTopic origTopic = checkValidTopic(topic, true); + Topic origTopic = checkValidTopic(topic, true); AMQTopic dest = AMQTopic.createDurableTopic(origTopic, name, _connection); String messageSelector = ((selector == null) || (selector.trim().length() == 0)) ? null : selector; @@ -1307,8 +1307,8 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic public QueueReceiver createQueueReceiver(Destination destination) throws JMSException { checkValidDestination(destination); - AMQQueue dest = (AMQQueue) destination; - C consumer = (C) createConsumer(destination); + Queue dest = validateQueue(destination); + C consumer = (C) createConsumer(dest); return new QueueReceiverAdaptor(dest, consumer); } @@ -1326,8 +1326,8 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic public QueueReceiver createQueueReceiver(Destination destination, String messageSelector) throws JMSException { checkValidDestination(destination); - AMQQueue dest = (AMQQueue) destination; - C consumer = (C) createConsumer(destination, messageSelector); + Queue dest = validateQueue(destination); + C consumer = (C) createConsumer(dest, messageSelector); return new QueueReceiverAdaptor(dest, consumer); } @@ -1344,7 +1344,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic public QueueReceiver createReceiver(Queue queue) throws JMSException { checkNotClosed(); - AMQQueue dest = (AMQQueue) queue; + Queue dest = validateQueue(queue); C consumer = (C) createConsumer(dest); return new QueueReceiverAdaptor(dest, consumer); @@ -1363,11 +1363,23 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic public QueueReceiver createReceiver(Queue queue, String messageSelector) throws JMSException { checkNotClosed(); - AMQQueue dest = (AMQQueue) queue; + Queue dest = validateQueue(queue); C consumer = (C) createConsumer(dest, messageSelector); return new QueueReceiverAdaptor(dest, consumer); } + + private Queue validateQueue(Destination dest) throws InvalidDestinationException + { + if (dest instanceof AMQDestination && dest instanceof javax.jms.Queue) + { + return (Queue)dest; + } + else + { + throw new InvalidDestinationException("The destination object used is not from this provider or of type javax.jms.Queue"); + } + } public QueueSender createSender(Queue queue) throws JMSException { @@ -1408,7 +1420,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic public TopicSubscriber createSubscriber(Topic topic) throws JMSException { checkNotClosed(); - AMQTopic dest = checkValidTopic(topic); + Topic dest = checkValidTopic(topic); // AMQTopic dest = new AMQTopic(topic.getTopicName()); return new TopicSubscriberAdaptor(dest, (C) createExclusiveConsumer(dest)); @@ -1428,7 +1440,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic public TopicSubscriber createSubscriber(Topic topic, String messageSelector, boolean noLocal) throws JMSException { checkNotClosed(); - AMQTopic dest = checkValidTopic(topic); + Topic dest = checkValidTopic(topic); // AMQTopic dest = new AMQTopic(topic.getTopicName()); return new TopicSubscriberAdaptor(dest, (C) createExclusiveConsumer(dest, messageSelector, noLocal)); @@ -2395,7 +2407,7 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic /* * I could have combined the last 3 methods, but this way it improves readability */ - protected AMQTopic checkValidTopic(Topic topic, boolean durable) throws JMSException + protected Topic checkValidTopic(Topic topic, boolean durable) throws JMSException { if (topic == null) { @@ -2414,17 +2426,17 @@ public abstract class AMQSession<C extends BasicMessageConsumer, P extends Basic ("Cannot create a durable subscription with a temporary topic: " + topic); } - if (!(topic instanceof AMQTopic)) + if (!(topic instanceof AMQDestination && topic instanceof javax.jms.Topic)) { throw new javax.jms.InvalidDestinationException( "Cannot create a subscription on topic created for another JMS Provider, class of topic provided is: " + topic.getClass().getName()); } - return (AMQTopic) topic; + return topic; } - protected AMQTopic checkValidTopic(Topic topic) throws JMSException + protected Topic checkValidTopic(Topic topic) throws JMSException { return checkValidTopic(topic, false); } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java index 517a7a5ce8..3f84716df7 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession_0_10.java @@ -47,6 +47,8 @@ import org.apache.qpid.client.message.AMQMessageDelegateFactory; import org.apache.qpid.client.message.FieldTableSupport; import org.apache.qpid.client.message.MessageFactoryRegistry; import org.apache.qpid.client.message.UnprocessedMessage_0_10; +import org.apache.qpid.client.messaging.address.Link; +import org.apache.qpid.client.messaging.address.Link.Reliability; import org.apache.qpid.client.messaging.address.Node.ExchangeNode; import org.apache.qpid.client.messaging.address.Node.QueueNode; import org.apache.qpid.client.protocol.AMQProtocolHandler; @@ -56,6 +58,7 @@ import org.apache.qpid.framing.FieldTable; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.transport.ExchangeBoundResult; import org.apache.qpid.transport.ExchangeQueryResult; +import org.apache.qpid.transport.ExecutionErrorCode; import org.apache.qpid.transport.ExecutionException; import org.apache.qpid.transport.MessageAcceptMode; import org.apache.qpid.transport.MessageAcquireMode; @@ -600,10 +603,16 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic (Map<? extends String, ? extends Object>) consumer.getDestination().getLink().getSubscription().getArgs()); } + boolean acceptModeNone = getAcknowledgeMode() == NO_ACKNOWLEDGE; + + if (consumer.getDestination().getLink() != null) + { + acceptModeNone = consumer.getDestination().getLink().getReliability() == Link.Reliability.UNRELIABLE; + } getQpidSession().messageSubscribe (queueName.toString(), String.valueOf(tag), - getAcknowledgeMode() == NO_ACKNOWLEDGE ? MessageAcceptMode.NONE : MessageAcceptMode.EXPLICIT, + acceptModeNone ? MessageAcceptMode.NONE : MessageAcceptMode.EXPLICIT, preAcquire ? MessageAcquireMode.PRE_ACQUIRED : MessageAcquireMode.NOT_ACQUIRED, null, 0, arguments, consumer.isExclusive() ? Option.EXCLUSIVE : Option.NONE); } @@ -1068,22 +1077,37 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic return match; } - public boolean isQueueExist(AMQDestination dest,QueueNode node,boolean assertNode) + public boolean isQueueExist(AMQDestination dest,QueueNode node,boolean assertNode) throws AMQException { boolean match = true; - QueueQueryResult result = getQpidSession().queueQuery(dest.getAddressName(), Option.NONE).get(); - match = dest.getAddressName().equals(result.getQueue()); - - if (match && assertNode) + try { - match = (result.getDurable() == node.isDurable()) && - (result.getAutoDelete() == node.isAutoDelete()) && - (result.getExclusive() == node.isExclusive()) && - (matchProps(result.getArguments(),node.getDeclareArgs())); + QueueQueryResult result = getQpidSession().queueQuery(dest.getAddressName(), Option.NONE).get(); + match = dest.getAddressName().equals(result.getQueue()); + + if (match && assertNode) + { + match = (result.getDurable() == node.isDurable()) && + (result.getAutoDelete() == node.isAutoDelete()) && + (result.getExclusive() == node.isExclusive()) && + (matchProps(result.getArguments(),node.getDeclareArgs())); + } + else if (match) + { + // should I use the queried details to update the local data structure. + } } - else if (match) + catch(SessionException e) { - // should I use the queried details to update the local data structure. + if (e.getException().getErrorCode() == ExecutionErrorCode.RESOURCE_DELETED) + { + match = false; + } + else + { + throw new AMQException(AMQConstant.getConstant(e.getException().getErrorCode().getValue()), + "Error querying queue",e); + } } return match; @@ -1149,6 +1173,22 @@ public class AMQSession_0_10 extends AMQSession<BasicMessageConsumer_0_10, Basic int type = resolveAddressType(dest); + if (type == AMQDestination.QUEUE_TYPE && + dest.getLink().getReliability() == Reliability.UNSPECIFIED) + { + dest.getLink().setReliability(Reliability.AT_LEAST_ONCE); + } + else if (type == AMQDestination.TOPIC_TYPE && + dest.getLink().getReliability() == Reliability.UNSPECIFIED) + { + dest.getLink().setReliability(Reliability.UNRELIABLE); + } + else if (type == AMQDestination.TOPIC_TYPE && + dest.getLink().getReliability() == Reliability.AT_LEAST_ONCE) + { + throw new AMQException("AT-LEAST-ONCE is not yet supported for Topics"); + } + switch (type) { case AMQDestination.QUEUE_TYPE: diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java index 6217cb534a..c573da9def 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQTopic.java @@ -22,6 +22,7 @@ package org.apache.qpid.client; import java.net.URISyntaxException; +import javax.jms.InvalidDestinationException; import javax.jms.JMSException; import javax.jms.Topic; @@ -95,39 +96,47 @@ public class AMQTopic extends AMQDestination implements Topic super(exchangeName, exchangeClass, routingKey, isExclusive, isAutoDelete, queueName, isDurable,bindingKeys); } - public static AMQTopic createDurableTopic(AMQTopic topic, String subscriptionName, AMQConnection connection) + public static AMQTopic createDurableTopic(Topic topic, String subscriptionName, AMQConnection connection) throws JMSException { - if (topic.getDestSyntax() == DestSyntax.ADDR) + if (topic instanceof AMQDestination && topic instanceof javax.jms.Topic) { - try + AMQDestination qpidTopic = (AMQDestination)topic; + if (qpidTopic.getDestSyntax() == DestSyntax.ADDR) { - AMQTopic t = new AMQTopic(topic.getAddress()); - AMQShortString queueName = getDurableTopicQueueName(subscriptionName, connection); - // link is never null if dest was created using an address string. - t.getLink().setName(queueName.asString()); - t.getSourceNode().setAutoDelete(false); - t.getSourceNode().setDurable(true); - - // The legacy fields are also populated just in case. - t.setQueueName(queueName); - t.setAutoDelete(false); - t.setDurable(true); - return t; + try + { + AMQTopic t = new AMQTopic(qpidTopic.getAddress()); + AMQShortString queueName = getDurableTopicQueueName(subscriptionName, connection); + // link is never null if dest was created using an address string. + t.getLink().setName(queueName.asString()); + t.getSourceNode().setAutoDelete(false); + t.getSourceNode().setDurable(true); + + // The legacy fields are also populated just in case. + t.setQueueName(queueName); + t.setAutoDelete(false); + t.setDurable(true); + return t; + } + catch(Exception e) + { + JMSException ex = new JMSException("Error creating durable topic"); + ex.initCause(e); + ex.setLinkedException(e); + throw ex; + } } - catch(Exception e) + else { - JMSException ex = new JMSException("Error creating durable topic"); - ex.initCause(e); - ex.setLinkedException(e); - throw ex; + return new AMQTopic(qpidTopic.getExchangeName(), qpidTopic.getRoutingKey(), false, + getDurableTopicQueueName(subscriptionName, connection), + true); } } else { - return new AMQTopic(topic.getExchangeName(), topic.getRoutingKey(), false, - getDurableTopicQueueName(subscriptionName, connection), - true); + throw new InvalidDestinationException("The destination object used is not from this provider or of type javax.jms.Topic"); } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java index 0a78403268..5d32863f2f 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java @@ -571,6 +571,7 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa if (!_session.isClosed() || _session.isClosing()) { sendCancel(); + cleanupQueue(); } } catch (AMQException e) @@ -608,6 +609,8 @@ public abstract class BasicMessageConsumer<U> extends Closeable implements Messa } abstract void sendCancel() throws AMQException, FailoverException; + + abstract void cleanupQueue() throws AMQException, FailoverException; /** * Called when you need to invalidate a consumer. Used for example when failover has occurred and the client has diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java index b5f3501e5a..964c238946 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_10.java @@ -19,8 +19,11 @@ package org.apache.qpid.client; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.apache.qpid.client.AMQDestination.AddressOption; import org.apache.qpid.client.AMQDestination.DestSyntax; +import org.apache.qpid.client.failover.FailoverException; import org.apache.qpid.client.message.*; +import org.apache.qpid.client.messaging.address.Node.QueueNode; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.FieldTable; @@ -509,4 +512,18 @@ public class BasicMessageConsumer_0_10 extends BasicMessageConsumer<UnprocessedM return _exclusive; } } + + void cleanupQueue() throws AMQException, FailoverException + { + AMQDestination dest = this.getDestination(); + if (dest != null && dest.getDestSyntax() == AMQDestination.DestSyntax.ADDR) + { + if (dest.getDelete() == AddressOption.ALWAYS || + dest.getDelete() == AddressOption.RECEIVER ) + { + ((AMQSession_0_10) getSession()).getQpidSession().queueDelete( + this.getDestination().getQueueName()); + } + } + } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java index cdbf57769d..00acd5e866 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer_0_8.java @@ -88,4 +88,8 @@ public class BasicMessageConsumer_0_8 extends BasicMessageConsumer<UnprocessedMe return receive(); } + void cleanupQueue() throws AMQException, FailoverException + { + + } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java index 53c0457120..36f2dfb66f 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer_0_10.java @@ -19,6 +19,7 @@ package org.apache.qpid.client; import static org.apache.qpid.transport.Option.NONE; import static org.apache.qpid.transport.Option.SYNC; +import static org.apache.qpid.transport.Option.UNRELIABLE; import java.nio.ByteBuffer; import java.util.HashMap; @@ -30,9 +31,12 @@ import javax.jms.JMSException; import javax.jms.Message; import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQDestination.AddressOption; import org.apache.qpid.client.AMQDestination.DestSyntax; import org.apache.qpid.client.message.AMQMessageDelegate_0_10; import org.apache.qpid.client.message.AbstractJMSMessage; +import org.apache.qpid.client.messaging.address.Link.Reliability; +import org.apache.qpid.client.messaging.address.Node.QueueNode; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.transport.DeliveryProperties; import org.apache.qpid.transport.Header; @@ -210,6 +214,9 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer deliveryMode == DeliveryMode.PERSISTENT) ); + boolean unreliable = (destination.getDestSyntax() == DestSyntax.ADDR) && + (destination.getLink().getReliability() == Reliability.UNRELIABLE); + org.apache.mina.common.ByteBuffer data = message.getData(); ByteBuffer buffer = data == null ? ByteBuffer.allocate(0) : data.buf().slice(); @@ -217,7 +224,7 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer MessageAcceptMode.NONE, MessageAcquireMode.PRE_ACQUIRED, new Header(deliveryProp, messageProps), - buffer, sync ? SYNC : NONE); + buffer, sync ? SYNC : NONE, unreliable ? UNRELIABLE : NONE); if (sync) { ssn.sync(); @@ -239,5 +246,21 @@ public class BasicMessageProducer_0_10 extends BasicMessageProducer { return _session.isQueueBound(destination); } + + @Override + public void close() + { + super.close(); + AMQDestination dest = _destination; + if (dest != null && dest.getDestSyntax() == AMQDestination.DestSyntax.ADDR) + { + if (dest.getDelete() == AddressOption.ALWAYS || + dest.getDelete() == AddressOption.SENDER ) + { + ((AMQSession_0_10) getSession()).getQpidSession().queueDelete( + _destination.getQueueName()); + } + } + } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java b/qpid/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java index 27783bcacf..295c6a4091 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java @@ -50,25 +50,25 @@ public class QueueSenderAdapter implements QueueSender public void send(Message msg) throws JMSException { - checkPreConditions(); + checkQueuePreConditions(_queue); _delegate.send(msg); } public void send(Queue queue, Message msg) throws JMSException { - checkPreConditions(queue); + checkQueuePreConditions(queue); _delegate.send(queue, msg); } public void publish(Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException { - checkPreConditions(); + checkQueuePreConditions(_queue); _delegate.send(msg, deliveryMode, priority, timeToLive); } public void send(Queue queue, Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException { - checkPreConditions(queue); + checkQueuePreConditions(queue); _delegate.send(queue, msg, deliveryMode, priority, timeToLive); } @@ -122,19 +122,19 @@ public class QueueSenderAdapter implements QueueSender public void send(Destination dest, Message msg) throws JMSException { - checkPreConditions((Queue) dest); + checkQueuePreConditions((Queue) dest); _delegate.send(dest, msg); } public void send(Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException { - checkPreConditions(); + checkQueuePreConditions(_queue); _delegate.send(msg, deliveryMode, priority, timeToLive); } public void send(Destination dest, Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException { - checkPreConditions((Queue) dest); + checkQueuePreConditions((Queue) dest); _delegate.send(dest, msg, deliveryMode, priority, timeToLive); } @@ -170,11 +170,6 @@ public class QueueSenderAdapter implements QueueSender private void checkPreConditions() throws JMSException { - checkPreConditions(_queue); - } - - private void checkPreConditions(Queue queue) throws JMSException - { if (closed) { throw new javax.jms.IllegalStateException("Publisher is closed"); @@ -186,39 +181,43 @@ public class QueueSenderAdapter implements QueueSender { throw new javax.jms.IllegalStateException("Invalid Session"); } + } - if (queue == null) - { - throw new UnsupportedOperationException("Queue is null."); - } - - if (!(queue instanceof AMQDestination)) - { - throw new InvalidDestinationException("Queue: " + queue + " is not a valid Qpid queue"); - } - - AMQDestination destination = (AMQDestination) queue; - if (!destination.isCheckedForQueueBinding() && checkQueueBeforePublish()) - { - - if (_delegate.getSession().isStrictAMQP()) - { - _delegate._logger.warn("AMQP does not support destination validation before publish, "); - destination.setCheckedForQueueBinding(true); - } - else - { - if (_delegate.isBound(destination)) - { - destination.setCheckedForQueueBinding(true); - } - else - { - throw new InvalidDestinationException("Queue: " + queue - + " is not a valid destination (no bindings on server"); - } - } - } + private void checkQueuePreConditions(Queue queue) throws JMSException + { + checkPreConditions() ; + + if (queue == null) + { + throw new UnsupportedOperationException("Queue is null."); + } + + if (!(queue instanceof AMQDestination)) + { + throw new InvalidDestinationException("Queue: " + queue + " is not a valid Qpid queue"); + } + + AMQDestination destination = (AMQDestination) queue; + if (!destination.isCheckedForQueueBinding() && checkQueueBeforePublish()) + { + if (_delegate.getSession().isStrictAMQP()) + { + _delegate._logger.warn("AMQP does not support destination validation before publish, "); + destination.setCheckedForQueueBinding(true); + } + else + { + if (_delegate.isBound(destination)) + { + destination.setCheckedForQueueBinding(true); + } + else + { + throw new InvalidDestinationException("Queue: " + queue + + " is not a valid destination (no bindings on server"); + } + } + } } private boolean checkQueueBeforePublish() diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_10.java b/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_10.java index 92e61984d2..fb7b191656 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_10.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQMessageDelegate_0_10.java @@ -22,10 +22,12 @@ package org.apache.qpid.client.message; import java.lang.ref.SoftReference; +import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; @@ -670,7 +672,19 @@ public class AMQMessageDelegate_0_10 extends AbstractAMQMessageDelegate public Enumeration getPropertyNames() throws JMSException { - return java.util.Collections.enumeration(getApplicationHeaders().keySet()); + List<String> props = new ArrayList<String>(); + Map<String, Object> propertyMap = getApplicationHeaders(); + for (String prop: getApplicationHeaders().keySet()) + { + Object value = propertyMap.get(prop); + if (value instanceof Boolean || value instanceof Number + || value instanceof String) + { + props.add(prop); + } + } + + return java.util.Collections.enumeration(props); } public void setBooleanProperty(String propertyName, boolean b) throws JMSException diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQPEncodedMapMessage.java b/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQPEncodedMapMessage.java index 6e22292ee0..58f108f1a4 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQPEncodedMapMessage.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/message/AMQPEncodedMapMessage.java @@ -23,6 +23,7 @@ package org.apache.qpid.client.message; import java.util.List; import java.util.Map; +import java.util.UUID; import javax.jms.JMSException; import javax.jms.MessageFormatException; @@ -65,7 +66,7 @@ public class AMQPEncodedMapMessage extends JMSMapMessage if ((value instanceof Boolean) || (value instanceof Byte) || (value instanceof Short) || (value instanceof Integer) || (value instanceof Long) || (value instanceof Character) || (value instanceof Float) || (value instanceof Double) || (value instanceof String) || (value instanceof byte[]) - || (value instanceof List) || (value instanceof Map) || (value == null)) + || (value instanceof List) || (value instanceof Map) || (value instanceof UUID) || (value == null)) { _map.put(propName, value); } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/AddressHelper.java b/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/AddressHelper.java index 00503cc650..e454a8eee4 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/AddressHelper.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/AddressHelper.java @@ -27,6 +27,7 @@ import java.util.Map; import org.apache.qpid.client.AMQDestination; import org.apache.qpid.client.AMQDestination.Binding; +import org.apache.qpid.client.messaging.address.Link.Reliability; import org.apache.qpid.client.messaging.address.Link.Subscription; import org.apache.qpid.client.messaging.address.Node.ExchangeNode; import org.apache.qpid.client.messaging.address.Node.QueueNode; @@ -262,7 +263,7 @@ public class AddressHelper } } - public Link getLink() + public Link getLink() throws Exception { Link link = new Link(); link.setSubscription(new Subscription()); @@ -272,6 +273,25 @@ public class AddressHelper : linkProps.getBoolean(DURABLE)); link.setName(linkProps.getString(NAME)); + String reliability = linkProps.getString(RELIABILITY); + if ( reliability != null) + { + if (reliability.equalsIgnoreCase("unreliable")) + { + link.setReliability(Reliability.UNRELIABLE); + } + else if (reliability.equalsIgnoreCase("at-least-once")) + { + link.setReliability(Reliability.AT_LEAST_ONCE); + } + else + { + throw new Exception("The reliability mode '" + + reliability + "' is not yet supported"); + } + + } + if (((Map) address.getOptions().get(LINK)).get(CAPACITY) instanceof Map) { MapAccessor capacityProps = new MapAccessor( diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/Link.java b/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/Link.java index a7d19d1bd5..5f97d625b4 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/Link.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/messaging/address/Link.java @@ -20,6 +20,8 @@ */ package org.apache.qpid.client.messaging.address; +import static org.apache.qpid.client.messaging.address.Link.Reliability.UNSPECIFIED; + import java.util.HashMap; import java.util.Map; @@ -29,6 +31,8 @@ public class Link { public enum FilterType { SQL92, XQUERY, SUBJECT } + public enum Reliability { UNRELIABLE, AT_MOST_ONCE, AT_LEAST_ONCE, EXACTLY_ONCE, UNSPECIFIED } + protected String name; protected String _filter; protected FilterType _filterType = FilterType.SUBJECT; @@ -38,7 +42,18 @@ public class Link protected int _producerCapacity = 0; protected Node node; protected Subscription subscription; + protected Reliability reliability = UNSPECIFIED; + public Reliability getReliability() + { + return reliability; + } + + public void setReliability(Reliability reliability) + { + this.reliability = reliability; + } + public Node getNode() { return node; diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties b/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties index 1bff43142b..b903208927 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.properties @@ -18,3 +18,4 @@ # AMQPLAIN=org.apache.qpid.client.security.amqplain.AmqPlainSaslClientFactory CRAM-MD5-HASHED=org.apache.qpid.client.security.crammd5hashed.CRAMMD5HashedSaslClientFactory +ANONYMOUS=org.apache.qpid.client.security.anonymous.AnonymousSaslClientFactory diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/security/anonymous/AnonymousSaslClient.java b/qpid/java/client/src/main/java/org/apache/qpid/client/security/anonymous/AnonymousSaslClient.java new file mode 100644 index 0000000000..0f56b2ef6c --- /dev/null +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/security/anonymous/AnonymousSaslClient.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.client.security.anonymous; + +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +public class AnonymousSaslClient implements SaslClient +{ + public String getMechanismName() { + return "ANONYMOUS"; + } + public boolean hasInitialResponse() { + return true; + } + public byte[] evaluateChallenge(byte[] challenge) throws SaslException { + return new byte[0]; + } + public boolean isComplete() { + return true; + } + public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException + { + throw new IllegalStateException("No security layer supported"); + } + public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException + { + throw new IllegalStateException("No security layer supported"); + } + public Object getNegotiatedProperty(String propName) { + return null; + } + public void dispose() throws SaslException {} +} diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/security/anonymous/AnonymousSaslClientFactory.java b/qpid/java/client/src/main/java/org/apache/qpid/client/security/anonymous/AnonymousSaslClientFactory.java new file mode 100644 index 0000000000..de698f87c6 --- /dev/null +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/security/anonymous/AnonymousSaslClientFactory.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.client.security.anonymous; + +import java.util.Arrays; +import java.util.Map; + +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslClientFactory; +import javax.security.sasl.SaslException; +import javax.security.auth.callback.CallbackHandler; + +public class AnonymousSaslClientFactory implements SaslClientFactory +{ + public SaslClient createSaslClient(String[] mechanisms, String authId, + String protocol, String server, + Map props, CallbackHandler cbh) throws SaslException + { + if (Arrays.asList(mechanisms).contains("ANONYMOUS")) { + return new AnonymousSaslClient(); + } else { + return null; + } + } + public String[] getMechanismNames(Map props) + { + if (props == null || props.isEmpty()) { + return new String[]{"ANONYMOUS"}; + } else { + return new String[0]; + } + } +} diff --git a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java index 2be3720c20..2c5fa0112e 100644 --- a/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java +++ b/qpid/java/client/src/test/java/org/apache/qpid/test/unit/client/connectionurl/ConnectionURLTest.java @@ -549,6 +549,37 @@ public class ConnectionURLTest extends TestCase assertTrue("String representation should contain options and values", url.toString().contains("maxprefetch='12345'")); } + public void testHostNamesWithUnderScore() throws URLSyntaxException + { + String url = "amqp://guest:guest@clientid/test?brokerlist='tcp://under_score:6672'"; + + ConnectionURL connectionurl = new AMQConnectionURL(url); + + assertTrue(connectionurl.getUsername().equals("guest")); + assertTrue(connectionurl.getPassword().equals("guest")); + assertTrue(connectionurl.getVirtualHost().equals("/test")); + + assertTrue(connectionurl.getBrokerCount() == 1); + BrokerDetails service = connectionurl.getBrokerDetails(0); + assertTrue(service.getTransport().equals("tcp")); + assertTrue(service.getHost().equals("under_score")); + assertTrue(service.getPort() == 6672); + + url = "amqp://guest:guest@clientid/test?brokerlist='tcp://under_score'"; + + connectionurl = new AMQConnectionURL(url); + + assertTrue(connectionurl.getUsername().equals("guest")); + assertTrue(connectionurl.getPassword().equals("guest")); + assertTrue(connectionurl.getVirtualHost().equals("/test")); + + assertTrue(connectionurl.getBrokerCount() == 1); + service = connectionurl.getBrokerDetails(0); + assertTrue(service.getTransport().equals("tcp")); + assertTrue(service.getHost().equals("under_score")); + assertTrue(service.getPort() == 5672); + } + public static junit.framework.Test suite() { return new junit.framework.TestSuite(ConnectionURLTest.class); diff --git a/qpid/java/common.xml b/qpid/java/common.xml index 066859b29f..a8d9c0dea4 100644 --- a/qpid/java/common.xml +++ b/qpid/java/common.xml @@ -23,7 +23,7 @@ <dirname property="project.root" file="${ant.file.common}"/> <property name="project.name" value="qpid"/> - <property name="project.version" value="0.9"/> + <property name="project.version" value="0.11"/> <property name="project.namever" value="${project.name}-${project.version}"/> <property name="resources" location="${project.root}/resources"/> diff --git a/qpid/java/common/src/main/java/common.bnd b/qpid/java/common/src/main/java/common.bnd index ef56ecec9e..89c397f400 100755 --- a/qpid/java/common/src/main/java/common.bnd +++ b/qpid/java/common/src/main/java/common.bnd @@ -17,7 +17,7 @@ # under the License.
#
-ver: 0.9.0
+ver: 0.11.0
Bundle-SymbolicName: qpid-common
Bundle-Version: ${ver}
diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/Connection.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/Connection.java index e5e10c0e07..dc32569ee8 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/Connection.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/Connection.java @@ -28,6 +28,7 @@ import static org.apache.qpid.transport.Connection.State.OPENING; import static org.apache.qpid.transport.Connection.State.RESUMING; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -695,4 +696,8 @@ public class Connection extends ConnectionInvoker return connectionLost.get(); } + protected Collection<Session> getChannels() + { + return channels.values(); + } } diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/Session.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/Session.java index 214d4534c1..d961507382 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/Session.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/Session.java @@ -422,7 +422,10 @@ public class Session extends SessionInvoker { return; } - sessionCompleted(copy, options); + if (copy.size() > 0) + { + sessionCompleted(copy, options); + } } } @@ -661,7 +664,12 @@ public class Session extends SessionInvoker { sessionCommandPoint(0, 0); } - if ((!closing && !transacted && m instanceof MessageTransfer) || m.hasCompletionListener()) + + boolean replayTransfer = !closing && !transacted && + m instanceof MessageTransfer && + ! m.isUnreliable(); + + if ((replayTransfer) || m.hasCompletionListener()) { commands[mod(next, commands.length)] = m; commandBytes += m.getBodySize(); diff --git a/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/AbstractEncoder.java b/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/AbstractEncoder.java index 908d14a307..0ccfcfcb70 100644 --- a/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/AbstractEncoder.java +++ b/qpid/java/common/src/main/java/org/apache/qpid/transport/codec/AbstractEncoder.java @@ -63,6 +63,7 @@ abstract class AbstractEncoder implements Encoder ENCODINGS.put(Double.class, Type.DOUBLE); ENCODINGS.put(Character.class, Type.CHAR); ENCODINGS.put(byte[].class, Type.VBIN32); + ENCODINGS.put(UUID.class, Type.UUID); } private final Map<String,byte[]> str8cache = new LinkedHashMap<String,byte[]>() diff --git a/qpid/java/management/common/src/main/java/management-common.bnd b/qpid/java/management/common/src/main/java/management-common.bnd index cb28d309a6..66eb9f156b 100644 --- a/qpid/java/management/common/src/main/java/management-common.bnd +++ b/qpid/java/management/common/src/main/java/management-common.bnd @@ -17,7 +17,7 @@ # under the License. # -ver: 0.9.0 +ver: 0.11.0 Bundle-SymbolicName: qpid-management-common Bundle-Version: ${ver} 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 c0e7293611..b48ad3f856 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 @@ -36,8 +36,8 @@ import org.apache.qpid.management.common.mbeans.annotations.MBeanOperationParame * The ManagedBroker is the management interface to expose management * features of the Broker. * - * @author Bhupendra Bhardwaj - * @version 0.1 + * @version Qpid JMX API 2.1 + * @since Qpid JMX API 1.3 */ public interface ManagedBroker { @@ -131,4 +131,118 @@ public interface ManagedBroker impact= MBeanOperationInfo.ACTION) void deleteQueue(@MBeanOperationParameter(name= ManagedQueue.TYPE, description="Queue Name")String queueName) throws IOException, JMException, MBeanException; + + /** + * Resets all message and data statistics for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanOperation(name="resetStatistics", + description="Resets all message and data statistics for the virtual host", + impact= MBeanOperationInfo.ACTION) + void resetStatistics() throws Exception; + + /** + * Peak rate of messages delivered per second for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="PeakMessageDeliveryRate", description=TYPE + " Peak Message Delivery Rate") + double getPeakMessageDeliveryRate(); + + /** + * Peak rate of bytes delivered per second for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="PeakDataDeliveryRate", description=TYPE + " Peak Data Delivery Rate") + double getPeakDataDeliveryRate(); + + /** + * Rate of messages delivered per second for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="MessageDeliveryRate", description=TYPE + " Message Delivery Rate") + double getMessageDeliveryRate(); + + /** + * Rate of bytes delivered per second for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="DataDeliveryRate", description=TYPE + " Data Delivery Rate") + double getDataDeliveryRate(); + + /** + * Total count of messages delivered for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="TotalMessagesDelivered", description=TYPE + " Total Messages Delivered") + long getTotalMessagesDelivered(); + + /** + * Total count of bytes for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="TotalDataDelivered", description=TYPE + " Total Data Delivered") + long getTotalDataDelivered(); + + /** + * Peak rate of messages received per second for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="PeakMessageReceiptRate", description=TYPE + " Peak Message Receipt Rate") + double getPeakMessageReceiptRate(); + + /** + * Peak rate of bytes received per second for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="PeakDataReceiptRate", description=TYPE + " Peak Data Receipt Rate") + double getPeakDataReceiptRate(); + + /** + * Rate of messages received per second for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="MessageReceiptRate", description=TYPE + " Message Receipt Rate") + double getMessageReceiptRate(); + + /** + * Rate of bytes received per second for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="DataReceiptRate", description=TYPE + " Data Receipt Rate") + double getDataReceiptRate(); + + /** + * Total count of messages received for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="TotalMessagesReceived", description=TYPE + " Total Messages Received") + long getTotalMessagesReceived(); + + /** + * Total count of bytes received for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="TotalDataReceived", description=TYPE + " Total Data Received") + long getTotalDataReceived(); + + /** + * Is statistics collection enabled for this connection. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="StatisticsEnabled", description=TYPE + " Statistics Enabled") + boolean isStatisticsEnabled(); } diff --git a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedConnection.java b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedConnection.java index d6b79d1dde..6ef0fd5b19 100644 --- a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedConnection.java +++ b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ManagedConnection.java @@ -37,8 +37,9 @@ import org.apache.qpid.management.common.mbeans.annotations.MBeanOperationParame /** * The management interface exposed to allow management of Connections. - * @author Bhupendra Bhardwaj - * @version 0.1 + * + * @version Qpid JMX API 2.1 + * @since Qpid JMX API 1.3 */ public interface ManagedConnection { @@ -145,4 +146,120 @@ public interface ManagedConnection description="Closes this connection and all related channels", impact= MBeanOperationInfo.ACTION) void closeConnection() throws Exception; + + /** + * Resets message and data statistics for this connection. + * + * @since Qpid JMX API 2.1 + */ + @MBeanOperation(name="resetStatistics", + description="Resets message and data statistics for this connection", + impact= MBeanOperationInfo.ACTION) + void resetStatistics() throws Exception; + + /** + * Peak rate of messages delivered per second for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="PeakMessageDeliveryRate", description=TYPE + " Peak Message Delivery Rate") + double getPeakMessageDeliveryRate(); + + /** + * Peak rate of bytes delivered per second for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="PeakDataDeliveryRate", description=TYPE + " Peak Data Delivery Rate") + double getPeakDataDeliveryRate(); + + /** + * Rate of messages delivered per second for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="MessageDeliveryRate", description=TYPE + " Message Delivery Rate") + double getMessageDeliveryRate(); + + /** + * Rate of bytes delivered per second for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="DataDeliveryRate", description=TYPE + " Data Delivery Rate") + double getDataDeliveryRate(); + + /** + * Total count of messages delivered for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="TotalMessagesDelivered", description=TYPE + " Total Messages Delivered") + long getTotalMessagesDelivered(); + + /** + * Total count of bytes for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="TotalDataDelivered", description=TYPE + " Total Data Delivered") + long getTotalDataDelivered(); + + /** + * Peak rate of messages received per second for this connection. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="PeakMessageReceiptRate", description=TYPE + " Peak Message Receipt Rate") + double getPeakMessageReceiptRate(); + + /** + * Peak rate of bytes received per second for this connection. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="PeakDataReceiptRate", description=TYPE + " Peak Data Receipt Rate") + double getPeakDataReceiptRate(); + + /** + * Rate of messages received per second for this connection. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="MessageReceiptRate", description=TYPE + " Message Receipt Rate") + double getMessageReceiptRate(); + + /** + * Rate of bytes received per second for this connection. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="DataReceiptRate", description=TYPE + " Data Receipt Rate") + double getDataReceiptRate(); + + /** + * Total count of messages received for this connection. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="TotalMessagesReceived", description=TYPE + " Total Messages Received") + long getTotalMessagesReceived(); + + /** + * Total count of bytes received for this connection. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="TotalDataReceived", description=TYPE + " Total Data Received") + long getTotalDataReceived(); + + /** + * Is statistics collection enabled for this connection. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="StatisticsEnabled", description=TYPE + " Statistics Enabled") + boolean isStatisticsEnabled(); + + void setStatisticsEnabled(boolean enabled); } diff --git a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ServerInformation.java b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ServerInformation.java index 618403fdca..abee6a745e 100644 --- a/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ServerInformation.java +++ b/qpid/java/management/common/src/main/java/org/apache/qpid/management/common/mbeans/ServerInformation.java @@ -22,10 +22,15 @@ package org.apache.qpid.management.common.mbeans; import java.io.IOException; +import javax.management.MBeanOperationInfo; + import org.apache.qpid.management.common.mbeans.annotations.MBeanAttribute; +import org.apache.qpid.management.common.mbeans.annotations.MBeanOperation; /** * Interface for the ServerInformation MBean + * + * @version Qpid JMX API 2.1 * @since Qpid JMX API 1.3 */ public interface ServerInformation @@ -42,7 +47,7 @@ public interface ServerInformation * Qpid JMX API 1.1 can be assumed. */ int QPID_JMX_API_MAJOR_VERSION = 2; - int QPID_JMX_API_MINOR_VERSION = 0; + int QPID_JMX_API_MINOR_VERSION = 1; /** @@ -80,4 +85,118 @@ public interface ServerInformation @MBeanAttribute(name="ProductVersion", description = "The product version string") String getProductVersion() throws IOException; + + /** + * Resets all message and data statistics for the broker. + * + * @since Qpid JMX API 2.1 + */ + @MBeanOperation(name="resetStatistics", + description="Resets all message and data statistics for the broker", + impact= MBeanOperationInfo.ACTION) + void resetStatistics() throws Exception; + + /** + * Peak rate of messages delivered per second for the virtual host. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="PeakMessageDeliveryRate", description=TYPE + " Peak Message Delivery Rate") + double getPeakMessageDeliveryRate(); + + /** + * Peak rate of bytes delivered per second for the broker. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="PeakDataDeliveryRate", description=TYPE + " Peak Data Delivery Rate") + double getPeakDataDeliveryRate(); + + /** + * Rate of messages delivered per second for the broker. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="MessageDeliveryRate", description=TYPE + " Message Delivery Rate") + double getMessageDeliveryRate(); + + /** + * Rate of bytes delivered per second for the broker. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="DataDeliveryRate", description=TYPE + " Data Delivery Rate") + double getDataDeliveryRate(); + + /** + * Total count of messages delivered for the broker. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="TotalMessagesDelivered", description=TYPE + " Total Messages Delivered") + long getTotalMessagesDelivered(); + + /** + * Total count of bytes for the broker. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="TotalDataDelivered", description=TYPE + " Total Data Delivered") + long getTotalDataDelivered(); + + /** + * Peak rate of messages received per second for the broker. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="PeakMessageReceiptRate", description=TYPE + " Peak Message Receipt Rate") + double getPeakMessageReceiptRate(); + + /** + * Peak rate of bytes received per second for the broker. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="PeakDataReceiptRate", description=TYPE + " Peak Data Receipt Rate") + double getPeakDataReceiptRate(); + + /** + * Rate of messages received per second for the broker. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="MessageReceiptRate", description=TYPE + " Message Receipt Rate") + double getMessageReceiptRate(); + + /** + * Rate of bytes received per second for the broker. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="DataReceiptRate", description=TYPE + " Data Receipt Rate") + double getDataReceiptRate(); + + /** + * Total count of messages received for the broker. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="TotalMessagesReceived", description=TYPE + " Total Messages Received") + long getTotalMessagesReceived(); + + /** + * Total count of bytes received for the broker. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="TotalDataReceived", description=TYPE + " Total Data Received") + long getTotalDataReceived(); + + /** + * Is statistics collection enabled for this connection. + * + * @since Qpid JMX API 2.1 + */ + @MBeanAttribute(name="StatisticsEnabled", description=TYPE + " Statistics Enabled") + boolean isStatisticsEnabled(); } diff --git a/qpid/java/management/eclipse-plugin/META-INF/MANIFEST.MF b/qpid/java/management/eclipse-plugin/META-INF/MANIFEST.MF index 124fe1e767..2164c5d326 100644 --- a/qpid/java/management/eclipse-plugin/META-INF/MANIFEST.MF +++ b/qpid/java/management/eclipse-plugin/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt Bundle-ManifestVersion: 2 Bundle-Name: Qpid JMX Management Console Plug-in Bundle-SymbolicName: org.apache.qpid.management.ui; singleton:=true -Bundle-Version: 0.9.0 +Bundle-Version: 0.11.0 Bundle-Activator: org.apache.qpid.management.ui.Activator Bundle-Vendor: Apache Software Foundation Bundle-Localization: plugin 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 20cfec3758..8a855a6b3c 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 @@ -47,7 +47,7 @@ public abstract class ApplicationRegistry //max supported broker management interface supported by this release of the management console public static final int SUPPORTED_QPID_JMX_API_MAJOR_VERSION = 2; - public static final int SUPPORTED_QPID_JMX_API_MINOR_VERSION = 0; + public static final int SUPPORTED_QPID_JMX_API_MINOR_VERSION = 1; public static final String DATA_DIR = System.getProperty("user.home") + File.separator + ".qpidmc"; diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXManagedObject.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXManagedObject.java index 3561e16098..a8fb864cf6 100644 --- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXManagedObject.java +++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/jmx/JMXManagedObject.java @@ -21,6 +21,7 @@ package org.apache.qpid.management.ui.jmx; import java.util.HashMap; +import java.util.Map; import javax.management.ObjectName; @@ -31,14 +32,36 @@ public class JMXManagedObject extends ManagedBean { private ObjectName _objName; - @SuppressWarnings("unchecked") public JMXManagedObject(ObjectName objName) { super(); this._objName = objName; setUniqueName(_objName.toString()); - setDomain(_objName.getDomain()); - super.setProperties(new HashMap(_objName.getKeyPropertyList())); + setDomain(_objName.getDomain()); + + HashMap<String,String> props = new HashMap<String,String>(_objName.getKeyPropertyList()); + + for(Map.Entry<String,String> entry : props.entrySet()) + { + String value = entry.getValue(); + + if(value != null) + { + try + { + //if the name is quoted in the ObjectName, unquote it + value = ObjectName.unquote(value); + entry.setValue(value); + } + catch(IllegalArgumentException e) + { + //ignore, this just means the name is not quoted + //and can be left unchanged + } + } + } + + super.setProperties(props); } public ObjectName getObjectName() diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/NotificationObject.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/NotificationObject.java index e42b3c53b6..35cc9f6e27 100644 --- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/NotificationObject.java +++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/model/NotificationObject.java @@ -61,7 +61,7 @@ public class NotificationObject { if (_source instanceof ObjectName) { - return ((ObjectName)_source).getKeyProperty("name"); + return unquote(((ObjectName)_source).getKeyProperty("name")); } return null; @@ -71,12 +71,31 @@ public class NotificationObject { if (_source instanceof ObjectName) { - return ((ObjectName)_source).getKeyProperty(VIRTUAL_HOST); + return unquote(((ObjectName)_source).getKeyProperty(VIRTUAL_HOST)); } return null; } - + + private String unquote(String value) + { + if(value != null) + { + try + { + //if the value is quoted in the ObjectName, unquote it + value = ObjectName.unquote(value); + } + catch(IllegalArgumentException e) + { + //ignore, this just means the value is not quoted + //and can be left unchanged + } + } + + return value; + } + public String getMessage() { return _message; diff --git a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/MBeanTabFolderFactory.java b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/MBeanTabFolderFactory.java index 1fef89d6b5..527fc67be3 100644 --- a/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/MBeanTabFolderFactory.java +++ b/qpid/java/management/eclipse-plugin/src/main/java/org/apache/qpid/management/ui/views/MBeanTabFolderFactory.java @@ -131,6 +131,8 @@ public class MBeanTabFolderFactory } break; case VHOST_MANAGER: + createAttributesTab(tabFolder, mbean); + tab = new TabItem(tabFolder, SWT.NONE); tab.setText("Operations"); controller = new VHostTabControl(tabFolder, mbean, mbsc); diff --git a/qpid/java/module.xml b/qpid/java/module.xml index d3954a1544..80f577b018 100644 --- a/qpid/java/module.xml +++ b/qpid/java/module.xml @@ -401,6 +401,15 @@ </target> + <target name="report-module" description="generate junit reports for each module"> + <junitreport todir="${module.results}"> + <fileset dir="${module.results}"> + <include name="TEST-*.xml"/> + </fileset> + <report format="frames" todir="${module.results}/report/html"/> + </junitreport> + </target> + <target name="touch-failed" if="test.failures"> <touch file="${module.failed}"/> <touch file="${build.failed}"/> diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/client/message/AMQPEncodedMapMessageTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/client/message/AMQPEncodedMapMessageTest.java index 004ce5ea8f..bf96dae02e 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/client/message/AMQPEncodedMapMessageTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/client/message/AMQPEncodedMapMessageTest.java @@ -27,6 +27,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; import javax.jms.Connection; import javax.jms.JMSException; @@ -51,6 +52,7 @@ public class AMQPEncodedMapMessageTest extends QpidBrokerTestCase private Session _session; MessageConsumer _consumer; MessageProducer _producer; + UUID myUUID = UUID.randomUUID(); public void setUp() throws Exception { @@ -119,7 +121,8 @@ public class AMQPEncodedMapMessageTest extends QpidBrokerTestCase m.setFloat("Float", Integer.MAX_VALUE + 5000); m.setInt("Int", Integer.MAX_VALUE - 5000); m.setShort("Short", (short)58); - m.setString("String", "Hello"); + m.setString("String", "Hello"); + m.setObject("uuid", myUUID); _producer.send(m); AMQPEncodedMapMessage msg = (AMQPEncodedMapMessage)_consumer.receive(RECEIVE_TIMEOUT); @@ -140,6 +143,7 @@ public class AMQPEncodedMapMessageTest extends QpidBrokerTestCase assertEquals(Integer.MAX_VALUE - 5000,m.getInt("Int")); assertEquals((short)58,m.getShort("Short")); assertEquals("Hello",m.getString("String")); + assertEquals(myUUID,(UUID)m.getObject("uuid")); } @@ -149,7 +153,11 @@ public class AMQPEncodedMapMessageTest extends QpidBrokerTestCase List<Integer> myList = getList(); - m.setObject("List", myList); + m.setObject("List", myList); + + List<UUID> uuidList = new ArrayList<UUID>(); + uuidList.add(myUUID); + m.setObject("uuid-list", uuidList); _producer.send(m); AMQPEncodedMapMessage msg = (AMQPEncodedMapMessage)_consumer.receive(RECEIVE_TIMEOUT); @@ -167,6 +175,10 @@ public class AMQPEncodedMapMessageTest extends QpidBrokerTestCase assertEquals(i,j.intValue()); i++; } + + List<UUID> list2 = (List<UUID>)msg.getObject("uuid-list"); + assertNotNull("UUID List not received",list2); + assertEquals(myUUID,list2.get(0)); } public void testMessageWithMapEntries() throws JMSException @@ -174,8 +186,12 @@ public class AMQPEncodedMapMessageTest extends QpidBrokerTestCase MapMessage m = _session.createMapMessage(); Map<String,String> myMap = getMap(); + m.setObject("Map", myMap); + + Map<String,UUID> uuidMap = new HashMap<String,UUID>(); + uuidMap.put("uuid", myUUID); + m.setObject("uuid-map", uuidMap); - m.setObject("Map", myMap); _producer.send(m); AMQPEncodedMapMessage msg = (AMQPEncodedMapMessage)_consumer.receive(RECEIVE_TIMEOUT); @@ -191,6 +207,10 @@ public class AMQPEncodedMapMessageTest extends QpidBrokerTestCase assertEquals("String" + i,map.get("Key" + i)); i++; } + + Map<String,UUID> map2 = (Map<String,UUID>)msg.getObject("uuid-map"); + assertNotNull("Map not received",map2); + assertEquals(myUUID,map2.get("uuid")); } public void testMessageWithNestedListsAndMaps() throws JMSException diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageConnectionStatisticsTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageConnectionStatisticsTest.java new file mode 100644 index 0000000000..9839c6e475 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageConnectionStatisticsTest.java @@ -0,0 +1,102 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.management.jmx; + +import java.util.ArrayList; +import java.util.List; + +import javax.jms.Connection; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.management.common.mbeans.ManagedBroker; +import org.apache.qpid.management.common.mbeans.ManagedConnection; + +/** + * Test enabling generation of message statistics on a per-connection basis. + */ +public class MessageConnectionStatisticsTest extends MessageStatisticsTestCase +{ + public void configureStatistics() throws Exception + { + // no statistics generation configured + } + + /** + * Test statistics on a single connection + */ + public void testEnablingStatisticsPerConnection() throws Exception + { + ManagedBroker vhost = _jmxUtils.getManagedBroker("test"); + + sendUsing(_test, 5, 200); + Thread.sleep(1000); + + List<String> addresses = new ArrayList<String>(); + for (ManagedConnection mc : _jmxUtils.getManagedConnections("test")) + { + assertEquals("Incorrect connection total", 0, mc.getTotalMessagesReceived()); + assertEquals("Incorrect connection data", 0, mc.getTotalDataReceived()); + assertFalse("Connection statistics should not be enabled", mc.isStatisticsEnabled()); + + addresses.add(mc.getRemoteAddress()); + } + assertEquals("Incorrect vhost total", 0, vhost.getTotalMessagesReceived()); + assertEquals("Incorrect vhost data", 0, vhost.getTotalDataReceived()); + + Connection test = new AMQConnection(_brokerUrl, USER, USER, "clientid", "test"); + test.start(); + for (ManagedConnection mc : _jmxUtils.getManagedConnections("test")) + { + if (addresses.contains(mc.getRemoteAddress())) + { + continue; + } + mc.setStatisticsEnabled(true); + assertEquals("Incorrect connection total", 0, mc.getTotalMessagesReceived()); + assertEquals("Incorrect connection data", 0, mc.getTotalDataReceived()); + } + + sendUsing(test, 5, 200); + sendUsing(_test, 5, 200); + Thread.sleep(1000); + + for (ManagedConnection mc : _jmxUtils.getManagedConnections("test")) + { + if (addresses.contains(mc.getRemoteAddress())) + { + assertEquals("Incorrect connection total", 0, mc.getTotalMessagesReceived()); + assertEquals("Incorrect connection data", 0, mc.getTotalDataReceived()); + assertFalse("Connection statistics should not be enabled", mc.isStatisticsEnabled()); + } + else + { + assertEquals("Incorrect connection total", 5, mc.getTotalMessagesReceived()); + assertEquals("Incorrect connection data", 1000, mc.getTotalDataReceived()); + assertTrue("Connection statistics should be enabled", mc.isStatisticsEnabled()); + } + } + assertEquals("Incorrect vhost total", 0, vhost.getTotalMessagesReceived()); + assertEquals("Incorrect vhost data", 0, vhost.getTotalDataReceived()); + assertFalse("Vhost statistics should not be enabled", vhost.isStatisticsEnabled()); + + test.close(); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsConfigurationTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsConfigurationTest.java new file mode 100644 index 0000000000..df8c6e74cd --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsConfigurationTest.java @@ -0,0 +1,177 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.management.common.mbeans.ManagedBroker; +import org.apache.qpid.management.common.mbeans.ManagedConnection; + +/** + * Test enabling generation of message statistics on a per-connection basis. + */ +public class MessageStatisticsConfigurationTest extends MessageStatisticsTestCase +{ + public void configureStatistics() throws Exception + { + setConfigurationProperty("statistics.generation.broker", Boolean.toString(getName().contains("Broker"))); + setConfigurationProperty("statistics.generation.virtualhosts", Boolean.toString(getName().contains("Virtualhost"))); + setConfigurationProperty("statistics.generation.connections", Boolean.toString(getName().contains("Connection"))); + } + + /** + * Just broker statistics. + */ + public void testGenerateBrokerStatistics() throws Exception + { + sendUsing(_test, 5, 200); + Thread.sleep(1000); + + for (ManagedConnection mc : _jmxUtils.getManagedConnections("test")) + { + assertEquals("Incorrect connection total", 0, mc.getTotalMessagesReceived()); + assertEquals("Incorrect connection data", 0, mc.getTotalDataReceived()); + assertFalse("Connection statistics should not be enabled", mc.isStatisticsEnabled()); + } + + ManagedBroker vhost = _jmxUtils.getManagedBroker("test"); + assertEquals("Incorrect vhost data", 0, vhost.getTotalMessagesReceived()); + assertEquals("Incorrect vhost data", 0, vhost.getTotalDataReceived()); + assertFalse("Vhost statistics should not be enabled", vhost.isStatisticsEnabled()); + + if (!_broker.equals(VM)) + { + assertEquals("Incorrect server total messages", 5, _jmxUtils.getServerInformation().getTotalMessagesReceived()); + assertEquals("Incorrect server total data", 1000, _jmxUtils.getServerInformation().getTotalDataReceived()); + assertTrue("Server statistics should be enabled", _jmxUtils.getServerInformation().isStatisticsEnabled()); + } + } + + /** + * Just virtualhost statistics. + */ + public void testGenerateVirtualhostStatistics() throws Exception + { + sendUsing(_test, 5, 200); + Thread.sleep(1000); + + for (ManagedConnection mc : _jmxUtils.getManagedConnections("test")) + { + assertEquals("Incorrect connection total", 0, mc.getTotalMessagesReceived()); + assertEquals("Incorrect connection data", 0, mc.getTotalDataReceived()); + assertFalse("Connection statistics should not be enabled", mc.isStatisticsEnabled()); + } + + ManagedBroker vhost = _jmxUtils.getManagedBroker("test"); + assertEquals("Incorrect vhost data", 5, vhost.getTotalMessagesReceived()); + assertEquals("Incorrect vhost data", 1000, vhost.getTotalDataReceived()); + assertTrue("Vhost statistics should be enabled", vhost.isStatisticsEnabled()); + + if (!_broker.equals(VM)) + { + assertEquals("Incorrect server total messages", 0, _jmxUtils.getServerInformation().getTotalMessagesReceived()); + assertEquals("Incorrect server total data", 0, _jmxUtils.getServerInformation().getTotalDataReceived()); + assertFalse("Server statistics should not be enabled", _jmxUtils.getServerInformation().isStatisticsEnabled()); + } + } + + /** + * Just connection statistics. + */ + public void testGenerateConnectionStatistics() throws Exception + { + sendUsing(_test, 5, 200); + Thread.sleep(1000); + + for (ManagedConnection mc : _jmxUtils.getManagedConnections("test")) + { + assertEquals("Incorrect connection total", 5, mc.getTotalMessagesReceived()); + assertEquals("Incorrect connection data", 1000, mc.getTotalDataReceived()); + assertTrue("Connection statistics should be enabled", mc.isStatisticsEnabled()); + } + + ManagedBroker vhost = _jmxUtils.getManagedBroker("test"); + assertEquals("Incorrect vhost data", 0, vhost.getTotalMessagesReceived()); + assertEquals("Incorrect vhost data", 0, vhost.getTotalDataReceived()); + assertFalse("Vhost statistics should not be enabled", vhost.isStatisticsEnabled()); + + if (!_broker.equals(VM)) + { + assertEquals("Incorrect server total messages", 0, _jmxUtils.getServerInformation().getTotalMessagesReceived()); + assertEquals("Incorrect server total data", 0, _jmxUtils.getServerInformation().getTotalDataReceived()); + assertFalse("Server statistics should not be enabled", _jmxUtils.getServerInformation().isStatisticsEnabled()); + } + } + + /** + * Both broker and virtualhost statistics. + */ + public void testGenerateBrokerAndVirtualhostStatistics() throws Exception + { + sendUsing(_test, 5, 200); + Thread.sleep(1000); + + for (ManagedConnection mc : _jmxUtils.getManagedConnections("test")) + { + assertEquals("Incorrect connection total", 0, mc.getTotalMessagesReceived()); + assertEquals("Incorrect connection data", 0, mc.getTotalDataReceived()); + assertFalse("Connection statistics should not be enabled", mc.isStatisticsEnabled()); + } + + ManagedBroker vhost = _jmxUtils.getManagedBroker("test"); + assertEquals("Incorrect vhost data", 5, vhost.getTotalMessagesReceived()); + assertEquals("Incorrect vhost data", 1000, vhost.getTotalDataReceived()); + assertTrue("Vhost statistics should be enabled", vhost.isStatisticsEnabled()); + + if (!_broker.equals(VM)) + { + assertEquals("Incorrect server total messages", 5, _jmxUtils.getServerInformation().getTotalMessagesReceived()); + assertEquals("Incorrect server total data", 1000, _jmxUtils.getServerInformation().getTotalDataReceived()); + assertTrue("Server statistics should be enabled", _jmxUtils.getServerInformation().isStatisticsEnabled()); + } + } + + /** + * Broker, virtualhost and connection statistics. + */ + public void testGenerateBrokerVirtualhostAndConnectionStatistics() throws Exception + { + sendUsing(_test, 5, 200); + Thread.sleep(1000); + + for (ManagedConnection mc : _jmxUtils.getManagedConnections("test")) + { + assertEquals("Incorrect connection total", 5, mc.getTotalMessagesReceived()); + assertEquals("Incorrect connection data", 1000, mc.getTotalDataReceived()); + assertTrue("Connection statistics should be enabled", mc.isStatisticsEnabled()); + } + + ManagedBroker vhost = _jmxUtils.getManagedBroker("test"); + assertEquals("Incorrect vhost data", 5, vhost.getTotalMessagesReceived()); + assertEquals("Incorrect vhost data", 1000, vhost.getTotalDataReceived()); + assertTrue("Vhost statistics should be enabled", vhost.isStatisticsEnabled()); + + if (!_broker.equals(VM)) + { + assertEquals("Incorrect server total messages", 5, _jmxUtils.getServerInformation().getTotalMessagesReceived()); + assertEquals("Incorrect server total data", 1000, _jmxUtils.getServerInformation().getTotalDataReceived()); + assertTrue("Server statistics should be enabled", _jmxUtils.getServerInformation().isStatisticsEnabled()); + } + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsDeliveryTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsDeliveryTest.java new file mode 100644 index 0000000000..e657856d0e --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsDeliveryTest.java @@ -0,0 +1,110 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 java.util.ArrayList; +import java.util.List; + +import javax.jms.Connection; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.Session; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.management.common.mbeans.ManagedBroker; +import org.apache.qpid.management.common.mbeans.ManagedConnection; + +/** + * Test statistics for delivery and receipt. + */ +public class MessageStatisticsDeliveryTest extends MessageStatisticsTestCase +{ + public void configureStatistics() throws Exception + { + setConfigurationProperty("statistics.generation.broker", "true"); + setConfigurationProperty("statistics.generation.virtualhosts", "true"); + setConfigurationProperty("statistics.generation.connections", "true"); + } + + public void testDeliveryAndReceiptStatistics() throws Exception + { + ManagedBroker vhost = _jmxUtils.getManagedBroker("test"); + + sendUsing(_test, 5, 200); + Thread.sleep(1000); + + List<String> addresses = new ArrayList<String>(); + for (ManagedConnection mc : _jmxUtils.getManagedConnections("test")) + { + assertEquals("Incorrect connection delivery total", 0, mc.getTotalMessagesDelivered()); + assertEquals("Incorrect connection delivery data", 0, mc.getTotalDataDelivered()); + assertEquals("Incorrect connection receipt total", 5, mc.getTotalMessagesReceived()); + assertEquals("Incorrect connection receipt data", 1000, mc.getTotalDataReceived()); + + addresses.add(mc.getRemoteAddress()); + } + + assertEquals("Incorrect vhost delivery total", 0, vhost.getTotalMessagesDelivered()); + assertEquals("Incorrect vhost delivery data", 0, vhost.getTotalDataDelivered()); + assertEquals("Incorrect vhost receipt total", 5, vhost.getTotalMessagesReceived()); + assertEquals("Incorrect vhost receipt data", 1000, vhost.getTotalDataReceived()); + + Connection test = new AMQConnection(_brokerUrl, USER, USER, "clientid", "test"); + test.start(); + receiveUsing(test, 5); + + for (ManagedConnection mc : _jmxUtils.getManagedConnections("test")) + { + if (addresses.contains(mc.getRemoteAddress())) + { + assertEquals("Incorrect connection delivery total", 0, mc.getTotalMessagesDelivered()); + assertEquals("Incorrect connection delivery data", 0, mc.getTotalDataDelivered()); + assertEquals("Incorrect connection receipt total", 5, mc.getTotalMessagesReceived()); + assertEquals("Incorrect connection receipt data", 1000, mc.getTotalDataReceived()); + } + else + { + assertEquals("Incorrect connection delivery total", 5, mc.getTotalMessagesDelivered()); + assertEquals("Incorrect connection delivery data", 1000, mc.getTotalDataDelivered()); + assertEquals("Incorrect connection receipt total", 0, mc.getTotalMessagesReceived()); + assertEquals("Incorrect connection receipt data", 0, mc.getTotalDataReceived()); + } + } + assertEquals("Incorrect vhost delivery total", 5, vhost.getTotalMessagesDelivered()); + assertEquals("Incorrect vhost delivery data", 1000, vhost.getTotalDataDelivered()); + assertEquals("Incorrect vhost receipt total", 5, vhost.getTotalMessagesReceived()); + assertEquals("Incorrect vhost receipt data", 1000, vhost.getTotalDataReceived()); + + test.close(); + } + + protected void receiveUsing(Connection con, int number) throws Exception + { + Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE); + createQueue(session); + MessageConsumer consumer = session.createConsumer(_queue); + for (int i = 0; i < number; i++) + { + Message msg = consumer.receive(100); + assertNotNull("Message " + i + " was not received", msg); + } + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsReportingTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsReportingTest.java new file mode 100644 index 0000000000..180440c0d6 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsReportingTest.java @@ -0,0 +1,90 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 java.util.List; + +import org.apache.qpid.util.LogMonitor; + +/** + * Test generation of message statistics reporting. + */ +public class MessageStatisticsReportingTest extends MessageStatisticsTestCase +{ + protected LogMonitor _monitor; + + public void configureStatistics() throws Exception + { + setConfigurationProperty("statistics.generation.broker", "true"); + setConfigurationProperty("statistics.generation.virtualhosts", "true"); + + if (getName().equals("testEnabledStatisticsReporting")) + { + setConfigurationProperty("statistics.reporting.period", "10"); + } + + _monitor = new LogMonitor(_outputFile); + } + + /** + * Test enabling reporting. + */ + public void testEnabledStatisticsReporting() throws Exception + { + sendUsing(_test, 10, 100); + sendUsing(_dev, 20, 100); + sendUsing(_local, 15, 100); + + Thread.sleep(10 * 1000); // 15s + + List<String> brokerStatsData = _monitor.findMatches("BRK-1008"); + List<String> brokerStatsMessages = _monitor.findMatches("BRK-1009"); + List<String> vhostStatsData = _monitor.findMatches("VHT-1003"); + List<String> vhostStatsMessages = _monitor.findMatches("VHT-1004"); + + assertEquals("Incorrect number of broker data stats log messages", 2, brokerStatsData.size()); + assertEquals("Incorrect number of broker message stats log messages", 2, brokerStatsMessages.size()); + assertEquals("Incorrect number of virtualhost data stats log messages", 6, vhostStatsData.size()); + assertEquals("Incorrect number of virtualhost message stats log messages", 6, vhostStatsMessages.size()); + } + + /** + * Test not enabling reporting. + */ + public void testNotEnabledStatisticsReporting() throws Exception + { + sendUsing(_test, 10, 100); + sendUsing(_dev, 20, 100); + sendUsing(_local, 15, 100); + + Thread.sleep(10 * 1000); // 15s + + List<String> brokerStatsData = _monitor.findMatches("BRK-1008"); + List<String> brokerStatsMessages = _monitor.findMatches("BRK-1009"); + List<String> vhostStatsData = _monitor.findMatches("VHT-1003"); + List<String> vhostStatsMessages = _monitor.findMatches("VHT-1004"); + + assertEquals("Incorrect number of broker data stats log messages", 0, brokerStatsData.size()); + assertEquals("Incorrect number of broker message stats log messages", 0, brokerStatsMessages.size()); + assertEquals("Incorrect number of virtualhost data stats log messages", 0, vhostStatsData.size()); + assertEquals("Incorrect number of virtualhost message stats log messages", 0, vhostStatsMessages.size()); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsTest.java new file mode 100644 index 0000000000..50ca51b18a --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsTest.java @@ -0,0 +1,233 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 javax.jms.Connection; + +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.management.common.mbeans.ManagedBroker; +import org.apache.qpid.management.common.mbeans.ManagedConnection; + +/** + * Test generation of message statistics. + */ +public class MessageStatisticsTest extends MessageStatisticsTestCase +{ + public void configureStatistics() throws Exception + { + setConfigurationProperty("statistics.generation.broker", "true"); + setConfigurationProperty("statistics.generation.virtualhosts", "true"); + setConfigurationProperty("statistics.generation.connections", "true"); + } + + /** + * Test message totals. + */ + public void testMessageTotals() throws Exception + { + sendUsing(_test, 10, 100); + sendUsing(_dev, 20, 100); + sendUsing(_local, 5, 100); + sendUsing(_local, 5, 100); + sendUsing(_local, 5, 100); + Thread.sleep(2000); + + ManagedBroker test = _jmxUtils.getManagedBroker("test"); + ManagedBroker dev = _jmxUtils.getManagedBroker("development"); + ManagedBroker local = _jmxUtils.getManagedBroker("localhost"); + + if (!isBroker010()) + { + long total = 0; + long data = 0; + for (ManagedConnection mc : _jmxUtils.getAllManagedConnections()) + { + total += mc.getTotalMessagesReceived(); + data += mc.getTotalDataReceived(); + } + assertEquals("Incorrect connection total", 45, total); + assertEquals("Incorrect connection data", 4500, data); + } + if (!_broker.equals(VM)) + { + assertEquals("Incorrect server total", 45, _jmxUtils.getServerInformation().getTotalMessagesReceived()); + assertEquals("Incorrect server data", 4500, _jmxUtils.getServerInformation().getTotalDataReceived()); + } + + if (!isBroker010()) + { + long testTotal = 0; + long testData = 0; + for (ManagedConnection mc : _jmxUtils.getManagedConnections("test")) + { + testTotal += mc.getTotalMessagesReceived(); + testData += mc.getTotalDataReceived(); + } + assertEquals("Incorrect test connection total", 10, testTotal); + assertEquals("Incorrect test connection data", 1000, testData); + } + assertEquals("Incorrect test vhost total", 10, test.getTotalMessagesReceived()); + assertEquals("Incorrect test vhost data", 1000, test.getTotalDataReceived()); + + if (!isBroker010()) + { + long devTotal = 0; + long devData = 0; + for (ManagedConnection mc : _jmxUtils.getManagedConnections("development")) + { + devTotal += mc.getTotalMessagesReceived(); + devData += mc.getTotalDataReceived(); + } + assertEquals("Incorrect test connection total", 20, devTotal); + assertEquals("Incorrect test connection data", 2000, devData); + } + assertEquals("Incorrect development total", 20, dev.getTotalMessagesReceived()); + assertEquals("Incorrect development data", 2000, dev.getTotalDataReceived()); + + if (!isBroker010()) + { + long localTotal = 0; + long localData = 0; + for (ManagedConnection mc : _jmxUtils.getManagedConnections("localhost")) + { + localTotal += mc.getTotalMessagesReceived(); + localData += mc.getTotalDataReceived(); + } + assertEquals("Incorrect test connection total", 15, localTotal); + assertEquals("Incorrect test connection data", 1500, localData); + } + assertEquals("Incorrect localhost total", 15, local.getTotalMessagesReceived()); + assertEquals("Incorrect localhost data", 1500, local.getTotalDataReceived()); + } + + /** + * Test message totals when a connection is closed. + */ + public void testMessageTotalsWithClosedConnections() throws Exception + { + Connection temp = new AMQConnection(_brokerUrl, USER, USER, "clientid", "test"); + temp.start(); + + sendUsing(_test, 10, 100); + sendUsing(temp, 10, 100); + sendUsing(_test, 10, 100); + Thread.sleep(2000); + + temp.close(); + + ManagedBroker test = _jmxUtils.getManagedBroker("test"); + + if (!isBroker010()) + { + long total = 0; + long data = 0; + for (ManagedConnection mc : _jmxUtils.getAllManagedConnections()) + { + total += mc.getTotalMessagesReceived(); + data += mc.getTotalDataReceived(); + } + assertEquals("Incorrect active connection total", 20, total); + assertEquals("Incorrect active connection data", 2000, data); + } + if (!_broker.equals(VM)) + { + assertEquals("Incorrect server total", 30, _jmxUtils.getServerInformation().getTotalMessagesReceived()); + assertEquals("Incorrect server data", 3000, _jmxUtils.getServerInformation().getTotalDataReceived()); + } + + if (!isBroker010()) + { + long testTotal = 0; + long testData = 0; + for (ManagedConnection mc : _jmxUtils.getManagedConnections("test")) + { + testTotal += mc.getTotalMessagesReceived(); + testData += mc.getTotalDataReceived(); + } + assertEquals("Incorrect test active connection total", 20, testTotal); + assertEquals("Incorrect test active connection data", 20 * 100, testData); + } + assertEquals("Incorrect test vhost total", 30, test.getTotalMessagesReceived()); + assertEquals("Incorrect test vhost data", 30 * 100, test.getTotalDataReceived()); + } + + /** + * Test message peak rate generation. + */ + public void testMessagePeakRates() throws Exception + { + sendUsing(_test, 2, 10); + Thread.sleep(10000); + sendUsing(_dev, 4, 10); + Thread.sleep(10000); + + ManagedBroker test = _jmxUtils.getManagedBroker("test"); + ManagedBroker dev = _jmxUtils.getManagedBroker("development"); + + assertApprox("Incorrect test vhost peak messages", 0.2d, 1.0d, test.getPeakMessageReceiptRate()); + assertApprox("Incorrect test vhost peak data", 0.2d, 10.0d, test.getPeakDataReceiptRate()); + assertApprox("Incorrect dev vhost peak messages", 0.2d, 2.0d, dev.getPeakMessageReceiptRate()); + assertApprox("Incorrect dev vhost peak data", 0.2d, 20.0d, dev.getPeakDataReceiptRate()); + + if (!_broker.equals(VM)) + { + assertApprox("Incorrect server peak messages", 0.2d, 2.0d, _jmxUtils.getServerInformation().getPeakMessageReceiptRate()); + assertApprox("Incorrect server peak data", 0.2d, 20.0d, _jmxUtils.getServerInformation().getPeakDataReceiptRate()); + } + } + + /** + * Test message totals when a vhost has its statistics reset + */ + public void testMessageTotalVhostReset() throws Exception + { + sendUsing(_test, 10, 10); + sendUsing(_dev, 10, 10); + Thread.sleep(2000); + + ManagedBroker test = _jmxUtils.getManagedBroker("test"); + ManagedBroker dev = _jmxUtils.getManagedBroker("development"); + + assertEquals("Incorrect test vhost total messages", 10, test.getTotalMessagesReceived()); + assertEquals("Incorrect test vhost total data", 100, test.getTotalDataReceived()); + assertEquals("Incorrect dev vhost total messages", 10, dev.getTotalMessagesReceived()); + assertEquals("Incorrect dev vhost total data", 100, dev.getTotalDataReceived()); + + if (!_broker.equals(VM)) + { + assertEquals("Incorrect server total messages", 20, _jmxUtils.getServerInformation().getTotalMessagesReceived()); + assertEquals("Incorrect server total data", 200, _jmxUtils.getServerInformation().getTotalDataReceived()); + } + + test.resetStatistics(); + + assertEquals("Incorrect test vhost total messages", 0, test.getTotalMessagesReceived()); + assertEquals("Incorrect test vhost total data", 0, test.getTotalDataReceived()); + assertEquals("Incorrect dev vhost total messages", 10, dev.getTotalMessagesReceived()); + assertEquals("Incorrect dev vhost total data", 100, dev.getTotalDataReceived()); + + if (!_broker.equals(VM)) + { + assertEquals("Incorrect server total messages", 20, _jmxUtils.getServerInformation().getTotalMessagesReceived()); + assertEquals("Incorrect server total data", 200, _jmxUtils.getServerInformation().getTotalDataReceived()); + } + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsTestCase.java new file mode 100644 index 0000000000..a5b3aa283c --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/management/jmx/MessageStatisticsTestCase.java @@ -0,0 +1,128 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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 javax.jms.Connection; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MessageProducer; +import javax.jms.Session; +import javax.jms.TextMessage; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.client.AMQSession; +import org.apache.qpid.exchange.ExchangeDefaults; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.test.utils.JMXTestUtils; +import org.apache.qpid.test.utils.QpidBrokerTestCase; + +/** + * Test generation of message statistics. + */ +public abstract class MessageStatisticsTestCase extends QpidBrokerTestCase +{ + protected static final String USER = "admin"; + + protected JMXTestUtils _jmxUtils; + protected Connection _test, _dev, _local; + protected String _queueName = "statistics"; + protected Destination _queue; + protected String _brokerUrl; + + @Override + public void setUp() throws Exception + { + _jmxUtils = new JMXTestUtils(this, USER, USER); + _jmxUtils.setUp(); + + configureStatistics(); + + super.setUp(); + + _brokerUrl = getBroker().toString(); + _test = new AMQConnection(_brokerUrl, USER, USER, "clientid", "test"); + _dev = new AMQConnection(_brokerUrl, USER, USER, "clientid", "development"); + _local = new AMQConnection(_brokerUrl, USER, USER, "clientid", "localhost"); + + _test.start(); + _dev.start(); + _local.start(); + + _jmxUtils.open(); + } + + protected void createQueue(Session session) throws AMQException, JMSException + { + _queue = new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, _queueName); + if (!((AMQSession<?,?>) session).isQueueBound((AMQDestination) _queue)) + { + ((AMQSession<?,?>) session).createQueue(new AMQShortString(_queueName), false, true, false, null); + ((AMQSession<?,?>) session).declareAndBind((AMQDestination) new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, _queueName)); + } + } + + + @Override + public void tearDown() throws Exception + { + _jmxUtils.close(); + + _test.close(); + _dev.close(); + _local.close(); + + super.tearDown(); + } + + /** + * Configure statistics generation properties on the broker. + */ + public abstract void configureStatistics() throws Exception; + + protected void sendUsing(Connection con, int number, int size) throws Exception + { + Session session = con.createSession(false, Session.AUTO_ACKNOWLEDGE); + createQueue(session); + MessageProducer producer = session.createProducer(_queue); + String content = new String(new byte[size]); + TextMessage msg = session.createTextMessage(content); + for (int i = 0; i < number; i++) + { + producer.send(msg); + } + } + + /** + * Asserts that the actual value is within the expected value plus or + * minus the given error. + */ + public void assertApprox(String message, double error, double expected, double actual) + { + double min = expected * (1.0d - error); + double max = expected * (1.0d + error); + String assertion = String.format("%s: expected %f +/- %d%%, actual %f", + message, expected, (int) (error * 100.0d), actual); + assertTrue(assertion, actual > min && actual < max); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/destination/AddressBasedDestinationTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/destination/AddressBasedDestinationTest.java index 51589c705f..0480ea4cab 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/client/destination/AddressBasedDestinationTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/client/destination/AddressBasedDestinationTest.java @@ -31,13 +31,20 @@ import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.QueueReceiver; +import javax.jms.QueueSession; import javax.jms.Session; import javax.jms.TextMessage; import javax.jms.Topic; +import javax.jms.TopicSession; +import javax.jms.TopicSubscriber; import javax.naming.Context; import org.apache.qpid.client.AMQAnyDestination; +import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQDestination; +import org.apache.qpid.client.AMQSession; import org.apache.qpid.client.AMQSession_0_10; import org.apache.qpid.client.messaging.address.Node.ExchangeNode; import org.apache.qpid.client.messaging.address.Node.QueueNode; @@ -796,4 +803,190 @@ public class AddressBasedDestinationTest extends QpidBrokerTestCase { } } + + public void testQueueReceiversAndTopicSubscriber() throws Exception + { + Queue queue = new AMQAnyDestination("ADDR:my-queue; {create: always}"); + Topic topic = new AMQAnyDestination("ADDR:amq.topic/test"); + + QueueSession qSession = ((AMQConnection)_connection).createQueueSession(false, Session.AUTO_ACKNOWLEDGE); + QueueReceiver receiver = qSession.createReceiver(queue); + + TopicSession tSession = ((AMQConnection)_connection).createTopicSession(false, Session.AUTO_ACKNOWLEDGE); + TopicSubscriber sub = tSession.createSubscriber(topic); + + Session ssn = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + MessageProducer prod1 = ssn.createProducer(ssn.createQueue("ADDR:my-queue")); + prod1.send(ssn.createTextMessage("test1")); + + MessageProducer prod2 = ssn.createProducer(ssn.createTopic("ADDR:amq.topic/test")); + prod2.send(ssn.createTextMessage("test2")); + + Message msg1 = receiver.receive(); + assertNotNull(msg1); + assertEquals("test1",((TextMessage)msg1).getText()); + + Message msg2 = sub.receive(); + assertNotNull(msg2); + assertEquals("test2",((TextMessage)msg2).getText()); + } + + public void testDurableSubscriber() throws Exception + { + Session ssn = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE); + Topic topic = ssn.createTopic("news.us"); + + MessageConsumer cons = ssn.createDurableSubscriber(topic, "my-sub"); + MessageProducer prod = ssn.createProducer(topic); + + Message m = ssn.createTextMessage("A"); + prod.send(m); + Message msg = cons.receive(1000); + assertNotNull(msg); + assertEquals("A",((TextMessage)msg).getText()); + } + + public void testDeleteOptions() throws Exception + { + Session jmsSession = _connection.createSession(false,Session.AUTO_ACKNOWLEDGE); + MessageConsumer cons; + + // default (create never, assert never) ------------------- + // create never -------------------------------------------- + String addr1 = "ADDR:testQueue1;{create: always, delete: always}"; + AMQDestination dest = new AMQAnyDestination(addr1); + try + { + cons = jmsSession.createConsumer(dest); + cons.close(); + } + catch(JMSException e) + { + fail("Exception should not be thrown. Exception thrown is : " + e); + } + + assertFalse("Queue not deleted as expected",( + (AMQSession_0_10)jmsSession).isQueueExist(dest,(QueueNode)dest.getSourceNode(), true)); + + + String addr2 = "ADDR:testQueue2;{create: always, delete: receiver}"; + dest = new AMQAnyDestination(addr2); + try + { + cons = jmsSession.createConsumer(dest); + cons.close(); + } + catch(JMSException e) + { + fail("Exception should not be thrown. Exception thrown is : " + e); + } + + assertFalse("Queue not deleted as expected",( + (AMQSession_0_10)jmsSession).isQueueExist(dest,(QueueNode)dest.getSourceNode(), true)); + + + String addr3 = "ADDR:testQueue3;{create: always, delete: sender}"; + dest = new AMQAnyDestination(addr3); + try + { + cons = jmsSession.createConsumer(dest); + MessageProducer prod = jmsSession.createProducer(dest); + prod.close(); + } + catch(JMSException e) + { + fail("Exception should not be thrown. Exception thrown is : " + e); + } + + assertFalse("Queue not deleted as expected",( + (AMQSession_0_10)jmsSession).isQueueExist(dest,(QueueNode)dest.getSourceNode(), true)); + + + } + + /** + * Test Goals : 1. Test if the client sets the correct accept mode for unreliable + * and at-least-once. + * 2. Test default reliability modes for Queues and Topics. + * 3. Test if an exception is thrown if exactly-once is used. + * 4. Test if an exception is thrown if at-least-once is used with topics. + * + * Test Strategy: For goal #1 & #2 + * For unreliable and at-least-once the test tries to receives messages + * in client_ack mode but does not ack the messages. + * It will then close the session, recreate a new session + * and will then try to verify the queue depth. + * For unreliable the messages should have been taken off the queue. + * For at-least-once the messages should be put back onto the queue. + * + */ + + public void testReliabilityOptions() throws Exception + { + String addr1 = "ADDR:testQueue1;{create: always, delete : receiver, link : {reliability : unreliable}}"; + acceptModeTest(addr1,0); + + String addr2 = "ADDR:testQueue2;{create: always, delete : receiver, link : {reliability : at-least-once}}"; + acceptModeTest(addr2,2); + + // Default accept-mode for topics + acceptModeTest("ADDR:amq.topic/test",0); + + // Default accept-mode for queues + acceptModeTest("ADDR:testQueue1;{create: always}",2); + + String addr3 = "ADDR:testQueue2;{create: always, delete : receiver, link : {reliability : exactly-once}}"; + try + { + AMQAnyDestination dest = new AMQAnyDestination(addr3); + fail("An exception should be thrown indicating it's an unsupported type"); + } + catch(Exception e) + { + assertTrue(e.getCause().getMessage().contains("The reliability mode 'exactly-once' is not yet supported")); + } + + String addr4 = "ADDR:amq.topic/test;{link : {reliability : at-least-once}}"; + try + { + AMQAnyDestination dest = new AMQAnyDestination(addr4); + Session ssn = _connection.createSession(false,Session.CLIENT_ACKNOWLEDGE); + MessageConsumer cons = ssn.createConsumer(dest); + fail("An exception should be thrown indicating it's an unsupported combination"); + } + catch(Exception e) + { + assertTrue(e.getCause().getMessage().contains("AT-LEAST-ONCE is not yet supported for Topics")); + } + } + + private void acceptModeTest(String address, int expectedQueueDepth) throws Exception + { + Session ssn = _connection.createSession(false,Session.CLIENT_ACKNOWLEDGE); + MessageConsumer cons; + MessageProducer prod; + + AMQDestination dest = new AMQAnyDestination(address); + cons = ssn.createConsumer(dest); + prod = ssn.createProducer(dest); + + for (int i=0; i < expectedQueueDepth; i++) + { + prod.send(ssn.createTextMessage("Msg" + i)); + } + + for (int i=0; i < expectedQueueDepth; i++) + { + Message msg = cons.receive(1000); + assertNotNull(msg); + assertEquals("Msg" + i,((TextMessage)msg).getText()); + } + + ssn.close(); + ssn = _connection.createSession(false,Session.CLIENT_ACKNOWLEDGE); + long queueDepth = ((AMQSession) ssn).getQueueDepth(dest); + assertEquals(expectedQueueDepth,queueDepth); + cons.close(); + prod.close(); + } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/DynamicQueueExchangeCreateTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/DynamicQueueExchangeCreateTest.java index 33575b58aa..8577fb5b6a 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/DynamicQueueExchangeCreateTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/DynamicQueueExchangeCreateTest.java @@ -20,6 +20,8 @@ */ package org.apache.qpid.test.unit.client; +import org.apache.qpid.AMQException; +import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.test.utils.QpidBrokerTestCase; import javax.jms.Connection; @@ -32,11 +34,9 @@ import javax.jms.Session; * * Test to validate that setting the respective qpid.declare_queues, * qpid.declare_exchanges system properties functions as expected. - * */ public class DynamicQueueExchangeCreateTest extends QpidBrokerTestCase { - public void testQueueDeclare() throws Exception { setSystemProperty("qpid.declare_queues", "false"); @@ -53,11 +53,8 @@ public class DynamicQueueExchangeCreateTest extends QpidBrokerTestCase fail("JMSException should be thrown as the queue does not exist"); } catch (JMSException e) - { - assertTrue("Exception should be that the queue does not exist :" + - e.getMessage(), - e.getMessage().contains("does not exist")); - + { + checkExceptionErrorCode(e, AMQConstant.NOT_FOUND); } } @@ -79,10 +76,15 @@ public class DynamicQueueExchangeCreateTest extends QpidBrokerTestCase } catch (JMSException e) { - assertTrue("Exception should be that the exchange does not exist :" + - e.getMessage(), - e.getMessage().contains("Exchange " + EXCHANGE_TYPE + " does not exist")); + checkExceptionErrorCode(e, AMQConstant.NOT_FOUND); } } + private void checkExceptionErrorCode(JMSException original, AMQConstant code) + { + Exception linked = original.getLinkedException(); + assertNotNull("Linked exception should have been set", linked); + assertTrue("Linked exception should be an AMQException", linked instanceof AMQException); + assertEquals("Error code should be " + code.getCode(), code, ((AMQException) linked).getErrorCode()); + } } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java index f0794c9dab..b6232b1734 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseTest.java @@ -29,7 +29,6 @@ import org.apache.qpid.client.transport.TransportConnection; import org.apache.qpid.framing.*; import org.apache.qpid.jms.ConnectionListener; import org.apache.qpid.protocol.AMQConstant; -import org.apache.qpid.url.URLSyntaxException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,70 +48,67 @@ public class ChannelCloseTest extends QpidBrokerTestCase implements ExceptionLis private static final Logger _logger = LoggerFactory.getLogger(ChannelCloseTest.class); Connection _connection; - private String _brokerlist = "vm://:1"; private Session _session; private static final long SYNC_TIMEOUT = 500; private int TEST = 0; - /* - close channel, use chanel with same id ensure error. + /** + * Close channel, use chanel with same id ensure error. + * + * This test is only valid for non 0-10 connection . */ public void testReusingChannelAfterFullClosure() throws Exception { - // this is testing an inVM Connetion conneciton - if (isJavaBroker() && !isExternalBroker()) + _connection=newConnection(); + + // Create Producer + try { - _connection=newConnection(); + _connection.start(); + + createChannelAndTest(1); - // Create Producer + // Cause it to close try { - _connection.start(); - - createChannelAndTest(1); - - // Cause it to close - try - { - _logger.info("Testing invalid exchange"); - declareExchange(1, "", "name_that_will_lookup_to_null", false); - fail("Exchange name is empty so this should fail "); - } - catch (AMQException e) - { - assertEquals("Exchange should not be found", AMQConstant.NOT_FOUND, e.getErrorCode()); - } + _logger.info("Testing invalid exchange"); + declareExchange(1, "", "name_that_will_lookup_to_null", false); + fail("Exchange name is empty so this should fail "); + } + catch (AMQException e) + { + assertEquals("Exchange should not be found", AMQConstant.NOT_FOUND, e.getErrorCode()); + } - // Check that - try + // Check that + try + { + _logger.info("Testing valid exchange should fail"); + declareExchange(1, "topic", "amq.topic", false); + fail("This should not succeed as the channel should be closed "); + } + catch (AMQException e) + { + if (_logger.isInfoEnabled()) { - _logger.info("Testing valid exchange should fail"); - declareExchange(1, "topic", "amq.topic", false); - fail("This should not succeed as the channel should be closed "); + _logger.info("Exception occured was:" + e.getErrorCode()); } - catch (AMQException e) - { - if (_logger.isInfoEnabled()) - { - _logger.info("Exception occured was:" + e.getErrorCode()); - } - assertEquals("Connection should be closed", AMQConstant.CHANNEL_ERROR, e.getErrorCode()); + assertEquals("Connection should be closed", AMQConstant.CHANNEL_ERROR, e.getErrorCode()); - _connection=newConnection(); - } + _connection=newConnection(); + } - checkSendingMessage(); + checkSendingMessage(); - _session.close(); - _connection.close(); + _session.close(); + _connection.close(); - } - catch (JMSException e) - { - e.printStackTrace(); - fail(e.getMessage()); - } + } + catch (JMSException e) + { + e.printStackTrace(); + fail(e.getMessage()); } } @@ -306,27 +302,19 @@ public class ChannelCloseTest extends QpidBrokerTestCase implements ExceptionLis private Connection newConnection() { - AMQConnection connection = null; + Connection connection = null; try { - connection = new AMQConnection("amqp://guest:guest@CCTTest/test?brokerlist='" + _brokerlist + "'"); + connection = getConnection(); - connection.setConnectionListener(this); + ((AMQConnection) connection).setConnectionListener(this); _session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE); connection.start(); } - catch (JMSException e) - { - fail("Creating new connection when:" + e.getMessage()); - } - catch (AMQException e) - { - fail("Creating new connection when:" + e.getMessage()); - } - catch (URLSyntaxException e) + catch (Exception e) { fail("Creating new connection when:" + e.getMessage()); } diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutConfigurationTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutConfigurationTest.java new file mode 100644 index 0000000000..36bac3b715 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutConfigurationTest.java @@ -0,0 +1,82 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.transacted; + +/** + * This verifies that changing the {@code transactionTimeout} configuration will alter + * the behaviour of the transaction open and idle logging, and that when the connection + * will be closed. + */ +public class TransactionTimeoutConfigurationTest extends TransactionTimeoutTestCase +{ + @Override + protected void configure() throws Exception + { + // Setup housekeeping every second + setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".housekeeping.expiredMessageCheckPeriod", "100"); + + // Set transaction timout properties. + setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.openWarn", "200"); + setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.openClose", "1000"); + setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.idleWarn", "100"); + setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.idleClose", "500"); + } + + public void testProducerIdleCommit() throws Exception + { + try + { + send(5, 0); + + sleep(2.0f); + + _psession.commit(); + fail("should fail"); + } + catch (Exception e) + { + _exception = e; + } + + monitor(5, 0); + + check(IDLE); + } + + public void testProducerOpenCommit() throws Exception + { + try + { + send(5, 0.3f); + + _psession.commit(); + fail("should fail"); + } + catch (Exception e) + { + _exception = e; + } + + monitor(6, 3); + + check(OPEN); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutDisabledTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutDisabledTest.java new file mode 100644 index 0000000000..71b89bf911 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutDisabledTest.java @@ -0,0 +1,72 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.test.unit.transacted; + +/** + * This verifies that the default behaviour is not to time out transactions. + */ +public class TransactionTimeoutDisabledTest extends TransactionTimeoutTestCase +{ + @Override + protected void configure() throws Exception + { + // Setup housekeeping every second + setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".housekeeping.expiredMessageCheckPeriod", "100"); + } + + public void testProducerIdleCommit() throws Exception + { + try + { + send(5, 0); + + sleep(2.0f); + + _psession.commit(); + } + catch (Exception e) + { + fail("Should have succeeded"); + } + + assertTrue("Listener should not have received exception", _caught.getCount() == 1); + + monitor(0, 0); + } + + public void testProducerOpenCommit() throws Exception + { + try + { + send(5, 0.3f); + + _psession.commit(); + } + catch (Exception e) + { + fail("Should have succeeded"); + } + + assertTrue("Listener should not have received exception", _caught.getCount() == 1); + + monitor(0, 0); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTest.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTest.java new file mode 100644 index 0000000000..c912d6a323 --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTest.java @@ -0,0 +1,335 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.test.unit.transacted; + +/** + * This tests the behaviour of transactional sessions when the {@code transactionTimeout} configuration + * is set for a virtual host. + * + * A producer that is idle for too long or open for too long will have its connection closed and + * any further operations will fail with a 408 resource timeout exception. Consumers will not + * be affected by the transaction timeout configuration. + */ +public class TransactionTimeoutTest extends TransactionTimeoutTestCase +{ + public void testProducerIdle() throws Exception + { + try + { + sleep(2.0f); + + _psession.commit(); + } + catch (Exception e) + { + fail("Should have succeeded"); + } + + assertTrue("Listener should not have received exception", _caught.getCount() == 1); + + monitor(0, 0); + } + + public void testProducerIdleCommit() throws Exception + { + try + { + send(5, 0); + + sleep(2.0f); + + _psession.commit(); + fail("should fail"); + } + catch (Exception e) + { + _exception = e; + } + + monitor(5, 0); + + check(IDLE); + } + + public void testProducerOpenCommit() throws Exception + { + try + { + send(6, 0.5f); + + _psession.commit(); + fail("should fail"); + } + catch (Exception e) + { + _exception = e; + } + + monitor(0, 10); + + check(OPEN); + } + + public void testProducerIdleCommitTwice() throws Exception + { + try + { + send(5, 0); + + sleep(1.0f); + + _psession.commit(); + + send(5, 0); + + sleep(2.0f); + + _psession.commit(); + fail("should fail"); + } + catch (Exception e) + { + _exception = e; + } + + monitor(10, 0); + + check(IDLE); + } + + public void testProducerOpenCommitTwice() throws Exception + { + try + { + send(5, 0); + + sleep(1.0f); + + _psession.commit(); + + send(6, 0.5f); + + _psession.commit(); + fail("should fail"); + } + catch (Exception e) + { + _exception = e; + } + + // the presistent store generates more idle messages? + monitor(isBrokerStorePersistent() ? 10 : 5, 10); + + check(OPEN); + } + + public void testProducerIdleRollback() throws Exception + { + try + { + send(5, 0); + + sleep(2.0f); + + _psession.rollback(); + fail("should fail"); + } + catch (Exception e) + { + _exception = e; + } + + monitor(5, 0); + + check(IDLE); + } + + public void testProducerIdleRollbackTwice() throws Exception + { + try + { + send(5, 0); + + sleep(1.0f); + + _psession.rollback(); + + send(5, 0); + + sleep(2.0f); + + _psession.rollback(); + fail("should fail"); + } + catch (Exception e) + { + _exception = e; + } + + monitor(10, 0); + + check(IDLE); + } + + public void testConsumerCommitClose() throws Exception + { + try + { + send(1, 0); + + _psession.commit(); + + expect(1, 0); + + _csession.commit(); + + sleep(3.0f); + + _csession.close(); + } + catch (Exception e) + { + fail("should have succeeded: " + e.getMessage()); + } + + assertTrue("Listener should not have received exception", _caught.getCount() == 1); + + monitor(0, 0); + } + + public void testConsumerIdleReceiveCommit() throws Exception + { + try + { + send(1, 0); + + _psession.commit(); + + sleep(2.0f); + + expect(1, 0); + + sleep(2.0f); + + _csession.commit(); + } + catch (Exception e) + { + fail("Should have succeeded"); + } + + assertTrue("Listener should not have received exception", _caught.getCount() == 1); + + monitor(0, 0); + } + + public void testConsumerIdleCommit() throws Exception + { + try + { + send(1, 0); + + _psession.commit(); + + expect(1, 0); + + sleep(2.0f); + + _csession.commit(); + } + catch (Exception e) + { + fail("Should have succeeded"); + } + + assertTrue("Listener should not have received exception", _caught.getCount() == 1); + + monitor(0, 0); + } + + public void testConsumerIdleRollback() throws Exception + { + try + { + send(1, 0); + + _psession.commit(); + + expect(1, 0); + + sleep(2.0f); + + _csession.rollback(); + } + catch (Exception e) + { + fail("Should have succeeded"); + } + + assertTrue("Listener should not have received exception", _caught.getCount() == 1); + + monitor(0, 0); + } + + public void testConsumerOpenCommit() throws Exception + { + try + { + send(1, 0); + + _psession.commit(); + + sleep(3.0f); + + _csession.commit(); + } + catch (Exception e) + { + fail("Should have succeeded"); + } + + assertTrue("Listener should not have received exception", _caught.getCount() == 1); + + monitor(0, 0); + } + + public void testConsumerOpenRollback() throws Exception + { + try + { + send(1, 0); + + _psession.commit(); + + sleep(3.0f); + + _csession.rollback(); + } + catch (Exception e) + { + fail("Should have succeeded"); + } + + assertTrue("Listener should not have received exception", _caught.getCount() == 1); + + monitor(0, 0); + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTestCase.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTestCase.java new file mode 100644 index 0000000000..637f43fb2c --- /dev/null +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/unit/transacted/TransactionTimeoutTestCase.java @@ -0,0 +1,253 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT 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.test.unit.transacted; + +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import javax.jms.DeliveryMode; +import javax.jms.ExceptionListener; +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.Queue; +import javax.jms.TextMessage; + +import junit.framework.TestCase; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.AMQConnection; +import org.apache.qpid.client.AMQConnectionURL; +import org.apache.qpid.client.AMQQueue; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.jms.ConnectionURL; +import org.apache.qpid.jms.Session; +import org.apache.qpid.protocol.AMQConstant; +import org.apache.qpid.test.utils.QpidBrokerTestCase; +import org.apache.qpid.util.LogMonitor; + +/** + * The {@link TestCase} for transaction timeout testing. + */ +public class TransactionTimeoutTestCase extends QpidBrokerTestCase implements ExceptionListener +{ + public static final String VIRTUALHOST = "test"; + public static final String TEXT = "0123456789abcdefghiforgettherest"; + public static final String CHN_OPEN_TXN = "CHN-1007"; + public static final String CHN_IDLE_TXN = "CHN-1008"; + public static final String IDLE = "Idle"; + public static final String OPEN = "Open"; + + protected LogMonitor _monitor; + protected AMQConnection _con; + protected Session _psession, _csession; + protected Queue _queue; + protected MessageConsumer _consumer; + protected MessageProducer _producer; + protected CountDownLatch _caught = new CountDownLatch(1); + protected String _message; + protected Exception _exception; + protected AMQConstant _code; + + protected void configure() throws Exception + { + // Setup housekeeping every second + setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".housekeeping.expiredMessageCheckPeriod", "100"); + + /* + * Set transaction timout properties. The XML in the virtualhosts configuration is as follows: + * + * <transactionTimeout> + * <openWarn>1000</openWarn> + * <openClose>2000</openClose> + * <idleWarn>500</idleWarn> + * <idleClose>1500</idleClose> + * </transactionTimeout> + */ + setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.openWarn", "1000"); + setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.openClose", "2000"); + setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.idleWarn", "500"); + setConfigurationProperty("virtualhosts.virtualhost." + VIRTUALHOST + ".transactionTimeout.idleClose", "1000"); + } + + protected void setUp() throws Exception + { + // Configure timeouts + configure(); + + // Monitor log file + _monitor = new LogMonitor(_outputFile); + + // Start broker + super.setUp(); + + // Connect to broker + String broker = _broker.equals(VM) ? ("vm://:" + DEFAULT_VM_PORT) : ("tcp://localhost:" + DEFAULT_PORT); + ConnectionURL url = new AMQConnectionURL("amqp://guest:guest@clientid/test?brokerlist='" + broker + "'&maxprefetch='1'"); + _con = (AMQConnection) getConnection(url); + _con.setExceptionListener(this); + _con.start(); + + // Create queue + Session qsession = _con.createSession(true, Session.SESSION_TRANSACTED); + AMQShortString queueName = new AMQShortString("test"); + _queue = new AMQQueue(qsession.getDefaultQueueExchangeName(), queueName, queueName, false, true); + qsession.close(); + + // Create producer and consumer + producer(); + consumer(); + } + + protected void tearDown() throws Exception + { + try + { + _con.close(); + } + finally + { + super.tearDown(); + } + } + + /** + * Create a transacted persistent message producer session. + */ + protected void producer() throws Exception + { + _psession = _con.createSession(true, Session.SESSION_TRANSACTED); + _producer = _psession.createProducer(_queue); + _producer.setDeliveryMode(DeliveryMode.PERSISTENT); + } + + /** + * Create a transacted message consumer session. + */ + protected void consumer() throws Exception + { + _csession = _con.createSession(true, Session.SESSION_TRANSACTED); + _consumer = _csession.createConsumer(_queue); + } + + /** + * Send a number of messages to the queue, optionally pausing after each. + */ + protected void send(int count, float delay) throws Exception + { + for (int i = 0; i < count; i++) + { + sleep(delay); + Message msg = _psession.createTextMessage(TEXT); + msg.setIntProperty("i", i); + _producer.send(msg); + } + } + + /** + * Sleep for a number of seconds. + */ + protected void sleep(float seconds) throws Exception + { + try + { + Thread.sleep((long) (seconds * 1000.0f)); + } + catch (InterruptedException ie) + { + throw new RuntimeException("Interrupted"); + } + } + + /** + * Check for idle and open messages. + * + * Either exactly zero messages, or +-2 error accepted around the specified number. + */ + protected void monitor(int idle, int open) throws Exception + { + List<String> idleMsgs = _monitor.findMatches(CHN_IDLE_TXN); + List<String> openMsgs = _monitor.findMatches(CHN_OPEN_TXN); + + String idleErr = "Expected " + idle + " but found " + idleMsgs.size() + " txn idle messages"; + String openErr = "Expected " + open + " but found " + openMsgs.size() + " txn open messages"; + + if (idle == 0) + { + assertTrue(idleErr, idleMsgs.isEmpty()); + } + else + { + assertTrue(idleErr, idleMsgs.size() >= idle - 2 && idleMsgs.size() <= idle + 2); + } + + if (open == 0) + { + assertTrue(openErr, openMsgs.isEmpty()); + } + else + { + assertTrue(openErr, openMsgs.size() >= open - 2 && openMsgs.size() <= open + 2); + } + } + + /** + * Receive a number of messages, optionally pausing after each. + */ + protected void expect(int count, float delay) throws Exception + { + for (int i = 0; i < count; i++) + { + sleep(delay); + Message msg = _consumer.receive(1000); + assertNotNull("Message should not be null", msg); + assertTrue("Message should be a text message", msg instanceof TextMessage); + assertEquals("Message content does not match expected", TEXT, ((TextMessage) msg).getText()); + assertEquals("Message order is incorrect", i, msg.getIntProperty("i")); + } + } + + /** + * Checks that the correct exception was thrown and was received + * by the listener with a 506 error code. + */ + protected void check(String reason)throws InterruptedException + { + assertTrue("Should have caught exception in listener", _caught.await(1, TimeUnit.SECONDS)); + assertNotNull("Should have thrown exception to client", _exception); + assertTrue("Exception message should contain '" + reason + "': " + _message, _message.contains(reason + " transaction timed out")); + assertNotNull("Exception should have an error code", _code); + assertEquals("Error code should be 506", AMQConstant.RESOURCE_ERROR, _code); + } + + /** @see javax.jms.ExceptionListener#onException(javax.jms.JMSException) */ + public void onException(JMSException jmse) + { + _caught.countDown(); + _message = jmse.getLinkedException().getMessage(); + if (jmse.getLinkedException() instanceof AMQException) + { + _code = ((AMQException) jmse.getLinkedException()).getErrorCode(); + } + } +} diff --git a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java index ff80c91fac..9f6963643a 100644 --- a/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java +++ b/qpid/java/systests/src/main/java/org/apache/qpid/test/utils/JMXTestUtils.java @@ -21,6 +21,8 @@ package org.apache.qpid.test.utils; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import javax.management.JMException; @@ -31,14 +33,18 @@ import javax.management.ObjectName; import javax.management.MalformedObjectNameException; import javax.management.remote.JMXConnector; +import junit.framework.TestCase; + import org.apache.commons.configuration.ConfigurationException; 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.management.common.mbeans.LoggingManagement; import org.apache.qpid.management.common.mbeans.ConfigurationManagement; import org.apache.qpid.management.common.mbeans.ManagedQueue; +import org.apache.qpid.management.common.mbeans.ServerInformation; import org.apache.qpid.management.common.mbeans.UserManagement; /** @@ -232,7 +238,8 @@ public class JMXTestUtils { // Get the name of the test manager AllObjects allObject = new AllObjects(_mbsc); - allObject.querystring = "org.apache.qpid:type=VirtualHost.VirtualHostManager,VirtualHost=" + vhostName + ",*"; + allObject.querystring = "org.apache.qpid:type=VirtualHost.VirtualHostManager,VirtualHost=" + + ObjectName.quote(vhostName) + ",*"; Set<ObjectName> objectNames = allObject.returnObjects(); @@ -259,7 +266,9 @@ public class JMXTestUtils { // Get the name of the test manager AllObjects allObject = new AllObjects(_mbsc); - allObject.querystring = "org.apache.qpid:type=VirtualHost.Queue,VirtualHost=" + virtualHostName + ",name=" + queue + ",*"; + allObject.querystring = "org.apache.qpid:type=VirtualHost.Queue,VirtualHost=" + + ObjectName.quote(virtualHostName) + ",name=" + + ObjectName.quote(queue) + ",*"; Set<ObjectName> objectNames = allObject.returnObjects(); @@ -287,7 +296,9 @@ public class JMXTestUtils { // Get the name of the test manager AllObjects allObject = new AllObjects(_mbsc); - allObject.querystring = "org.apache.qpid:type=VirtualHost.Exchange,VirtualHost=" + virtualHostName + ",name=" + exchange + ",*"; + allObject.querystring = "org.apache.qpid:type=VirtualHost.Exchange,VirtualHost=" + + ObjectName.quote(virtualHostName) + ",name=" + + ObjectName.quote(exchange) + ",*"; Set<ObjectName> objectNames = allObject.returnObjects(); @@ -321,6 +332,16 @@ public class JMXTestUtils return MBeanServerInvocationHandler.newProxyInstance(_mbsc, objectName, managedClass, false); } + public <T> List<T> getManagedObjectList(Class<T> managedClass, Set<ObjectName> objectNames) + { + List<T> objects = new ArrayList<T>(); + for (ObjectName name : objectNames) + { + objects.add(getManagedObject(managedClass, name)); + } + return objects; + } + public ManagedBroker getManagedBroker(String virtualHost) { return getManagedObject(ManagedBroker.class, getVirtualHostManagerObjectName(virtualHost)); @@ -355,4 +376,54 @@ public class JMXTestUtils ObjectName objectName = new ObjectName("org.apache.qpid:type=UserManagement,name=UserManagement"); return getManagedObject(UserManagement.class, objectName); } + + /** + * Retrive {@link ServerInformation} JMX MBean. + */ + public ServerInformation getServerInformation() + { + // Get the name of the test manager + AllObjects allObject = new AllObjects(_mbsc); + allObject.querystring = "org.apache.qpid:type=ServerInformation,name=ServerInformation,*"; + + Set<ObjectName> objectNames = allObject.returnObjects(); + + TestCase.assertNotNull("Null ObjectName Set returned", objectNames); + TestCase.assertEquals("Incorrect number of objects returned", 1, objectNames.size()); + + // We have verified we have only one value in objectNames so return it + return getManagedObject(ServerInformation.class, objectNames.iterator().next()); + } + + /** + * Retrive all {@link ManagedConnection} objects. + */ + public List<ManagedConnection> getAllManagedConnections() + { + // Get the name of the test manager + AllObjects allObject = new AllObjects(_mbsc); + allObject.querystring = "org.apache.qpid:type=VirtualHost.Connection,VirtualHost=*,name=*"; + + Set<ObjectName> objectNames = allObject.returnObjects(); + + TestCase.assertNotNull("Null ObjectName Set returned", objectNames); + + return getManagedObjectList(ManagedConnection.class, objectNames); + } + + /** + * Retrive all {@link ManagedConnection} objects for a particular virtual host. + */ + public List<ManagedConnection> getManagedConnections(String vhost) + { + // Get the name of the test manager + AllObjects allObject = new AllObjects(_mbsc); + allObject.querystring = "org.apache.qpid:type=VirtualHost.Connection,VirtualHost=" + ObjectName.quote(vhost) + ",name=*"; + + Set<ObjectName> objectNames = allObject.returnObjects(); + + TestCase.assertNotNull("Null ObjectName Set returned", objectNames); + + return getManagedObjectList(ManagedConnection.class, objectNames); + } } diff --git a/qpid/java/test-profiles/CPPExcludes b/qpid/java/test-profiles/CPPExcludes index e89b09cca2..4127682208 100755 --- a/qpid/java/test-profiles/CPPExcludes +++ b/qpid/java/test-profiles/CPPExcludes @@ -165,6 +165,11 @@ org.apache.qpid.server.security.firewall.FirewallConfigTest#* org.apache.qpid.server.security.firewall.FirewallConfigurationTest#* org.apache.qpid.server.plugins.PluginTest#* +// Transacion timeouts not implemented in CPP broker +org.apache.qpid.test.unit.transacted.TransactionTimeoutDisabledTest#* +org.apache.qpid.test.unit.transacted.TransactionTimeoutConfigurationTest#* +org.apache.qpid.test.unit.transacted.TransactionTimeoutTest#* + // Java broker only org.apache.qpid.server.logging.management.LoggingManagementMBeanTest#* org.apache.qpid.server.management.AMQUserManagementMBeanTest#* diff --git a/qpid/java/test-profiles/Java010Excludes b/qpid/java/test-profiles/Java010Excludes index 3486d5c70c..c05aad0cb1 100755 --- a/qpid/java/test-profiles/Java010Excludes +++ b/qpid/java/test-profiles/Java010Excludes @@ -47,6 +47,7 @@ org.apache.qpid.server.logging.SubscriptionLoggingTest#testSubscriptionSuspend // 0-10 Broker does not have a JMX connection MBean org.apache.qpid.management.jmx.ManagementActorLoggingTest#testConnectionCloseViaManagement +org.apache.qpid.management.jmx.MessageConnectionStatisticsTest#* // 0-10 has different ideas about clientid and ownership of queues org.apache.qpid.server.queue.ModelTest#* @@ -54,9 +55,6 @@ org.apache.qpid.server.queue.ModelTest#* // 0-10 is not supported by the MethodRegistry org.apache.qpid.test.unit.close.JavaServerCloseRaceConditionTest#* -// QPID-2084 : this test needs more work for 0-10 -org.apache.qpid.test.unit.client.DynamicQueueExchangeCreateTest#* - //QPID-942 : Implemented Channel.Flow based Producer Side flow control to the Java Broker (not in CPP Broker) org.apache.qpid.server.queue.ProducerFlowControlTest#* @@ -74,6 +72,9 @@ org.apache.qpid.test.unit.publish.DirtyTransactedPublishTest#* org.apache.qpid.test.client.RollbackOrderTest#testOrderingAfterRollbackOnMessage org.apache.qpid.test.unit.ack.RecoverTest#testRecoverInAutoAckListener +// This test uses 0-8 channel frames +org.apache.qpid.test.unit.client.channelclose.ChannelCloseTest#* + //Temporarily adding the following until the issues are sorted out. //Should probably raise JIRAs for them. org.apache.qpid.transport.network.mina.MINANetworkDriverTest#* diff --git a/qpid/python/qpid/codec010.py b/qpid/python/qpid/codec010.py index 0846db6bf7..94a1cd4263 100644 --- a/qpid/python/qpid/codec010.py +++ b/qpid/python/qpid/codec010.py @@ -17,7 +17,7 @@ # under the License. # -import datetime +import datetime, string from packer import Packer from datatypes import serial, timestamp, RangedSet, Struct, UUID from ops import Compound, PRIMITIVE, COMPOUND @@ -241,15 +241,20 @@ class Codec(Packer): v = sc.read_primitive(type) result[k] = v return result + + def _write_map_elem(self, k, v): + type = self.encoding(v) + sc = StringCodec() + sc.write_str8(k) + sc.write_uint8(type.CODE) + sc.write_primitive(type, v) + return sc.encoded + 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) + sc.write(string.joinfields(map(self._write_map_elem, m.keys(), m.values()), "")) self.write_vbin32(sc.encoded) def read_array(self): diff --git a/qpid/python/qpid/messaging/endpoints.py b/qpid/python/qpid/messaging/endpoints.py index b8101b76e6..30c5850397 100644 --- a/qpid/python/qpid/messaging/endpoints.py +++ b/qpid/python/qpid/messaging/endpoints.py @@ -197,7 +197,7 @@ class Connection(Endpoint): return result def check_closed(self): - if self.closed: + if not self._connected: self._condition.gc() raise ConnectionClosed() diff --git a/qpid/python/qpid/tests/messaging/endpoints.py b/qpid/python/qpid/tests/messaging/endpoints.py index c303ca652a..0977b2ab3a 100644 --- a/qpid/python/qpid/tests/messaging/endpoints.py +++ b/qpid/python/qpid/tests/messaging/endpoints.py @@ -186,6 +186,9 @@ class ConnectionTests(Base): def setup_connection(self): return Connection.establish(self.broker, **self.connection_options()) + def testCheckClosed(self): + assert not self.conn.check_closed() + def testSessionAnon(self): ssn1 = self.conn.session() ssn2 = self.conn.session() diff --git a/qpid/python/setup.py b/qpid/python/setup.py index 9c04421c51..a301b3e4cc 100755 --- a/qpid/python/setup.py +++ b/qpid/python/setup.py @@ -298,7 +298,7 @@ class install_lib(_install_lib): return outfiles + extra setup(name="qpid-python", - version="0.9", + version="0.11", author="Apache Qpid", author_email="dev@qpid.apache.org", packages=["mllib", "qpid", "qpid.messaging", "qpid.tests", diff --git a/qpid/specs/management-schema.xml b/qpid/specs/management-schema.xml index e52e3f23cf..881632c881 100644 --- a/qpid/specs/management-schema.xml +++ b/qpid/specs/management-schema.xml @@ -176,6 +176,7 @@ <statistic name="unackedMessages" type="hilo32" unit="message" desc="Messages consumed but not yet acked"/> <statistic name="messageLatency" type="mmaTime" unit="nanosecond" desc="Broker latency through this queue"/> <statistic name="flowStopped" type="bool" desc="Flow control active."/> + <statistic name="flowStoppedCount" type="count32" desc="Number of times flow control was activated for this queue"/> <method name="purge" desc="Discard all or some messages on a queue"> <arg name="request" dir="I" type="uint32" desc="0 for all messages or n>0 for n messages"/> diff --git a/qpid/tests/setup.py b/qpid/tests/setup.py index 5438275b22..6a99fbb80f 100755 --- a/qpid/tests/setup.py +++ b/qpid/tests/setup.py @@ -20,7 +20,7 @@ from distutils.core import setup setup(name="qpid-tests", - version="0.9", + version="0.11", author="Apache Qpid", author_email="dev@qpid.apache.org", packages=["qpid_tests", "qpid_tests.broker_0_10", "qpid_tests.broker_0_9", diff --git a/qpid/tests/src/py/qpid_tests/broker_0_10/alternate_exchange.py b/qpid/tests/src/py/qpid_tests/broker_0_10/alternate_exchange.py index 0ffeb57172..8cbb5793d9 100644 --- a/qpid/tests/src/py/qpid_tests/broker_0_10/alternate_exchange.py +++ b/qpid/tests/src/py/qpid_tests/broker_0_10/alternate_exchange.py @@ -18,7 +18,7 @@ # import traceback from qpid.queue import Empty -from qpid.datatypes import Message +from qpid.datatypes import Message, RangedSet from qpid.testlib import TestBase010 from qpid.session import SessionException @@ -77,13 +77,7 @@ class AlternateExchangeTests(TestBase010): """ session = self.session #set up a 'dead letter queue': - session.exchange_declare(exchange="dlq", type="fanout") - session.queue_declare(queue="deleted", exclusive=True, auto_delete=True) - session.exchange_bind(exchange="dlq", queue="deleted") - session.message_subscribe(destination="dlq", queue="deleted") - session.message_flow(destination="dlq", unit=session.credit_unit.message, value=0xFFFFFFFFL) - session.message_flow(destination="dlq", unit=session.credit_unit.byte, value=0xFFFFFFFFL) - dlq = session.incoming("dlq") + dlq = self.setup_dlq() #create a queue using the dlq as its alternate exchange: session.queue_declare(queue="delete-me", alternate_exchange="dlq") @@ -236,6 +230,121 @@ class AlternateExchangeTests(TestBase010): self.assertEqual("Three", dlq.get(timeout=1).body) self.assertEmpty(dlq) + def test_queue_delete_loop(self): + """ + Test that if a queue is bound to its own alternate exchange, + then on deletion there is no infinite looping + """ + session = self.session + dlq = self.setup_dlq() + + #create a queue using the dlq as its alternate exchange: + session.queue_declare(queue="delete-me", alternate_exchange="dlq") + #bind that queue to the dlq as well: + session.exchange_bind(exchange="dlq", queue="delete-me") + #send it some messages: + dp=self.session.delivery_properties(routing_key="delete-me") + for m in ["One", "Two", "Three"]: + session.message_transfer(message=Message(dp, m)) + #delete it: + session.queue_delete(queue="delete-me") + #cleanup: + session.exchange_delete(exchange="dlq") + + #check the messages were delivered to the dlq: + for m in ["One", "Two", "Three"]: + self.assertEqual(m, dlq.get(timeout=1).body) + self.assertEmpty(dlq) + + def test_queue_delete_no_match(self): + """ + Test that on queue deletion, if the queues own alternate + exchange cannot find a match for the message, the + alternate-exchange of that exchange will be tried. Note: + though the spec rules out going to the alternate-exchanges + alternate exchange when sending to an exchange, it does not + cover this case. + """ + session = self.session + dlq = self.setup_dlq() + + #setu up an 'intermediary' exchange + session.exchange_declare(exchange="my-exchange", type="direct", alternate_exchange="dlq") + + #create a queue using the intermediary as its alternate exchange: + session.queue_declare(queue="delete-me", alternate_exchange="my-exchange") + #bind that queue to the dlq as well: + session.exchange_bind(exchange="dlq", queue="delete-me") + #send it some messages: + dp=self.session.delivery_properties(routing_key="delete-me") + for m in ["One", "Two", "Three"]: + session.message_transfer(message=Message(dp, m)) + + #delete it: + session.queue_delete(queue="delete-me") + #cleanup: + session.exchange_delete(exchange="my-exchange") + session.exchange_delete(exchange="dlq") + + #check the messages were delivered to the dlq: + for m in ["One", "Two", "Three"]: + self.assertEqual(m, dlq.get(timeout=1).body) + self.assertEmpty(dlq) + + def test_reject_no_match(self): + """ + Test that on rejecting a message, if the queues own alternate + exchange cannot find a match for the message, the + alternate-exchange of that exchange will be tried. Note: + though the spec rules out going to the alternate-exchanges + alternate exchange when sending to an exchange, it does not + cover this case. + """ + session = self.session + dlq = self.setup_dlq() + + #setu up an 'intermediary' exchange + session.exchange_declare(exchange="my-exchange", type="direct", alternate_exchange="dlq") + + #create a queue using the intermediary as its alternate exchange: + session.queue_declare(queue="delivery-queue", alternate_exchange="my-exchange", auto_delete=True) + #bind that queue to the dlq as well: + session.exchange_bind(exchange="dlq", queue="delivery-queue") + #send it some messages: + dp=self.session.delivery_properties(routing_key="delivery-queue") + for m in ["One", "Two", "Three"]: + session.message_transfer(message=Message(dp, m)) + + #get and reject those messages: + session.message_subscribe(destination="a", queue="delivery-queue") + session.message_flow(destination="a", unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination="a", unit=session.credit_unit.byte, value=0xFFFFFFFFL) + incoming = session.incoming("a") + for m in ["One", "Two", "Three"]: + msg = incoming.get(timeout=1) + self.assertEqual(m, msg.body) + session.message_reject(RangedSet(msg.id)) + session.message_cancel(destination="a") + + #check the messages were delivered to the dlq: + for m in ["One", "Two", "Three"]: + self.assertEqual(m, dlq.get(timeout=1).body) + self.assertEmpty(dlq) + #cleanup: + session.exchange_delete(exchange="my-exchange") + session.exchange_delete(exchange="dlq") + + def setup_dlq(self): + session = self.session + #set up 'dead-letter' handling: + session.exchange_declare(exchange="dlq", type="fanout") + session.queue_declare(queue="deleted", exclusive=True, auto_delete=True) + session.exchange_bind(exchange="dlq", queue="deleted") + session.message_subscribe(destination="dlq", queue="deleted") + session.message_flow(destination="dlq", unit=session.credit_unit.message, value=0xFFFFFFFFL) + session.message_flow(destination="dlq", unit=session.credit_unit.byte, value=0xFFFFFFFFL) + dlq = session.incoming("dlq") + return dlq def assertEmpty(self, queue): try: diff --git a/qpid/tests/src/py/qpid_tests/broker_0_10/exchange.py b/qpid/tests/src/py/qpid_tests/broker_0_10/exchange.py index 9a4cfd37d6..f51923fcf3 100644 --- a/qpid/tests/src/py/qpid_tests/broker_0_10/exchange.py +++ b/qpid/tests/src/py/qpid_tests/broker_0_10/exchange.py @@ -448,9 +448,9 @@ class MiscellaneousErrorsTests(TestHelper): def testTypeNotKnown(self): try: self.session.exchange_declare(exchange="test_type_not_known_exchange", type="invalid_type") - self.fail("Expected 503 for declaration of unknown exchange type.") + self.fail("Expected 404 for declaration of unknown exchange type.") except SessionException, e: - self.assertEquals(503, e.args[0].error_code) + self.assertEquals(404, e.args[0].error_code) def testDifferentDeclaredType(self): self.exchange_declare(exchange="test_different_declared_type_exchange", type="direct") diff --git a/qpid/tests/src/py/qpid_tests/broker_0_10/extensions.py b/qpid/tests/src/py/qpid_tests/broker_0_10/extensions.py index 26ea3cb0e9..11e03f8a1c 100644 --- a/qpid/tests/src/py/qpid_tests/broker_0_10/extensions.py +++ b/qpid/tests/src/py/qpid_tests/broker_0_10/extensions.py @@ -20,6 +20,8 @@ from qpid.client import Client, Closed from qpid.queue import Empty from qpid.content import Content from qpid.testlib import TestBase010 +from qpid.session import SessionException +from qpid.datatypes import uuid4 from time import sleep class ExtensionTests(TestBase010): @@ -28,10 +30,57 @@ class ExtensionTests(TestBase010): def test_timed_autodelete(self): session = self.session session2 = self.conn.session("another-session") - session2.queue_declare(queue="my-queue", exclusive=True, auto_delete=True, arguments={"qpid.auto_delete_timeout":5}) + session2.queue_declare(queue="my-queue", exclusive=True, auto_delete=True, arguments={"qpid.auto_delete_timeout":3}) session2.close() result = session.queue_query(queue="my-queue") self.assertEqual("my-queue", result.queue) sleep(5) result = session.queue_query(queue="my-queue") self.assert_(not result.queue) + + def valid_policy_args(self, args, name="test-queue"): + try: + self.session.queue_declare(queue=name, arguments=args) + self.session.queue_delete(queue=name) # cleanup + except SessionException, e: + self.fail("declare with valid policy args failed: %s" % (args)) + self.session = self.conn.session("replacement", 2) + + def invalid_policy_args(self, args, name="test-queue"): + # go through invalid declare attempts twice to make sure that + # the queue doesn't actually get created first time around + # even if exception is thrown + for i in range(1, 3): + try: + self.session.queue_declare(queue=name, arguments=args) + self.session.queue_delete(queue=name) # cleanup + self.fail("declare with invalid policy args suceeded: %s (iteration %d)" % (args, i)) + except SessionException, e: + self.session = self.conn.session(str(uuid4())) + + def test_policy_max_size_as_valid_string(self): + self.valid_policy_args({"qpid.max_size":"3"}) + + def test_policy_max_count_as_valid_string(self): + self.valid_policy_args({"qpid.max_count":"3"}) + + def test_policy_max_count_and_size_as_valid_strings(self): + self.valid_policy_args({"qpid.max_count":"3","qpid.max_size":"0"}) + + def test_policy_negative_count(self): + self.invalid_policy_args({"qpid.max_count":-1}) + + def test_policy_negative_size(self): + self.invalid_policy_args({"qpid.max_size":-1}) + + def test_policy_size_as_invalid_string(self): + self.invalid_policy_args({"qpid.max_size":"foo"}) + + def test_policy_count_as_invalid_string(self): + self.invalid_policy_args({"qpid.max_count":"foo"}) + + def test_policy_size_as_float(self): + self.invalid_policy_args({"qpid.max_size":3.14159}) + + def test_policy_count_as_float(self): + self.invalid_policy_args({"qpid.max_count":"2222222.22222"}) diff --git a/qpid/tests/src/py/qpid_tests/broker_0_10/management.py b/qpid/tests/src/py/qpid_tests/broker_0_10/management.py index 06f3212a6f..952878e0b7 100644 --- a/qpid/tests/src/py/qpid_tests/broker_0_10/management.py +++ b/qpid/tests/src/py/qpid_tests/broker_0_10/management.py @@ -242,6 +242,38 @@ class ManagementTest (TestBase010): pq = self.qmf.getObjects(_class="queue", name="purge-queue")[0] self.assertEqual (pq.msgDepth,0) + def test_reroute_priority_queue(self): + self.startQmf() + session = self.session + + #setup test queue supporting multiple priority levels + session.queue_declare(queue="test-queue", exclusive=True, auto_delete=True, arguments={'x-qpid-priorities':10}) + + #send some messages of varying priority to that queue: + for i in range(0, 5): + deliveryProps = session.delivery_properties(routing_key="test-queue", priority=i+5) + session.message_transfer(message=Message(deliveryProps, "Message %d" % (i+1))) + + + #declare and bind a queue to amq.fanout through which rerouted + #messages can be verified: + session.queue_declare(queue="rerouted", exclusive=True, auto_delete=True, arguments={'x-qpid-priorities':10}) + session.exchange_bind(queue="rerouted", exchange="amq.fanout") + + #reroute messages from test queue to amq.fanout (and hence to + #rerouted queue): + pq = self.qmf.getObjects(_class="queue", name="test-queue")[0] + result = pq.reroute(0, False, "amq.fanout") + self.assertEqual(result.status, 0) + + #verify messages are all rerouted: + self.subscribe(destination="incoming", queue="rerouted") + incoming = session.incoming("incoming") + for i in range(0, 5): + msg = incoming.get(timeout=1) + self.assertEqual("Message %d" % (5-i), msg.body) + + def test_reroute_queue(self): """ Test ability to reroute messages from the head of a queue. @@ -309,7 +341,40 @@ class ManagementTest (TestBase010): self.assertEqual(result.status, 0) pq.update() self.assertEqual(pq.msgDepth,20) - + + def test_reroute_alternate_exchange(self): + """ + Test that when rerouting, the alternate-exchange is considered if relevant + """ + self.startQmf() + session = self.session + # 1. Create 2 exchanges A and B (fanout) where B is the + # alternate exchange for A + session.exchange_declare(exchange="B", type="fanout") + session.exchange_declare(exchange="A", type="fanout", alternate_exchange="B") + + # 2. Bind queue X to B + session.queue_declare(queue="X", exclusive=True, auto_delete=True) + session.exchange_bind(queue="X", exchange="B") + + # 3. Send 1 message to queue Y + session.queue_declare(queue="Y", exclusive=True, auto_delete=True) + props = session.delivery_properties(routing_key="Y") + session.message_transfer(message=Message(props, "reroute me!")) + + # 4. Call reroute on queue Y and specify that messages should + # be sent to exchange A + y = self.qmf.getObjects(_class="queue", name="Y")[0] + result = y.reroute(1, False, "A") + self.assertEqual(result.status, 0) + + # 5. verify that the message is rerouted through B (as A has + # no matching bindings) to X + self.subscribe(destination="x", queue="X") + self.assertEqual("reroute me!", session.incoming("x").get(timeout=1).body) + + # Cleanup + for e in ["A", "B"]: session.exchange_delete(exchange=e) def test_methods_async (self): """ diff --git a/qpid/tests/src/py/qpid_tests/broker_0_10/message.py b/qpid/tests/src/py/qpid_tests/broker_0_10/message.py index e80333a1e6..b46c446833 100644 --- a/qpid/tests/src/py/qpid_tests/broker_0_10/message.py +++ b/qpid/tests/src/py/qpid_tests/broker_0_10/message.py @@ -245,9 +245,19 @@ class MessageTests(TestBase010): self.fail("Got message after cancellation: " + msg) except Empty: None - #cancellation of non-existant consumers should be handled without error - session.message_cancel(destination="my-consumer") - session.message_cancel(destination="this-never-existed") + #cancellation of non-existant consumers should be result in 404s + try: + session.message_cancel(destination="my-consumer") + self.fail("Expected 404 for recancellation of subscription.") + except SessionException, e: + self.assertEquals(404, e.args[0].error_code) + + session = self.conn.session("alternate-session", timeout=10) + try: + session.message_cancel(destination="this-never-existed") + self.fail("Expected 404 for cancellation of unknown subscription.") + except SessionException, e: + self.assertEquals(404, e.args[0].error_code) def test_ack(self): diff --git a/qpid/tests/src/py/qpid_tests/broker_0_10/priority.py b/qpid/tests/src/py/qpid_tests/broker_0_10/priority.py index 3651a1218b..6a60add97e 100644 --- a/qpid/tests/src/py/qpid_tests/broker_0_10/priority.py +++ b/qpid/tests/src/py/qpid_tests/broker_0_10/priority.py @@ -33,13 +33,13 @@ class PriorityTests (Base): def setup_session(self): return self.conn.session() - def prioritised_delivery(self, priorities, levels=10): + def prioritised_delivery(self, priorities, levels=10, key="x-qpid-priorities"): """ Test that message on a queue are delivered in priority order. """ msgs = [Message(content=str(uuid4()), priority = p) for p in priorities] - snd = self.ssn.sender("priority-queue; {create: sender, delete: receiver, node: {x-declare:{arguments:{x-qpid-priorities:%s}}}}" % levels, + snd = self.ssn.sender("priority-queue; {create: sender, delete: receiver, node: {x-declare:{arguments:{'%s':%s}}}}" % (key, levels), durable=self.durable()) for m in msgs: snd.send(m) @@ -50,16 +50,16 @@ class PriorityTests (Base): assert msg.content == expected.content self.ssn.acknowledge(msg) - def fairshare_delivery(self, priorities, default_limit=5, limits=None, levels=10): + def fairshare_delivery(self, priorities, default_limit=5, limits=None, levels=10, level_key="x-qpid-priorities", fairshare_key="x-qpid-fairshare"): msgs = [Message(content=str(uuid4()), priority = p) for p in priorities] - limit_policy = "x-qpid-fairshare:%s" % default_limit + limit_policy = "'%s':%s" % (fairshare_key, default_limit) if limits: for k, v in limits.items(): - limit_policy += ", x-qpid-fairshare-%s:%s" % (k, v) + limit_policy += ", '%s-%s':%s" % (fairshare_key, k, v) - snd = self.ssn.sender("priority-queue; {create: sender, delete: receiver, node: {x-declare:{arguments:{x-qpid-priorities:%s, %s}}}}" - % (levels, limit_policy), + snd = self.ssn.sender("priority-queue; {create: sender, delete: receiver, node: {x-declare:{arguments:{'%s':%s, %s}}}}" + % (level_key, levels, limit_policy), durable=self.durable()) for m in msgs: snd.send(m) @@ -79,12 +79,18 @@ class PriorityTests (Base): def test_prioritised_delivery_1(self): self.prioritised_delivery(priorities = [8,9,5,1,2,2,3,4,15,7,8,10,10,2], levels = 10) + def test_prioritised_delivery_with_alias(self): + self.prioritised_delivery(priorities = [8,9,5,1,2,2,3,4,15,7,8,10,10,2], levels = 10, key="qpid.priorities") + def test_prioritised_delivery_2(self): self.prioritised_delivery(priorities = [8,9,5,1,2,2,3,4,15,7,8,10,10,2], levels = 5) def test_fairshare_1(self): self.fairshare_delivery(priorities = [4,5,3,6,10,10,2,10,2,10,10,1,10,10,10,3,3,3,10,10,3,10,3,10,10,10,10,10,10,2,3]) + def test_fairshare_with_alias(self): + self.fairshare_delivery(priorities = [4,5,3,6,10,10,2,10,2,10,10,1,10,10,10,3,3,3,10,10,2,3], level_key="qpid.priorities", fairshare_key="qpid.fairshare") + def test_fairshare_2(self): self.fairshare_delivery(priorities = [10 for i in range(30)]) diff --git a/qpid/tests/src/py/qpid_tests/broker_0_10/threshold.py b/qpid/tests/src/py/qpid_tests/broker_0_10/threshold.py index bcd3c507e2..6628ae8424 100644 --- a/qpid/tests/src/py/qpid_tests/broker_0_10/threshold.py +++ b/qpid/tests/src/py/qpid_tests/broker_0_10/threshold.py @@ -60,3 +60,18 @@ class ThresholdTests (Base): def test_alert_size_alias(self): self.do_threshold_test("x-qpid-maximum-message-size", 15, [Message("msg-%s" % i) for i in range(3)]) + + def test_alert_on_alert_queue(self): + rcv = self.ssn.receiver("qmf.default.topic/agent.ind.event.org_apache_qpid_broker.queueThresholdExceeded.#; {link:{x-declare:{arguments:{'qpid.alert_count':1}}}}") + rcvQMFv1 = self.ssn.receiver("qpid.management/console.event.#; {link:{x-declare:{arguments:{'qpid.alert_count':1}}}}") + snd = self.ssn.sender("ttq; {create:always, node: {x-declare:{auto_delete:True,exclusive:True,arguments:{'qpid.alert_count':1}}}}") + snd.send(Message("my-message")) + queues = [] + for i in range(2): + event = rcv.fetch() + schema = event.content[0]["_schema_id"] + assert schema["_class_name"] == "queueThresholdExceeded" + values = event.content[0]["_values"] + queues.append(values["qName"]) + assert "ttq" in queues, "expected event for ttq (%s)" % (queues) + diff --git a/qpid/tools/setup.py b/qpid/tools/setup.py index 8811e49682..58863a07d9 100755 --- a/qpid/tools/setup.py +++ b/qpid/tools/setup.py @@ -20,7 +20,7 @@ from distutils.core import setup setup(name="qpid-tools", - version="0.9", + version="0.11", author="Apache Qpid", author_email="dev@qpid.apache.org", scripts=["src/py/qpid-cluster", @@ -30,7 +30,8 @@ setup(name="qpid-tools", "src/py/qpid-queue-stats", "src/py/qpid-route", "src/py/qpid-stat", - "src/py/qpid-tool"], + "src/py/qpid-tool", + "src/py/qmf-tool"], url="http://qpid.apache.org/", license="Apache Software License", description="Diagnostic and management tools for Apache Qpid brokers.") diff --git a/qpid/tools/src/py/qpid-config b/qpid/tools/src/py/qpid-config index 9ff405541c..da490e831a 100755 --- a/qpid/tools/src/py/qpid-config +++ b/qpid/tools/src/py/qpid-config @@ -101,6 +101,7 @@ class Config: self._flowResumeCount = None self._flowStopSize = None self._flowResumeSize = None + self._extra_arguments = [] config = Config() @@ -119,6 +120,13 @@ FLOW_STOP_COUNT = "qpid.flow_stop_count" FLOW_RESUME_COUNT = "qpid.flow_resume_count" FLOW_STOP_SIZE = "qpid.flow_stop_size" FLOW_RESUME_SIZE = "qpid.flow_resume_size" +#There are various arguments to declare that have specific program +#options in this utility. However there is now a generic mechanism for +#passing arguments as well. The SPECIAL_ARGS list contains the +#arguments for which there are specific program options defined +#i.e. the arguments for which there is special processing on add and +#list +SPECIAL_ARGS=[FILECOUNT,FILESIZE,MAX_QUEUE_SIZE,MAX_QUEUE_COUNT,POLICY_TYPE,CLUSTER_DURABLE,LVQ,LVQNB,MSG_SEQUENCE,IVE,QUEUE_EVENT_GENERATION,FLOW_STOP_COUNT,FLOW_STOP_SIZE,FLOW_RESUME_SIZE] class JHelpFormatter(IndentedHelpFormatter): """Format usage and description without stripping newlines from usage strings @@ -178,6 +186,8 @@ def OptionsAndArguments(argv): help="Turn on sender flow control when the number of queued messages exceeds this value.") group3.add_option("--flow-resume-count", action="store", type="int", metavar="<n>", help="Turn off sender flow control when the number of queued messages drops below this value.") + group3.add_option("--argument", dest="extra_arguments", action="append", default=[], + metavar="<NAME=VALUE>", help="Specify a key-value pair to add to queue arguments") # no option for declaring an exclusive queue - which can only be used by the session that creates it. parser.add_option_group(group3) @@ -257,6 +267,8 @@ def OptionsAndArguments(argv): config._flowStopCount = opts.flow_stop_count if opts.flow_resume_count: config._flowResumeCount = opts.flow_resume_count + if opts.extra_arguments: + config._extra_arguments = opts.extra_arguments return args @@ -360,6 +372,7 @@ class BrokerManager: if self.match(ex.name, filter): print "%-10s%-*s " % (ex.type, maxNameLen, ex.name), args = ex.arguments + if not args: args = {} if ex.durable: print "--durable", if MSG_SEQUENCE in args and args[MSG_SEQUENCE] == 1: print "--sequence", if IVE in args and args[IVE] == 1: print "--ive", @@ -401,25 +414,26 @@ class BrokerManager: if self.match(q.name, filter): print "%-*s " % (maxNameLen, q.name), args = q.arguments + if not args: args = {} if q.durable: print "--durable", if CLUSTER_DURABLE in args and args[CLUSTER_DURABLE] == 1: print "--cluster-durable", if q.autoDelete: print "auto-del", if q.exclusive: print "excl", - if FILESIZE in args: print "--file-size=%d" % args[FILESIZE], - if FILECOUNT in args: print "--file-count=%d" % args[FILECOUNT], - if MAX_QUEUE_SIZE in args: print "--max-queue-size=%d" % args[MAX_QUEUE_SIZE], - if MAX_QUEUE_COUNT in args: print "--max-queue-count=%d" % args[MAX_QUEUE_COUNT], + if FILESIZE in args: print "--file-size=%s" % args[FILESIZE], + if FILECOUNT in args: print "--file-count=%s" % args[FILECOUNT], + if MAX_QUEUE_SIZE in args: print "--max-queue-size=%s" % args[MAX_QUEUE_SIZE], + if MAX_QUEUE_COUNT in args: print "--max-queue-count=%s" % args[MAX_QUEUE_COUNT], if POLICY_TYPE in args: print "--limit-policy=%s" % args[POLICY_TYPE].replace("_", "-"), if LVQ in args and args[LVQ] == 1: print "--order lvq", if LVQNB in args and args[LVQNB] == 1: print "--order lvq-no-browse", - if QUEUE_EVENT_GENERATION in args: print "--generate-queue-events=%d" % args[QUEUE_EVENT_GENERATION], + if QUEUE_EVENT_GENERATION in args: print "--generate-queue-events=%s" % args[QUEUE_EVENT_GENERATION], if q.altExchange: print "--alternate-exchange=%s" % q._altExchange_.name, - if FLOW_STOP_SIZE in args: print "--flow-stop-size=%d" % args[FLOW_STOP_SIZE], - if FLOW_RESUME_SIZE in args: print "--flow-resume-size=%d" % args[FLOW_RESUME_SIZE], - if FLOW_STOP_COUNT in args: print "--flow-stop-count=%d" % args[FLOW_STOP_COUNT], - if FLOW_RESUME_COUNT in args: print "--flow-resume-count=%d" % args[FLOW_RESUME_COUNT], - print + if FLOW_STOP_SIZE in args: print "--flow-stop-size=%s" % args[FLOW_STOP_SIZE], + if FLOW_RESUME_SIZE in args: print "--flow-resume-size=%s" % args[FLOW_RESUME_SIZE], + if FLOW_STOP_COUNT in args: print "--flow-stop-count=%s" % args[FLOW_STOP_COUNT], + if FLOW_RESUME_COUNT in args: print "--flow-resume-count=%s" % args[FLOW_RESUME_COUNT], + print " ".join(["--argument %s=%s" % (k, v) for k,v in args.iteritems() if not k in SPECIAL_ARGS]) def QueueListRecurse(self, filter): exchanges = self.qmf.getObjects(_class="exchange", _agent=self.brokerAgent) @@ -464,6 +478,12 @@ class BrokerManager: Usage() qname = args[0] declArgs = {} + for a in config._extra_arguments: + r = a.split("=", 1) + if len(r) == 2: value = r[1] + else: value = None + declArgs[r[0]] = value + if config._durable: declArgs[FILECOUNT] = config._fileCount declArgs[FILESIZE] = config._fileSize diff --git a/qpid/tools/src/py/qpid-printevents b/qpid/tools/src/py/qpid-printevents index 2be2f0be8b..d56d2899b1 100755 --- a/qpid/tools/src/py/qpid-printevents +++ b/qpid/tools/src/py/qpid-printevents @@ -20,7 +20,7 @@ # import os -import optparse +import optparse from optparse import IndentedHelpFormatter import sys import socket @@ -62,11 +62,11 @@ _usage = "%prog [options] [broker-addr]..." _description = \ """ -Collect and print events from one or more Qpid message brokers. +Collect and print events from one or more Qpid message brokers. If no broker-addr is supplied, %prog connects to 'localhost:5672'. -[broker-addr] syntax: +[broker-addr] syntax: [username/password@] hostname ip-address [:<port>] @@ -91,20 +91,20 @@ def main(argv=None): session = Session(console, rcvObjects=False, rcvHeartbeats=options.heartbeats, manageConnections=True) brokers = [] try: - for host in arguments: - brokers.append(session.addBroker(host, None, options.sasl_mechanism)) + try: + for host in arguments: + brokers.append(session.addBroker(host, None, options.sasl_mechanism)) - while (True): - sleep(10) + while (True): + sleep(10) - except KeyboardInterrupt: - print - return 0 - - except Exception, e: - print "Failed: %s - %s" % (e.__class__.__name__, e) - return 1 + except KeyboardInterrupt: + print + return 0 + except Exception, e: + print "Failed: %s - %s" % (e.__class__.__name__, e) + return 1 finally: while len(brokers): b = brokers.pop() diff --git a/qpid/tools/src/py/qpid-route b/qpid/tools/src/py/qpid-route index 3c4de85d1e..516c02d5cd 100755 --- a/qpid/tools/src/py/qpid-route +++ b/qpid/tools/src/py/qpid-route @@ -27,18 +27,18 @@ import locale from qmf.console import Session, BrokerURL usage = """ -Usage: qpid-route [OPTIONS] dynamic add <dest-broker> <src-broker> <exchange> [tag] [exclude-list] +Usage: qpid-route [OPTIONS] dynamic add <dest-broker> <src-broker> <exchange> [tag] [exclude-list] [mechanism] qpid-route [OPTIONS] dynamic del <dest-broker> <src-broker> <exchange> qpid-route [OPTIONS] route add <dest-broker> <src-broker> <exchange> <routing-key> [tag] [exclude-list] [mechanism] qpid-route [OPTIONS] route del <dest-broker> <src-broker> <exchange> <routing-key> - qpid-route [OPTIONS] queue add <dest-broker> <src-broker> <exchange> <queue> + qpid-route [OPTIONS] queue add <dest-broker> <src-broker> <exchange> <queue> [mechanism] qpid-route [OPTIONS] queue del <dest-broker> <src-broker> <exchange> <queue> qpid-route [OPTIONS] route list [<dest-broker>] qpid-route [OPTIONS] route flush [<dest-broker>] qpid-route [OPTIONS] route map [<broker>] - qpid-route [OPTIONS] link add <dest-broker> <src-broker> + qpid-route [OPTIONS] link add <dest-broker> <src-broker> [mechanism] qpid-route [OPTIONS] link del <dest-broker> <src-broker> qpid-route [OPTIONS] link list [<dest-broker>]""" @@ -61,7 +61,7 @@ class Config: self._transport = "tcp" self._ack = 0 self._connTimeout = 10 - self._sasl_mechanism = None + self._client_sasl_mechanism = None config = Config() @@ -95,7 +95,7 @@ def OptionsAndArguments(argv): parser.add_option("--ack", action="store", type="int", metavar="<n>", help="Acknowledge transfers over the bridge in batches of N") parser.add_option("-t", "--transport", action="store", type="string", default="tcp", metavar="<transport>", help="Transport to use for links, defaults to tcp") - parser.add_option("--sasl-mechanism", action="store", type="string", metavar="<mech>", help="SASL mechanism for authentication (e.g. EXTERNAL, ANONYMOUS, PLAIN, CRAM-MD, DIGEST-MD5, GSSAPI). Used when the client connects to the destination broker (not for authentication between the source and destination brokers - that is specified using the [mechanisms] argument to 'add route'). SASL automatically picks the most secure available mechanism - use this option to override.") + parser.add_option("--client-sasl-mechanism", action="store", type="string", metavar="<mech>", help="SASL mechanism for authentication (e.g. EXTERNAL, ANONYMOUS, PLAIN, CRAM-MD, DIGEST-MD5, GSSAPI). Used when the client connects to the destination broker (not for authentication between the source and destination brokers - that is specified using the [mechanisms] argument to 'add route'). SASL automatically picks the most secure available mechanism - use this option to override.") opts, encArgs = parser.parse_args(args=argv) @@ -131,8 +131,8 @@ def OptionsAndArguments(argv): if opts.ack: config._ack = opts.ack - if opts.sasl_mechanism: - config._sasl_mechanism = opts.sasl_mechanism + if opts.client_sasl_mechanism: + config._client_sasl_mechanism = opts.client_sasl_mechanism return args @@ -143,7 +143,7 @@ class RouteManager: self.local = BrokerURL(localBroker) self.remote = None self.qmf = Session() - self.broker = self.qmf.addBroker(localBroker, config._connTimeout, config._sasl_mechanism) + self.broker = self.qmf.addBroker(localBroker, config._connTimeout, config._client_sasl_mechanism) self.broker._waitForStable() self.agent = self.broker.getBrokerAgent() @@ -166,7 +166,7 @@ class RouteManager: return link return None - def addLink(self, remoteBroker, mech="PLAIN"): + def addLink(self, remoteBroker, interbroker_mechanism=""): self.remote = BrokerURL(remoteBroker) if self.local.match(self.remote.host, self.remote.port): raise Exception("Linking broker to itself is not permitted") @@ -176,7 +176,7 @@ class RouteManager: link = self.getLink() if link == None: res = broker.connect(self.remote.host, self.remote.port, config._durable, - mech, self.remote.authName or "", self.remote.authPass or "", + interbroker_mechanism, self.remote.authName or "", self.remote.authPass or "", config._transport) if config._verbose: print "Connect method returned:", res.status, res.text @@ -295,11 +295,11 @@ class RouteManager: if b[0] != self.local.name(): self.qmf.delBroker(b[1]) - def addRoute(self, remoteBroker, exchange, routingKey, tag, excludes, mech="PLAIN", dynamic=False): + def addRoute(self, remoteBroker, exchange, routingKey, tag, excludes, interbroker_mechanism="", dynamic=False): if dynamic and config._srclocal: raise Exception("--src-local is not permitted on dynamic routes") - self.addLink(remoteBroker, mech) + self.addLink(remoteBroker, interbroker_mechanism) link = self.getLink() if link == None: raise Exception("Link failed to create") @@ -320,8 +320,8 @@ class RouteManager: if config._verbose: print "Bridge method returned:", res.status, res.text - def addQueueRoute(self, remoteBroker, exchange, queue): - self.addLink(remoteBroker) + def addQueueRoute(self, remoteBroker, interbroker_mechanism, exchange, queue ): + self.addLink(remoteBroker, interbroker_mechanism) link = self.getLink() if link == None: raise Exception("Link failed to create") @@ -504,10 +504,12 @@ def main(argv=None): rm = RouteManager(localBroker) if group == "link": if cmd == "add": - if nargs != 4: + if nargs < 3 or nargs > 5: Usage() return(-1) - rm.addLink(remoteBroker) + interbroker_mechanism = "" + if nargs > 4: interbroker_mechanism = args[4] + rm.addLink(remoteBroker, interbroker_mechanism) elif cmd == "del": if nargs != 4: Usage() @@ -518,16 +520,17 @@ def main(argv=None): elif group == "dynamic": if cmd == "add": - if nargs < 5 or nargs > 7: + if nargs < 5 or nargs > 8: Usage() return(-1) tag = "" excludes = "" - mech = "PLAIN" + interbroker_mechanism = "" if nargs > 5: tag = args[5] if nargs > 6: excludes = args[6] - rm.addRoute(remoteBroker, args[4], "", tag, excludes, mech, dynamic=True) + if nargs > 7: interbroker_mechanism = args[7] + rm.addRoute(remoteBroker, args[4], "", tag, excludes, interbroker_mechanism, dynamic=True) elif cmd == "del": if nargs != 5: Usage() @@ -543,11 +546,11 @@ def main(argv=None): tag = "" excludes = "" - mech = "PLAIN" + interbroker_mechanism = "" if nargs > 6: tag = args[6] if nargs > 7: excludes = args[7] - if nargs > 8: mech = args[8] - rm.addRoute(remoteBroker, args[4], args[5], tag, excludes, mech, dynamic=False) + if nargs > 8: interbroker_mechanism = args[8] + rm.addRoute(remoteBroker, args[4], args[5], tag, excludes, interbroker_mechanism, dynamic=False) elif cmd == "del": if nargs != 6: Usage() @@ -565,11 +568,13 @@ def main(argv=None): return(-1) elif group == "queue": - if nargs != 6: + if nargs < 6 or nargs > 7: Usage() return(-1) if cmd == "add": - rm.addQueueRoute(remoteBroker, exchange=args[4], queue=args[5]) + interbroker_mechanism = "" + if nargs > 6: interbroker_mechanism = args[6] + rm.addQueueRoute(remoteBroker, interbroker_mechanism, exchange=args[4], queue=args[5] ) elif cmd == "del": rm.delQueueRoute(remoteBroker, exchange=args[4], queue=args[5]) else: diff --git a/qpid/tools/src/py/qpid-tool b/qpid/tools/src/py/qpid-tool index d3b0aa4097..2a06dae6ac 100755 --- a/qpid/tools/src/py/qpid-tool +++ b/qpid/tools/src/py/qpid-tool @@ -259,7 +259,24 @@ class QmfData(Console): return displayId = long(tokens[0]) methodName = tokens[1] - args = tokens[2:] + args = [] + for arg in tokens[2:]: + ## + ## If the argument is a map, list, boolean, integer, or floating (one decimal point), + ## run it through the Python evaluator so it is converted to the correct type. + ## + ## TODO: use a regex for this instead of this convoluted logic, + ## or even consider passing all args through eval() [which would + ## be a minor change to the nterface as string args would then + ## always need to be quoted as strings within a map/list would + ## now] + if arg[0] == '{' or arg[0] == '[' or arg == "True" or arg == "False" or \ + ((arg.count('.') < 2 and (arg.count('-') == 0 or \ + (arg.count('-') == 1 and arg[0] == '-')) and \ + arg.replace('.','').replace('-','').isdigit())): + args.append(eval(arg)) + else: + args.append(arg) obj = None try: |